339 lines
14 KiB
HTML
339 lines
14 KiB
HTML
|
|
{% extends 'base.html' %}
|
|||
|
|
{% block title %}域名详情 - {{ domain.name }}{% endblock %}
|
|||
|
|
{% block extra_head %}
|
|||
|
|
<style>
|
|||
|
|
.sidebar-sticky { position: sticky; top: 80px; }
|
|||
|
|
.list-group .list-group-item.active { background-color: #0d6efd; border-color: #0d6efd; }
|
|||
|
|
.card .badge { font-size: 0.9rem; }
|
|||
|
|
.progress { height: 20px; }
|
|||
|
|
.progress-bar { font-size: 0.8rem; }
|
|||
|
|
@media (max-width: 991.98px){ .sidebar-sticky { position: static; } }
|
|||
|
|
.table td, .table th { vertical-align: middle; }
|
|||
|
|
.text-muted.small { line-height: 1.2; }
|
|||
|
|
</style>
|
|||
|
|
{% endblock %}
|
|||
|
|
|
|||
|
|
{% block content %}
|
|||
|
|
<div class="row g-4">
|
|||
|
|
<div class="col-lg-2 col-md-3">
|
|||
|
|
<div class="sidebar-sticky">
|
|||
|
|
<div class="list-group">
|
|||
|
|
<a class="list-group-item list-group-item-action active" href="#">概览</a>
|
|||
|
|
<a class="list-group-item list-group-item-action" href="#">统计</a>
|
|||
|
|
<a class="list-group-item list-group-item-action" href="#">接入信息</a>
|
|||
|
|
<a class="list-group-item list-group-item-action" href="{% url 'domains:settings' domain_id=domain.id %}">设置</a>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
<div class="col-lg-10 col-md-9">
|
|||
|
|
<div class="d-flex justify-content-between align-items-center mb-3">
|
|||
|
|
<h3>域名:{{ domain.name }}</h3>
|
|||
|
|
<div class="btn-group">
|
|||
|
|
<a href="{% url 'domains:list' %}" class="btn btn-outline-secondary">返回列表</a>
|
|||
|
|
<a href="{% url 'domains:upgrade' domain_id=domain.id %}" class="btn btn-primary" onclick="return confirm('升级套餐将影响后续计费,确认继续?');">升级套餐</a>
|
|||
|
|
<a href="{% url 'domains:logs' domain_id=domain.id %}" class="btn btn-outline-primary">访问日志</a>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div class="card mb-4">
|
|||
|
|
<div class="card-header">概览</div>
|
|||
|
|
<div class="card-body">
|
|||
|
|
<div class="row mb-3">
|
|||
|
|
<div class="col-md-6">
|
|||
|
|
<div>当前套餐:{% if domain.current_plan %}{{ domain.current_plan.name }}{% else %}-{% endif %}</div>
|
|||
|
|
<div>状态:{{ domain.get_status_display }}</div>
|
|||
|
|
</div>
|
|||
|
|
<div class="col-md-6">
|
|||
|
|
<div class="mb-1">本月流量:{{ used_gb }} GB / {{ total_quota_gb }} GB</div>
|
|||
|
|
<div class="progress" role="progressbar" aria-label="Monthly usage" aria-valuenow="{{ progress_pct }}" aria-valuemin="0" aria-valuemax="100">
|
|||
|
|
<div class="progress-bar" style="width: {{ progress_pct }}%">{{ progress_pct }}%</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div class="card mb-4">
|
|||
|
|
<div class="card-header">统计(近30天)</div>
|
|||
|
|
<div class="card-body">
|
|||
|
|
<div class="row mb-3">
|
|||
|
|
<div class="col-md-4">
|
|||
|
|
<div class="fw-semibold">近 7 天合计</div>
|
|||
|
|
<div><span class="badge bg-primary">{{ last7_total_gb|default:0 }} GB</span></div>
|
|||
|
|
<div class="text-muted small">均值:{{ last7_avg_gb|default:0 }} GB/日</div>
|
|||
|
|
</div>
|
|||
|
|
<div class="col-md-4">
|
|||
|
|
<div class="fw-semibold">近 30 天合计</div>
|
|||
|
|
<div><span class="badge bg-info text-dark">{{ last30_total_gb|default:0 }} GB</span></div>
|
|||
|
|
<div class="text-muted small">均值:{{ last30_avg_gb|default:0 }} GB/日</div>
|
|||
|
|
</div>
|
|||
|
|
<div class="col-md-4">
|
|||
|
|
<div class="fw-semibold">峰值日(流量)</div>
|
|||
|
|
<div>
|
|||
|
|
{% if peak_volume_day %}
|
|||
|
|
<span class="badge bg-warning text-dark">{{ peak_volume_day.day }} / {{ peak_volume_day.gb }} GB</span>
|
|||
|
|
{% else %}
|
|||
|
|
<span class="text-muted">无数据</span>
|
|||
|
|
{% endif %}
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
<div class="row mb-3">
|
|||
|
|
<div class="col-md-4">
|
|||
|
|
<div class="fw-semibold">峰值日(带宽)</div>
|
|||
|
|
<div>
|
|||
|
|
{% if peak_bandwidth_day %}
|
|||
|
|
<span class="badge bg-secondary">{{ peak_bandwidth_day.day }} / {{ peak_bandwidth_day.mbps }} Mbps</span>
|
|||
|
|
{% else %}
|
|||
|
|
<span class="text-muted">无数据</span>
|
|||
|
|
{% endif %}
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
<div class="col-md-8">
|
|||
|
|
<div class="fw-semibold">请求数 / 状态码分布</div>
|
|||
|
|
<div class="mb-2">
|
|||
|
|
{% if requests_24h_total %}
|
|||
|
|
近 24 小时请求数:<span class="badge bg-primary">{{ requests_24h_total }}</span>
|
|||
|
|
{% else %}
|
|||
|
|
<span class="text-muted">请求数不可用(请在设置启用访问日志)</span>
|
|||
|
|
{% endif %}
|
|||
|
|
</div>
|
|||
|
|
{% if status_bins %}
|
|||
|
|
<div class="d-flex gap-2 flex-wrap">
|
|||
|
|
<span class="badge text-bg-success">2xx {{ status_bins.2xx }}</span>
|
|||
|
|
<span class="badge text-bg-info">3xx {{ status_bins.3xx }}</span>
|
|||
|
|
<span class="badge text-bg-warning text-dark">4xx {{ status_bins.4xx }}</span>
|
|||
|
|
<span class="badge text-bg-danger">5xx {{ status_bins.5xx }}</span>
|
|||
|
|
</div>
|
|||
|
|
{% if status_top %}
|
|||
|
|
<div class="text-muted small mt-1">Top 状态码:
|
|||
|
|
{% for code,count in status_top %}
|
|||
|
|
<span class="me-2"><code>{{ code }}</code> {{ count }}</span>
|
|||
|
|
{% endfor %}
|
|||
|
|
</div>
|
|||
|
|
{% endif %}
|
|||
|
|
{% else %}
|
|||
|
|
<div class="text-muted small">状态码分布不可用。</div>
|
|||
|
|
{% endif %}
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
{% if traffic_rows %}
|
|||
|
|
<canvas id="trafficChart" height="120" class="mb-3" style="width: 100%;"></canvas>
|
|||
|
|
<div class="table-responsive">
|
|||
|
|
<table class="table table-sm align-middle">
|
|||
|
|
<thead>
|
|||
|
|
<tr>
|
|||
|
|
<th>日期</th>
|
|||
|
|
<th>流量(GB)</th>
|
|||
|
|
<th>峰值带宽(Mbps)</th>
|
|||
|
|
</tr>
|
|||
|
|
</thead>
|
|||
|
|
<tbody>
|
|||
|
|
{% for r in traffic_rows %}
|
|||
|
|
<tr>
|
|||
|
|
<td>{{ r.day }}</td>
|
|||
|
|
<td>{{ r.gb }}</td>
|
|||
|
|
<td>{% if r.peak_mbps %}{{ r.peak_mbps }}{% else %}<span class="text-muted">-</span>{% endif %}</td>
|
|||
|
|
</tr>
|
|||
|
|
{% endfor %}
|
|||
|
|
</tbody>
|
|||
|
|
</table>
|
|||
|
|
</div>
|
|||
|
|
{% else %}
|
|||
|
|
<div class="text-muted">暂无统计数据。</div>
|
|||
|
|
{% endif %}
|
|||
|
|
<div class="small text-muted">说明:统计数据来源于 GoEdge 每日拉取结果,仅供参考。</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div class="card mb-4">
|
|||
|
|
<div class="card-header">接入信息(CNAME)</div>
|
|||
|
|
<div class="card-body">
|
|||
|
|
<table class="table table-sm">
|
|||
|
|
<thead>
|
|||
|
|
<tr>
|
|||
|
|
<th>主机名</th>
|
|||
|
|
<th>期望 CNAME 目标</th>
|
|||
|
|
<th>实际解析</th>
|
|||
|
|
<th>状态</th>
|
|||
|
|
</tr>
|
|||
|
|
</thead>
|
|||
|
|
<tbody>
|
|||
|
|
{% if cname_results %}
|
|||
|
|
{% for r in cname_results %}
|
|||
|
|
<tr>
|
|||
|
|
<td>{{ r.hostname }}</td>
|
|||
|
|
<td><code>{{ r.expected }}</code></td>
|
|||
|
|
<td>
|
|||
|
|
{% if r.actual %}
|
|||
|
|
{% for a in r.actual %}<code>{{ a }}</code>{% if not forloop.last %}, {% endif %}{% endfor %}
|
|||
|
|
{% else %}
|
|||
|
|
<span class="text-muted">(无)</span>
|
|||
|
|
{% endif %}
|
|||
|
|
</td>
|
|||
|
|
<td>
|
|||
|
|
{% if r.ok %}
|
|||
|
|
<span class="badge text-bg-success">正确</span>
|
|||
|
|
{% else %}
|
|||
|
|
<span class="badge text-bg-warning">未匹配</span>
|
|||
|
|
{% if r.error %}<div class="text-muted">{{ r.error }}</div>{% endif %}
|
|||
|
|
{% endif %}
|
|||
|
|
</td>
|
|||
|
|
</tr>
|
|||
|
|
{% endfor %}
|
|||
|
|
{% else %}
|
|||
|
|
{% for host, target in domain.cname_targets.items %}
|
|||
|
|
<tr>
|
|||
|
|
<td>{{ host }}</td>
|
|||
|
|
<td><code>{{ target }}</code></td>
|
|||
|
|
<td class="text-muted">(未检测)</td>
|
|||
|
|
<td><span class="badge text-bg-secondary">未知</span></td>
|
|||
|
|
</tr>
|
|||
|
|
{% endfor %}
|
|||
|
|
{% endif %}
|
|||
|
|
</tbody>
|
|||
|
|
</table>
|
|||
|
|
|
|||
|
|
<form method="post" action="{% url 'domains:check_dns' domain_id=domain.id %}" class="mt-2">
|
|||
|
|
{% csrf_token %}
|
|||
|
|
<button type="submit" class="btn btn-primary">立即检测 DNS 生效</button>
|
|||
|
|
</form>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div class="card mb-4">
|
|||
|
|
<div class="card-header">GoEdge 同步状态</div>
|
|||
|
|
<div class="card-body">
|
|||
|
|
{% if goedge_status and not goedge_status.error %}
|
|||
|
|
<div class="row g-3">
|
|||
|
|
<div class="col-md-3">
|
|||
|
|
<div class="fw-semibold">Web ID</div>
|
|||
|
|
<div>{% if goedge_status.webId %}<span class="badge text-bg-secondary">{{ goedge_status.webId }}</span>{% else %}<span class="text-muted">未获取</span>{% endif %}</div>
|
|||
|
|
</div>
|
|||
|
|
<div class="col-md-3">
|
|||
|
|
<div class="fw-semibold">访问日志</div>
|
|||
|
|
{% if goedge_status.accessLog.isOn %}
|
|||
|
|
<span class="badge text-bg-success">已启用</span>
|
|||
|
|
{% elif goedge_status.accessLog.isOn is not none %}
|
|||
|
|
<span class="badge text-bg-secondary">未启用</span>
|
|||
|
|
{% else %}
|
|||
|
|
<span class="text-muted">未知</span>
|
|||
|
|
{% endif %}
|
|||
|
|
{% if goedge_status.accessLog.policyId %}
|
|||
|
|
<div class="text-muted small">PolicyId: {{ goedge_status.accessLog.policyId }}</div>
|
|||
|
|
{% endif %}
|
|||
|
|
</div>
|
|||
|
|
<div class="col-md-3">
|
|||
|
|
<div class="fw-semibold">WebSocket</div>
|
|||
|
|
{% if goedge_status.websocket.isOn %}
|
|||
|
|
<span class="badge text-bg-success">已启用</span>
|
|||
|
|
{% elif goedge_status.websocket.isOn is not none %}
|
|||
|
|
<span class="badge text-bg-secondary">未启用</span>
|
|||
|
|
{% else %}
|
|||
|
|
<span class="text-muted">未知</span>
|
|||
|
|
{% endif %}
|
|||
|
|
</div>
|
|||
|
|
<div class="col-md-3">
|
|||
|
|
<div class="fw-semibold">WAF</div>
|
|||
|
|
{% if goedge_status.firewall.isOn %}
|
|||
|
|
<span class="badge text-bg-success">已启用</span>
|
|||
|
|
{% elif goedge_status.firewall.isOn is not none %}
|
|||
|
|
<span class="badge text-bg-secondary">未启用</span>
|
|||
|
|
{% else %}
|
|||
|
|
<span class="text-muted">未知</span>
|
|||
|
|
{% endif %}
|
|||
|
|
{% if goedge_status.firewall.policyId %}
|
|||
|
|
<div class="text-muted small">PolicyId: {{ goedge_status.firewall.policyId }}</div>
|
|||
|
|
{% endif %}
|
|||
|
|
</div>
|
|||
|
|
<div class="col-md-3">
|
|||
|
|
<div class="fw-semibold">SSL策略</div>
|
|||
|
|
<div>{% if goedge_status.sslPolicy.id %}<span class="badge text-bg-secondary">{{ goedge_status.sslPolicy.id }}</span>{% else %}<span class="text-muted">未获取</span>{% endif %}</div>
|
|||
|
|
</div>
|
|||
|
|
<div class="col-md-3">
|
|||
|
|
<div class="fw-semibold">HTTP/3</div>
|
|||
|
|
{% if goedge_status.sslPolicy.http3Enabled %}
|
|||
|
|
<span class="badge text-bg-success">已启用</span>
|
|||
|
|
{% elif goedge_status.sslPolicy.http3Enabled is not none %}
|
|||
|
|
<span class="badge text-bg-secondary">未启用</span>
|
|||
|
|
{% else %}
|
|||
|
|
<span class="text-muted">未知</span>
|
|||
|
|
{% endif %}
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
{% elif goedge_status and goedge_status.error %}
|
|||
|
|
<div class="alert alert-warning">状态查询失败:{{ goedge_status.error }}</div>
|
|||
|
|
{% else %}
|
|||
|
|
<div class="text-muted">当前域名尚未关联 GoEdge 服务或无可查询信息。</div>
|
|||
|
|
{% endif %}
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
{% endblock %}
|
|||
|
|
|
|||
|
|
{% block extra_scripts %}
|
|||
|
|
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.1/dist/chart.umd.min.js"></script>
|
|||
|
|
<script>
|
|||
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|||
|
|
var rows = [
|
|||
|
|
{% for r in traffic_rows reversed %}
|
|||
|
|
{ day: '{{ r.day|date:"Y-m-d" }}', gb: {{ r.gb|default:0 }}, peak_mbps: {% if r.peak_mbps %}{{ r.peak_mbps }}{% else %}null{% endif %} },
|
|||
|
|
{% endfor %}
|
|||
|
|
];
|
|||
|
|
if (!rows.length || !window.Chart) return;
|
|||
|
|
var labels = rows.map(function(x){ return x.day; });
|
|||
|
|
var dataGb = rows.map(function(x){ return x.gb || 0; });
|
|||
|
|
var dataMbps = rows.map(function(x){ return x.peak_mbps === null ? null : x.peak_mbps; });
|
|||
|
|
var el = document.getElementById('trafficChart');
|
|||
|
|
if (!el) return;
|
|||
|
|
var ctx = el.getContext('2d');
|
|||
|
|
new Chart(ctx, {
|
|||
|
|
type: 'line',
|
|||
|
|
data: {
|
|||
|
|
labels: labels,
|
|||
|
|
datasets: [
|
|||
|
|
{
|
|||
|
|
label: '流量(GB)',
|
|||
|
|
data: dataGb,
|
|||
|
|
borderColor: '#0d6efd',
|
|||
|
|
backgroundColor: 'rgba(13,110,253,0.15)',
|
|||
|
|
fill: true,
|
|||
|
|
tension: 0.3,
|
|||
|
|
yAxisID: 'yGb'
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
label: '峰值带宽(Mbps)',
|
|||
|
|
data: dataMbps,
|
|||
|
|
borderColor: '#6f42c1',
|
|||
|
|
backgroundColor: 'rgba(111,66,193,0.08)',
|
|||
|
|
fill: false,
|
|||
|
|
tension: 0.3,
|
|||
|
|
yAxisID: 'yMbps'
|
|||
|
|
}
|
|||
|
|
]
|
|||
|
|
},
|
|||
|
|
options: {
|
|||
|
|
responsive: true,
|
|||
|
|
interaction: { mode: 'index', intersect: false },
|
|||
|
|
plugins: {
|
|||
|
|
legend: { position: 'bottom' },
|
|||
|
|
tooltip: {
|
|||
|
|
callbacks: {
|
|||
|
|
label: function(ctx){
|
|||
|
|
var suffix = ctx.dataset.yAxisID === 'yGb' ? ' GB' : ' Mbps';
|
|||
|
|
return ctx.dataset.label + ': ' + ctx.formattedValue + suffix;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
scales: {
|
|||
|
|
yGb: { type: 'linear', position: 'left', title: { display: true, text: 'GB' } },
|
|||
|
|
yMbps: { type: 'linear', position: 'right', title: { display: true, text: 'Mbps' }, grid: { drawOnChartArea: false } }
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
});
|
|||
|
|
</script>
|
|||
|
|
{% endblock %}
|