feat: 初始化Webhook中继系统项目
- 添加FastAPI应用基础结构,包括主入口、路由和模型定义
- 实现Webhook接收端点(/webhook/{namespace})和健康检查(/health)
- 添加管理后台路由和模板,支持端点、目标、渠道和模板管理
- 包含SQLite数据库模型定义和初始化逻辑
- 添加日志记录和统计服务
- 包含Dockerfile和配置示例文件
- 添加项目文档,包括设计、流程图和验收标准
This commit is contained in:
@@ -0,0 +1,123 @@
|
||||
{% extends "admin/base.html" %}
|
||||
{% block title %}系统日志 - Webhook{% endblock %}
|
||||
{% block content %}
|
||||
<div class="row mb-3">
|
||||
<div class="col">
|
||||
<h3>请求日志 (最近100条)</h3>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<form action="/admin/logs/clear" method="post" onsubmit="return confirm('确定清空所有日志?')">
|
||||
<button type="submit" class="btn btn-outline-danger">清空日志</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="accordion" id="logsAccordion">
|
||||
{% for log in logs %}
|
||||
<div class="accordion-item">
|
||||
<h2 class="accordion-header" id="heading{{ log.id }}">
|
||||
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapse{{ log.id }}">
|
||||
<div class="d-flex w-100 justify-content-between me-3 align-items-center">
|
||||
<div>
|
||||
<span class="badge bg-secondary me-2">{{ log.received_at.strftime('%m-%d %H:%M:%S') }}</span>
|
||||
<span class="fw-bold me-2">{{ log.namespace }}</span>
|
||||
{% if log.status == 'replay' %}
|
||||
<span class="badge bg-info text-dark me-1">重试</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div>
|
||||
{% set success_count = 0 %}
|
||||
{% set fail_count = 0 %}
|
||||
{% for d in log.delivery_logs %}
|
||||
{% if d.status == 'success' %}
|
||||
{% set success_count = success_count + 1 %}
|
||||
{% else %}
|
||||
{% set fail_count = fail_count + 1 %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
{% if fail_count > 0 %}
|
||||
<span class="badge bg-danger">{{ fail_count }} 失败</span>
|
||||
{% endif %}
|
||||
<span class="badge bg-success">{{ success_count }} 成功</span>
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
</h2>
|
||||
<div id="collapse{{ log.id }}" class="accordion-collapse collapse" data-bs-parent="#logsAccordion">
|
||||
<div class="accordion-body">
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="d-flex justify-content-between align-items-center mb-2">
|
||||
<h6 class="mb-0">原始 Payload</h6>
|
||||
<button class="btn btn-sm btn-outline-secondary" onclick="replayLog('{{ log.id }}')">
|
||||
↺ 重试 (Replay)
|
||||
</button>
|
||||
</div>
|
||||
<pre class="bg-light p-2 border rounded" style="max-height: 300px; overflow-y: auto;">{{ log.raw_body | tojson(indent=2) }}</pre>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<h6>分发记录</h6>
|
||||
<ul class="list-group">
|
||||
{% for d in log.delivery_logs %}
|
||||
<li class="list-group-item d-flex justify-content-between align-items-start">
|
||||
<div class="ms-2 me-auto">
|
||||
<div class="fw-bold">
|
||||
{% if d.type == 'relay' %}
|
||||
<span class="badge bg-primary me-1">转发</span>
|
||||
{% else %}
|
||||
<span class="badge bg-success me-1">通知</span>
|
||||
{% endif %}
|
||||
{{ d.target_name }}
|
||||
</div>
|
||||
<small class="text-muted text-break">{{ d.response_summary }}</small>
|
||||
</div>
|
||||
{% if d.status == 'success' %}
|
||||
<span class="badge bg-success rounded-pill">成功</span>
|
||||
{% else %}
|
||||
<span class="badge bg-danger rounded-pill">失败</span>
|
||||
{% endif %}
|
||||
</li>
|
||||
{% endfor %}
|
||||
{% if not log.delivery_logs %}
|
||||
<li class="list-group-item text-muted">无分发记录</li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
{% if not logs %}
|
||||
<div class="text-center py-5 text-muted">
|
||||
暂无日志记录
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
{% block scripts %}
|
||||
<script>
|
||||
async function replayLog(id) {
|
||||
if(!confirm("确定要使用此 Payload 重新触发处理流程吗?")) return;
|
||||
|
||||
try {
|
||||
const response = await fetch(`/admin/logs/${id}/replay`, {
|
||||
method: 'POST'
|
||||
});
|
||||
const data = await response.json();
|
||||
|
||||
if (response.ok) {
|
||||
alert("重试成功,已生成新的日志记录 ID: " + data.new_log_id);
|
||||
window.location.reload();
|
||||
} else {
|
||||
alert("重试失败: " + (data.error || "未知错误"));
|
||||
}
|
||||
} catch (e) {
|
||||
alert("请求错误: " + e);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user