重新架构了整个models
This commit is contained in:
@@ -5,7 +5,7 @@
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>服务状态监控</title>
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
<link href="https://cdn.jsdelivr.net/npm/font-awesome@4.7.0/css/font-awesome.min.css" rel="stylesheet">
|
||||
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css" rel="stylesheet">
|
||||
<script>
|
||||
tailwind.config = {
|
||||
theme: {
|
||||
@@ -52,13 +52,18 @@
|
||||
<div class="container mx-auto px-4 py-6">
|
||||
<div class="flex flex-col md:flex-row justify-between items-center">
|
||||
<div class="flex items-center mb-4 md:mb-0">
|
||||
<i class="fa fa-server text-3xl mr-3"></i>
|
||||
<i class="fas fa-server text-3xl mr-3"></i>
|
||||
<h1 class="text-[clamp(1.5rem,3vw,2.5rem)] font-bold">服务状态监控</h1>
|
||||
</div>
|
||||
<div class="flex items-center space-x-2">
|
||||
<i class="fa fa-refresh animate-spin mr-2"></i>
|
||||
<span id="last-updated" class="text-sm md:text-base">最后更新: 加载中...</span>
|
||||
</div>
|
||||
<i class="fas fa-refresh animate-spin mr-2"></i>
|
||||
<span id="last-updated" class="text-sm md:text-base">最后更新: 加载中...</span>
|
||||
</div>
|
||||
<div class="mt-4 md:mt-0">
|
||||
<a href="/services/" class="bg-white text-primary px-4 py-2 rounded-lg font-medium hover:bg-gray-100 transition-colors">
|
||||
<i class="fas fa-list mr-2"></i> 查看所有服务
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<p class="mt-2 text-gray-200">实时监控系统服务运行状态与可靠性</p>
|
||||
</div>
|
||||
@@ -71,43 +76,72 @@
|
||||
<div class="bg-white rounded-xl p-6 card-shadow card-hover border-l-4 border-operational">
|
||||
<div class="flex items-center justify-between">
|
||||
<div>
|
||||
<p class="text-gray-500 text-sm">正常运行</p>
|
||||
<p class="text-gray-500 text-sm">正常</p>
|
||||
<h3 id="operational-count" class="text-3xl font-bold mt-1">0</h3>
|
||||
</div>
|
||||
<div class="w-12 h-12 rounded-full bg-operational/10 flex items-center justify-center text-operational">
|
||||
<i class="fa fa-check-circle text-xl"></i>
|
||||
<i class="fas fa-check-circle text-xl"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bg-white rounded-xl p-6 card-shadow card-hover border-l-4 border-degraded">
|
||||
<div class="flex items-center justify-between">
|
||||
<div>
|
||||
<p class="text-gray-500 text-sm">性能下降</p>
|
||||
<p class="text-gray-500 text-sm">未知</p>
|
||||
<h3 id="degraded-count" class="text-3xl font-bold mt-1">0</h3>
|
||||
</div>
|
||||
<div class="w-12 h-12 rounded-full bg-degraded/10 flex items-center justify-center text-degraded">
|
||||
<i class="fa fa-exclamation-triangle text-xl"></i>
|
||||
<i class="fas fa-question-circle text-xl"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bg-white rounded-xl p-6 card-shadow card-hover border-l-4 border-outage">
|
||||
<div class="flex items-center justify-between">
|
||||
<div>
|
||||
<p class="text-gray-500 text-sm">服务中断</p>
|
||||
<p class="text-gray-500 text-sm">故障</p>
|
||||
<h3 id="outage-count" class="text-3xl font-bold mt-1">0</h3>
|
||||
</div>
|
||||
<div class="w-12 h-12 rounded-full bg-outage/10 flex items-center justify-center text-outage">
|
||||
<i class="fa fa-times-circle text-xl"></i>
|
||||
<i class="fas fa-times-circle text-xl"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 最近异常 -->
|
||||
<div class="bg-white rounded-xl p-6 card-shadow mb-10">
|
||||
<div class="flex justify-between items-center mb-4">
|
||||
<h2 class="text-xl font-bold text-gray-800">最近异常</h2>
|
||||
<a href="/services/?status=down" class="text-primary hover:underline text-sm font-medium">
|
||||
查看全部 <i class="fas fa-arrow-right ml-1"></i>
|
||||
</a>
|
||||
</div>
|
||||
<div class="overflow-x-auto">
|
||||
<table class="min-w-full divide-y divide-gray-200">
|
||||
<thead class="bg-gray-50">
|
||||
<tr>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">服务名称</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">状态</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">消息</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">检测时间</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="bg-white divide-y divide-gray-200" id="recent-issues-tbody">
|
||||
<tr>
|
||||
<td colspan="4" class="px-6 py-4 text-center text-gray-500">
|
||||
<i class="fas fa-circle-o-notch fa-spin mr-2"></i> 加载中...
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 服务列表 -->
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6" id="services-container">
|
||||
<!-- 服务卡片将通过JavaScript动态生成 -->
|
||||
<div class="col-span-full text-center py-12 text-gray-500">
|
||||
<i class="fa fa-circle-o-notch fa-spin text-3xl mb-4"></i>
|
||||
<i class="fas fa-circle-o-notch fa-spin text-3xl mb-4"></i>
|
||||
<p>加载服务数据中...</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -123,20 +157,20 @@
|
||||
<script>
|
||||
// 状态样式映射
|
||||
const statusStyles = {
|
||||
operational: {
|
||||
up: {
|
||||
class: 'bg-operational text-white',
|
||||
icon: 'fa-check-circle',
|
||||
text: '正常运行'
|
||||
icon: 'fas fa-check-circle',
|
||||
text: '正常'
|
||||
},
|
||||
degraded: {
|
||||
class: 'bg-degraded text-white',
|
||||
icon: 'fa-exclamation-triangle',
|
||||
text: '性能下降'
|
||||
},
|
||||
outage: {
|
||||
down: {
|
||||
class: 'bg-outage text-white',
|
||||
icon: 'fa-times-circle',
|
||||
text: '服务中断'
|
||||
icon: 'fas fa-times-circle',
|
||||
text: '故障'
|
||||
},
|
||||
unknown: {
|
||||
class: 'bg-degraded text-white',
|
||||
icon: 'fas fa-question-circle',
|
||||
text: '未知'
|
||||
}
|
||||
};
|
||||
|
||||
@@ -151,19 +185,23 @@
|
||||
|
||||
// 统计各状态数量
|
||||
const counts = {
|
||||
operational: 0,
|
||||
degraded: 0,
|
||||
outage: 0
|
||||
up: 0,
|
||||
down: 0,
|
||||
unknown: 0
|
||||
};
|
||||
|
||||
data.forEach(service => {
|
||||
counts[service.status]++;
|
||||
if (service.latest_status) {
|
||||
counts[service.latest_status.toLowerCase()]++;
|
||||
} else {
|
||||
counts.unknown++;
|
||||
}
|
||||
});
|
||||
|
||||
// 更新统计数字
|
||||
document.getElementById('operational-count').textContent = counts.operational;
|
||||
document.getElementById('degraded-count').textContent = counts.degraded;
|
||||
document.getElementById('outage-count').textContent = counts.outage;
|
||||
document.getElementById('operational-count').textContent = counts.up;
|
||||
document.getElementById('degraded-count').textContent = counts.unknown;
|
||||
document.getElementById('outage-count').textContent = counts.down;
|
||||
|
||||
// 生成服务卡片
|
||||
const container = document.getElementById('services-container');
|
||||
@@ -172,7 +210,7 @@
|
||||
if (data.length === 0) {
|
||||
container.innerHTML = `
|
||||
<div class="col-span-full text-center py-12 text-gray-500">
|
||||
<i class="fa fa-info-circle text-3xl mb-4"></i>
|
||||
<i class="fas fa-info-circle text-3xl mb-4"></i>
|
||||
<p>暂无服务数据</p>
|
||||
</div>
|
||||
`;
|
||||
@@ -180,7 +218,8 @@
|
||||
}
|
||||
|
||||
data.forEach(service => {
|
||||
const style = statusStyles[service.status];
|
||||
const status = service.latest_status ? service.latest_status.toLowerCase() : 'unknown';
|
||||
const style = statusStyles[status];
|
||||
const card = document.createElement('div');
|
||||
card.className = 'bg-white rounded-xl overflow-hidden card-shadow card-hover';
|
||||
card.innerHTML = `
|
||||
@@ -188,29 +227,41 @@
|
||||
<div class="flex justify-between items-start mb-4">
|
||||
<h3 class="text-xl font-bold text-gray-800">${service.name}</h3>
|
||||
<span class="px-3 py-1 rounded-full text-sm font-medium ${style.class}">
|
||||
<i class="fa ${style.icon} mr-1"></i> ${style.text}
|
||||
<i class="fas ${style.icon} mr-1"></i> ${style.text}
|
||||
</span>
|
||||
</div>
|
||||
<p class="text-gray-600 mb-4">${service.description}</p>
|
||||
<div class="text-sm text-gray-600 mb-2">
|
||||
<span class="inline-block bg-gray-100 rounded-full px-3 py-1 text-xs font-medium text-gray-800 mr-2">
|
||||
${service.group}
|
||||
</span>
|
||||
<span class="inline-block bg-gray-100 rounded-full px-3 py-1 text-xs font-medium text-gray-800">
|
||||
${service.check_type}
|
||||
</span>
|
||||
</div>
|
||||
<p class="text-gray-600 mb-4">${service.description || '暂无描述'}</p>
|
||||
<div class="grid grid-cols-2 gap-3 text-sm">
|
||||
<div class="bg-gray-50 p-3 rounded-lg">
|
||||
<p class="text-gray-500">IP地址</p>
|
||||
<p class="font-medium">${service.ip_address}</p>
|
||||
<p class="text-gray-500">主机/IP</p>
|
||||
<p class="font-medium">${service.host}</p>
|
||||
</div>
|
||||
<div class="bg-gray-50 p-3 rounded-lg">
|
||||
<p class="text-gray-500">端口</p>
|
||||
<p class="font-medium">${service.port}</p>
|
||||
<p class="font-medium">${service.port || 'N/A'}</p>
|
||||
</div>
|
||||
${service.latest_response_time ? `
|
||||
<div class="bg-gray-50 p-3 rounded-lg col-span-2">
|
||||
<p class="text-gray-500">可靠率</p>
|
||||
<div class="w-full bg-gray-200 rounded-full h-2 mt-1">
|
||||
<div class="${style.class} h-2 rounded-full" style="width: ${service.reliability}%"></div>
|
||||
</div>
|
||||
<p class="font-medium mt-1">${service.reliability}%</p>
|
||||
<p class="text-gray-500">响应时间</p>
|
||||
<p class="font-medium">${service.latest_response_time} ms</p>
|
||||
</div>
|
||||
` : ''}
|
||||
</div>
|
||||
<div class="mt-4 text-xs text-gray-500">
|
||||
<p>最后更新: ${new Date(service.last_updated).toLocaleString()}</p>
|
||||
<p>最后检测: ${service.latest_check_time ? new Date(service.latest_check_time).toLocaleString() : '从未检测'}</p>
|
||||
</div>
|
||||
<div class="mt-4">
|
||||
<a href="/services/${service.id}/" class="text-primary hover:underline text-sm font-medium">
|
||||
查看详情 <i class="fas fa-arrow-right ml-1"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
@@ -222,7 +273,7 @@
|
||||
const container = document.getElementById('services-container');
|
||||
container.innerHTML = `
|
||||
<div class="col-span-full text-center py-12 text-red-500">
|
||||
<i class="fa fa-exclamation-circle text-3xl mb-4"></i>
|
||||
<i class="fas fa-exclamation-circle text-3xl mb-4"></i>
|
||||
<p>加载服务数据失败,请刷新页面重试</p>
|
||||
</div>
|
||||
`;
|
||||
|
||||
340
status/templates/status/service_detail.html
Normal file
340
status/templates/status/service_detail.html
Normal file
@@ -0,0 +1,340 @@
|
||||
<!doctype html>
|
||||
<html lang="zh-cn">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>服务详情 - 服务状态监控</title>
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css" rel="stylesheet">
|
||||
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
||||
<script>
|
||||
tailwind.config = {
|
||||
theme: {
|
||||
extend: {
|
||||
colors: {
|
||||
primary: '#165dff',
|
||||
operational: '#36d399',
|
||||
degraded: '#fbbd23',
|
||||
outage: '#f87272',
|
||||
dark: '#1e293b',
|
||||
light: '#f8fafc'
|
||||
},
|
||||
fontfamily: {
|
||||
inter: ['inter', 'system-ui', 'sans-serif'],
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style type="text/tailwindcss">
|
||||
@layer utilities {
|
||||
.content-auto {
|
||||
content-visibility: auto;
|
||||
}
|
||||
.card-shadow {
|
||||
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
.card-hover {
|
||||
transition: transform 0.3s ease, box-shadow 0.3s ease;
|
||||
}
|
||||
.card-hover:hover {
|
||||
transform: translatey(-5px);
|
||||
box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
|
||||
}
|
||||
.gradient-bg {
|
||||
background: linear-gradient(135deg, #165dff 0%, #0a2463 100%);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body class="bg-gray-50 font-inter min-h-screen">
|
||||
<!-- 顶部导航 -->
|
||||
<header class="gradient-bg text-white shadow-lg">
|
||||
<div class="container mx-auto px-4 py-6">
|
||||
<div class="flex flex-col md:flex-row justify-between items-center">
|
||||
<div class="flex items-center mb-4 md:mb-0">
|
||||
<a href="/" class="flex items-center">
|
||||
<i class="fas fa-server text-3xl mr-3"></i>
|
||||
<h1 class="text-[clamp(1.5rem,3vw,2.5rem)] font-bold">服务状态监控</h1>
|
||||
</a>
|
||||
</div>
|
||||
<div class="flex items-center space-x-4">
|
||||
<a href="/" class="text-white hover:text-gray-200">
|
||||
<i class="fas fa-home mr-2"></i>首页
|
||||
</a>
|
||||
<a href="/services/" class="text-white hover:text-gray-200">
|
||||
<i class="fas fa-list mr-2"></i>服务列表
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<!-- 主内容区 -->
|
||||
<main class="container mx-auto px-4 py-8">
|
||||
<!-- 服务基本信息 -->
|
||||
<div class="bg-white rounded-xl p-6 card-shadow mb-8">
|
||||
<div class="flex flex-col md:flex-row justify-between items-start md:items-center mb-6">
|
||||
<div>
|
||||
<h2 class="text-2xl font-bold text-gray-800">{{ service.name }}</h2>
|
||||
<div class="flex items-center mt-2">
|
||||
{% if service.latest_status == 'UP' %}
|
||||
<span class="px-3 inline-flex text-sm leading-5 font-semibold rounded-full bg-green-100 text-green-800">
|
||||
<i class="fas fa-check-circle mr-1"></i> 正常
|
||||
</span>
|
||||
{% elif service.latest_status == 'DOWN' %}
|
||||
<span class="px-3 inline-flex text-sm leading-5 font-semibold rounded-full bg-red-100 text-red-800">
|
||||
<i class="fas fa-times-circle mr-1"></i> 故障
|
||||
</span>
|
||||
{% else %}
|
||||
<span class="px-3 inline-flex text-sm leading-5 font-semibold rounded-full bg-yellow-100 text-yellow-800">
|
||||
<i class="fas fa-question-circle mr-1"></i> 未知
|
||||
</span>
|
||||
{% endif %}
|
||||
<span class="ml-3 text-sm text-gray-500">
|
||||
<i class="fas fa-clock-o mr-1"></i>
|
||||
{% if service.latest_check_time %}最后检测: {{ service.latest_check_time|date:"y-m-d h:i:s" }}{% else %}从未检测{% endif %}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<a href="/services/" class="mt-4 md:mt-0 bg-gray-100 text-gray-700 py-2 px-4 rounded-lg hover:bg-gray-200 transition-colors">
|
||||
<i class="fas fa-arrow-left mr-2"></i>返回服务列表
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
<div>
|
||||
<h3 class="text-lg font-semibold text-gray-700 mb-3">基本信息</h3>
|
||||
<div class="space-y-2">
|
||||
<div class="flex">
|
||||
<span class="w-32 text-gray-500">服务名称:</span>
|
||||
<span class="text-gray-800">{{ service.name }}</span>
|
||||
</div>
|
||||
<div class="flex">
|
||||
<span class="w-32 text-gray-500">所属分组:</span>
|
||||
<span class="text-gray-800">{{ service.group.name }}</span>
|
||||
</div>
|
||||
<div class="flex">
|
||||
<span class="w-32 text-gray-500">主机/IP:</span>
|
||||
<span class="text-gray-800">{{ service.host }}</span>
|
||||
</div>
|
||||
<div class="flex">
|
||||
<span class="w-32 text-gray-500">端口:</span>
|
||||
<span class="text-gray-800">{% if service.port %}{{ service.port }}{% else %}N/A{% endif %}</span>
|
||||
</div>
|
||||
<div class="flex">
|
||||
<span class="w-32 text-gray-500">检测类型:</span>
|
||||
<span class="text-gray-800">{{ service.get_check_type_display }}</span>
|
||||
</div>
|
||||
<div class="flex">
|
||||
<span class="w-32 text-gray-500">是否启用:</span>
|
||||
<span class="text-gray-800">{% if service.is_active %}是{% else %}否{% endif %}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h3 class="text-lg font-semibold text-gray-700 mb-3">最新状态</h3>
|
||||
<div class="space-y-2">
|
||||
<div class="flex">
|
||||
<span class="w-32 text-gray-500">当前状态:</span>
|
||||
<span class="text-gray-800">
|
||||
{% if service.latest_status == 'UP' %}正常
|
||||
{% elif service.latest_status == 'DOWN' %}故障
|
||||
{% else %}未知{% endif %}
|
||||
</span>
|
||||
</div>
|
||||
<div class="flex">
|
||||
<span class="w-32 text-gray-500">响应时间:</span>
|
||||
<span class="text-gray-800">{% if service.latest_response_time %}{{ service.latest_response_time }} ms{% else %}N/A{% endif %}</span>
|
||||
</div>
|
||||
<div class="flex">
|
||||
<span class="w-32 text-gray-500">最后检测:</span>
|
||||
<span class="text-gray-800">{% if service.latest_check_time %}{{ service.latest_check_time|date:"y-m-d h:i:s" }}{% else %}从未检测{% endif %}</span>
|
||||
</div>
|
||||
<div class="flex">
|
||||
<span class="w-32 text-gray-500">检测消息:</span>
|
||||
<span class="text-gray-800">{% if service.latest_message %}{{ service.latest_message }}{% else %}无{% endif %}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if service.description %}
|
||||
<div class="mt-6">
|
||||
<h3 class="text-lg font-semibold text-gray-700 mb-3">描述</h3>
|
||||
<p class="text-gray-600">{{ service.description }}</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<!-- 响应时间趋势图 -->
|
||||
<div class="bg-white rounded-xl p-6 card-shadow mb-8">
|
||||
<h3 class="text-xl font-bold text-gray-800 mb-4">响应时间趋势 (最近24小时)</h3>
|
||||
<div class="h-64">
|
||||
<canvas id="response-time-chart"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 状态变化时间轴 -->
|
||||
<div class="bg-white rounded-xl p-6 card-shadow mb-8">
|
||||
<h3 class="text-xl font-bold text-gray-800 mb-4">状态变化时间轴</h3>
|
||||
<div class="relative">
|
||||
<div class="absolute left-4 top-0 bottom-0 w-0.5 bg-gray-200"></div>
|
||||
<div class="space-y-4" id="status-timeline">
|
||||
{% for record in status_changes %}
|
||||
<div class="relative pl-10">
|
||||
<div class="absolute left-0 w-8 h-8 rounded-full flex items-center justify-center
|
||||
{% if record.status == 'UP' %}bg-green-100 text-green-600{% elif record.status == 'DOWN' %}bg-red-100 text-red-600{% else %}bg-yellow-100 text-yellow-600{% endif %}
|
||||
">
|
||||
{% if record.status == 'UP' %}<i class="fas fa-check"></i>
|
||||
{% elif record.status == 'DOWN' %}<i class="fas fa-times"></i>
|
||||
{% else %}<i class="fas fa-question"></i>{% endif %}
|
||||
</div>
|
||||
<div class="bg-gray-50 p-4 rounded-lg">
|
||||
<div class="flex justify-between">
|
||||
<span class="font-medium">
|
||||
{% if record.status == 'UP' %}正常
|
||||
{% elif record.status == 'DOWN' %}故障
|
||||
{% else %}未知{% endif %}
|
||||
</span>
|
||||
<span class="text-sm text-gray-500">{{ record.checked_at|date:"y-m-d h:i:s" }}</span>
|
||||
</div>
|
||||
{% if record.message %}
|
||||
<p class="text-sm text-gray-600 mt-1">{{ record.message }}</p>
|
||||
{% endif %}
|
||||
{% if record.response_time %}
|
||||
<p class="text-sm text-gray-500 mt-1">响应时间: {{ record.response_time }} ms</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% empty %}
|
||||
<div class="text-center py-4 text-gray-500">
|
||||
<i class="fas fa-info-circle text-2xl mb-2"></i>
|
||||
<p>暂无状态变化记录</p>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 最近检测记录 -->
|
||||
<div class="bg-white rounded-xl overflow-hidden card-shadow">
|
||||
<div class="px-6 py-4 border-b border-gray-200">
|
||||
<h3 class="text-xl font-bold text-gray-800">最近检测记录</h3>
|
||||
</div>
|
||||
<div class="overflow-x-auto">
|
||||
<table class="min-w-full divide-y divide-gray-200">
|
||||
<thead class="bg-gray-50">
|
||||
<tr>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">检测时间</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">状态</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">响应时间</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">消息</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="bg-white divide-y divide-gray-200">
|
||||
{% for record in recent_records %}
|
||||
<tr class="hover:bg-gray-50">
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">{{ record.checked_at|date:"y-m-d h:i:s" }}</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap">
|
||||
{% if record.status == 'UP' %}
|
||||
<span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-green-100 text-green-800">
|
||||
<i class="fas fa-check-circle mr-1"></i> 正常
|
||||
</span>
|
||||
{% elif record.status == 'DOWN' %}
|
||||
<span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-red-100 text-red-800">
|
||||
<i class="fas fa-times-circle mr-1"></i> 故障
|
||||
</span>
|
||||
{% else %}
|
||||
<span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-yellow-100 text-yellow-800">
|
||||
<i class="fas fa-question-circle mr-1"></i> 未知
|
||||
</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
|
||||
{% if record.response_time %}{{ record.response_time }} ms{% else %}N/A{% endif %}
|
||||
</td>
|
||||
<td class="px-6 py-4 text-sm text-gray-500 max-w-md truncate">
|
||||
{% if record.message %}{{ record.message }}{% else %}无{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% empty %}
|
||||
<tr>
|
||||
<td colspan="4" class="px-6 py-4 text-center text-gray-500">
|
||||
<i class="fas fa-info-circle text-2xl mb-2"></i>
|
||||
<p>暂无检测记录</p>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<!-- 页脚 -->
|
||||
<footer class="bg-dark text-white py-6 mt-12">
|
||||
<div class="container mx-auto px-4 text-center">
|
||||
<p>© 2025 服务状态监控系统</p>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
<script>
|
||||
// 响应时间趋势图
|
||||
const ctx = document.getelementbyid('response-time-chart').getcontext('2d');
|
||||
const responsetimechart = new chart(ctx, {
|
||||
type: 'line',
|
||||
data: {
|
||||
labels: {{ chart_labels|safe }},
|
||||
datasets: [{
|
||||
label: '响应时间 (ms)',
|
||||
data: {{ chart_data|safe }},
|
||||
backgroundcolor: 'rgba(22, 93, 255, 0.1)',
|
||||
bordercolor: '#165dff',
|
||||
borderwidth: 2,
|
||||
tension: 0.3,
|
||||
pointbackgroundcolor: '#165dff',
|
||||
pointbordercolor: '#fff',
|
||||
pointborderwidth: 2,
|
||||
pointradius: 4,
|
||||
pointhoverradius: 6
|
||||
}]
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
maintainaspectratio: false,
|
||||
plugins: {
|
||||
legend: {
|
||||
display: false
|
||||
},
|
||||
tooltip: {
|
||||
mode: 'index',
|
||||
intersect: false,
|
||||
callbacks: {
|
||||
label: function(context) {
|
||||
return `响应时间: ${context.parsed.y} ms`;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
scales: {
|
||||
y: {
|
||||
beginatzero: true,
|
||||
title: {
|
||||
display: true,
|
||||
text: '响应时间 (ms)'
|
||||
}
|
||||
},
|
||||
x: {
|
||||
title: {
|
||||
display: true,
|
||||
text: '时间'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
216
status/templates/status/service_list.html
Normal file
216
status/templates/status/service_list.html
Normal file
@@ -0,0 +1,216 @@
|
||||
<!doctype html>
|
||||
<html lang="zh-cn">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>服务列表 - 服务状态监控</title>
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css" rel="stylesheet">
|
||||
<script>
|
||||
tailwind.config = {
|
||||
theme: {
|
||||
extend: {
|
||||
colors: {
|
||||
primary: '#165dff',
|
||||
operational: '#36d399',
|
||||
degraded: '#fbbd23',
|
||||
outage: '#f87272',
|
||||
dark: '#1e293b',
|
||||
light: '#f8fafc'
|
||||
},
|
||||
fontfamily: {
|
||||
inter: ['inter', 'system-ui', 'sans-serif'],
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style type="text/tailwindcss">
|
||||
@layer utilities {
|
||||
.content-auto {
|
||||
content-visibility: auto;
|
||||
}
|
||||
.card-shadow {
|
||||
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
.card-hover {
|
||||
transition: transform 0.3s ease, box-shadow 0.3s ease;
|
||||
}
|
||||
.card-hover:hover {
|
||||
transform: translatey(-5px);
|
||||
box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
|
||||
}
|
||||
.gradient-bg {
|
||||
background: linear-gradient(135deg, #165dff 0%, #0a2463 100%);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body class="bg-gray-50 font-inter min-h-screen">
|
||||
<!-- 顶部导航 -->
|
||||
<header class="gradient-bg text-white shadow-lg">
|
||||
<div class="container mx-auto px-4 py-6">
|
||||
<div class="flex flex-col md:flex-row justify-between items-center">
|
||||
<div class="flex items-center mb-4 md:mb-0">
|
||||
<a href="/" class="flex items-center">
|
||||
<i class="fas fa-server text-3xl mr-3"></i>
|
||||
<h1 class="text-[clamp(1.5rem,3vw,2.5rem)] font-bold">服务状态监控</h1>
|
||||
</a>
|
||||
</div>
|
||||
<div class="flex items-center space-x-4">
|
||||
<a href="/" class="text-white hover:text-gray-200">
|
||||
<i class="fas fa-home mr-2"></i>首页
|
||||
</a>
|
||||
<a href="/services/" class="text-white font-medium border-b-2 border-white pb-1">
|
||||
<i class="fas fa-list mr-2"></i>服务列表
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<!-- 主内容区 -->
|
||||
<main class="container mx-auto px-4 py-8">
|
||||
<!-- 筛选和搜索 -->
|
||||
<div class="bg-white rounded-xl p-6 card-shadow mb-8">
|
||||
<div class="flex flex-col md:flex-row justify-between items-start md:items-center gap-4">
|
||||
<h2 class="text-xl font-bold text-gray-800">服务列表</h2>
|
||||
<div class="flex flex-col md:flex-row gap-3 w-full md:w-auto">
|
||||
<div class="relative">
|
||||
<select id="group-filter" class="appearance-none bg-gray-50 border border-gray-300 text-gray-700 py-2 px-4 pr-8 rounded-lg leading-tight focus:outline-none focus:bg-white focus:border-primary w-full md:w-auto">
|
||||
<option value="">所有分组</option>
|
||||
{% for group in groups %}
|
||||
<option value="{{ group.name }}" {% if current_group == group.name %}selected{% endif %}>{{ group.name }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
<div class="pointer-events-none absolute inset-y-0 right-0 flex items-center px-2 text-gray-700">
|
||||
<i class="fas fa-chevron-down"></i>
|
||||
</div>
|
||||
</div>
|
||||
<div class="relative">
|
||||
<select id="status-filter" class="appearance-none bg-gray-50 border border-gray-300 text-gray-700 py-2 px-4 pr-8 rounded-lg leading-tight focus:outline-none focus:bg-white focus:border-primary w-full md:w-auto">
|
||||
<option value="">所有状态</option>
|
||||
<option value="up" {% if current_status == 'up' %}selected{% endif %}>正常</option>
|
||||
<option value="down" {% if current_status == 'down' %}selected{% endif %}>故障</option>
|
||||
<option value="unknown" {% if current_status == 'unknown' %}selected{% endif %}>未知</option>
|
||||
</select>
|
||||
<div class="pointer-events-none absolute inset-y-0 right-0 flex items-center px-2 text-gray-700">
|
||||
<i class="fas fa-chevron-down"></i>
|
||||
</div>
|
||||
</div>
|
||||
<div class="relative">
|
||||
<input type="text" id="search-input" placeholder="搜索服务..." value="{{ search_query }}" class="bg-gray-50 border border-gray-300 text-gray-700 py-2 px-4 rounded-lg leading-tight focus:outline-none focus:bg-white focus:border-primary w-full md:w-auto">
|
||||
<div class="absolute inset-y-0 right-0 flex items-center pr-3 pointer-events-none">
|
||||
<i class="fas fa-search text-gray-500"></i>
|
||||
</div>
|
||||
</div>
|
||||
<button id="search-button" class="bg-primary text-white py-2 px-4 rounded-lg hover:bg-blue-700 transition-colors">
|
||||
<i class="fas fa-filter mr-2"></i>筛选
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 服务表格 -->
|
||||
<div class="bg-white rounded-xl overflow-hidden card-shadow">
|
||||
<div class="overflow-x-auto">
|
||||
<table class="min-w-full divide-y divide-gray-200">
|
||||
<thead class="bg-gray-50">
|
||||
<tr>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">服务名称</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">分组</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">主机/IP</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">状态</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">最后检测时间</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">响应时间</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="bg-white divide-y divide-gray-200" id="services-tbody">
|
||||
{% for service in services %}
|
||||
<tr class="hover:bg-gray-50">
|
||||
<td class="px-6 py-4 whitespace-nowrap">
|
||||
<div class="text-sm font-medium text-gray-900">{{ service.name }}</div>
|
||||
<div class="text-sm text-gray-500">{{ service.check_type }}</div>
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap">
|
||||
<div class="text-sm text-gray-900">{{ service.group.name }}</div>
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap">
|
||||
<div class="text-sm text-gray-900">{{ service.host }}</div>
|
||||
<div class="text-sm text-gray-500">{% if service.port %}{{ service.port }}{% else %}N/A{% endif %}</div>
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap">
|
||||
{% if service.latest_status == 'UP' %}
|
||||
<span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-green-100 text-green-800">
|
||||
<i class="fas fa-check-circle mr-1"></i> 正常
|
||||
</span>
|
||||
{% elif service.latest_status == 'DOWN' %}
|
||||
<span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-red-100 text-red-800">
|
||||
<i class="fas fa-times-circle mr-1"></i> 故障
|
||||
</span>
|
||||
{% else %}
|
||||
<span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-yellow-100 text-yellow-800">
|
||||
<i class="fas fa-question-circle mr-1"></i> 未知
|
||||
</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
|
||||
{% if service.latest_check_time %}{{ service.latest_check_time|date:"y-m-d h:i" }}{% else %}从未检测{% endif %}
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
|
||||
{% if service.latest_response_time %}{{ service.latest_response_time }} ms{% else %}N/A{% endif %}
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium">
|
||||
<a href="/services/{{ service.id }}/" class="text-primary hover:text-blue-700">
|
||||
<i class="fas fa-eye mr-1"></i> 查看详情
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
{% empty %}
|
||||
<tr>
|
||||
<td colspan="7" class="px-6 py-4 text-center text-gray-500">
|
||||
<i class="fas fa-info-circle text-2xl mb-2"></i>
|
||||
<p>暂无服务数据</p>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<!-- 页脚 -->
|
||||
<footer class="bg-dark text-white py-6 mt-12">
|
||||
<div class="container mx-auto px-4 text-center">
|
||||
<p>© 2025 服务状态监控系统</p>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
<script>
|
||||
// 筛选和搜索功能
|
||||
document.getelementbyid('search-button').addeventlistener('click', function() {
|
||||
const groupfilter = document.getelementbyid('group-filter').value;
|
||||
const statusfilter = document.getelementbyid('status-filter').value;
|
||||
const searchquery = document.getelementbyid('search-input').value;
|
||||
|
||||
let url = '/services/?';
|
||||
const params = [];
|
||||
|
||||
if (groupfilter) params.push('group=' + encodeuricomponent(groupfilter));
|
||||
if (statusfilter) params.push('status=' + encodeuricomponent(statusfilter));
|
||||
if (searchquery) params.push('search=' + encodeuricomponent(searchquery));
|
||||
|
||||
window.location.href = url + params.join('&');
|
||||
});
|
||||
|
||||
// 回车键触发搜索
|
||||
document.getelementbyid('search-input').addeventlistener('keypress', function(e) {
|
||||
if (e.key === 'enter') {
|
||||
document.getelementbyid('search-button').click();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user