docs: add comprehensive README.md and fix bugs

- add README.md with usage and deployment guide
- fix category sync logic in backend
- fix URL overflow in admin services list
- fix data caching issues in front-end and back-end
- add 'View Front-end' button in admin dashboard
This commit is contained in:
OpenClaw Agent
2026-02-13 07:00:49 +08:00
parent 872526505e
commit 521cd9ba42
10 changed files with 2884 additions and 218 deletions

View File

@@ -15,10 +15,7 @@
<!-- 分类 Tabs -->
<div class="tabs" id="categoryTabs">
<button class="tab-btn active" data-category="all">全部</button>
<button class="tab-btn" data-category="内网服务">内网服务</button>
<button class="tab-btn" data-category="开发工具">开发工具</button>
<button class="tab-btn" data-category="测试环境">测试环境</button>
<div class="loading" style="color: #8c8c8c; font-size: 12px; padding: 10px;">加载分类...</div>
</div>
<!-- 服务卡片网格 -->
@@ -137,20 +134,27 @@
}
.card-status {
width: 12px;
height: 12px;
width: 14px;
height: 14px;
border-radius: 50%;
background: #d9d9d9;
display: flex;
align-items: center;
justify-content: center;
font-size: 10px;
flex-shrink: 0;
}
.card-status.online {
background: #52c41a;
box-shadow: 0 0 8px rgba(82, 196, 26, 0.5);
color: #fff;
box-shadow: 0 0 10px rgba(82, 196, 26, 0.6);
}
.card-status.offline {
background: #ff4d4f;
box-shadow: 0 0 8px rgba(255, 77, 79, 0.5);
color: #fff;
box-shadow: 0 0 10px rgba(255, 77, 79, 0.6);
}
.card-name {
@@ -231,7 +235,7 @@
// 加载分类
async function loadCategories() {
try {
const response = await fetch('/api/categories');
const response = await fetch(`/api/categories?t=${new Date().getTime()}`);
allCategories = await response.json();
renderTabs();
} catch (err) {
@@ -242,10 +246,11 @@
// 加载服务
async function loadServices() {
try {
const response = await fetch('/api/services');
const response = await fetch(`/api/services?t=${new Date().getTime()}`);
allServices = await response.json();
renderServices(window.currentTab || 'all');
updateLastCheckTime();
loadHealthStatus();
} catch (err) {
console.error('加载服务失败:', err);
document.getElementById('servicesGrid').innerHTML =
@@ -253,6 +258,26 @@
}
}
// 加载健康状态
async function loadHealthStatus() {
try {
const response = await fetch('/api/admin/health-check', {
method: 'POST'
});
const data = await response.json();
if (data.results) {
data.results.forEach(result => {
healthStatus[result.id] = result.status;
});
// 重新渲染以显示状态
renderServices(window.currentTab || 'all');
}
} catch (err) {
console.error('健康检测失败:', err);
}
}
// 渲染分类 Tabs
function renderTabs() {
const tabsContainer = document.getElementById('categoryTabs');
@@ -297,13 +322,22 @@
let html = '';
filteredServices.forEach((service, index) => {
const status = healthStatus[service.id] || 'unknown';
const statusClass = status === 'online' ? 'online' : (status === 'offline' ? 'offline' : '');
let statusClass = '';
let statusIcon = '';
if (status === 'online') {
statusClass = 'online';
statusIcon = '✓';
} else if (status === 'offline') {
statusClass = 'offline';
statusIcon = '✗';
}
html += `
<a href="${service.url}" target="_blank" class="service-card" style="animation-delay: ${index * 0.05}s">
<div class="card-header">
<span class="card-icon">${service.icon || '📡'}</span>
<span class="card-status ${statusClass}"></span>
<span class="card-status ${statusClass}">${statusIcon}</span>
</div>
<div class="card-name">${service.name}</div>
<div class="card-desc">${service.description || ''}</div>
@@ -331,5 +365,12 @@
setInterval(() => {
loadServices();
}, 30000);
// 页面显示时刷新
document.addEventListener('visibilitychange', function() {
if (!document.hidden) {
loadServices();
}
});
</script>
{% endblock %}