添加copyright

This commit is contained in:
CN-JS-HuiBai
2026-04-06 16:58:58 +08:00
parent d469dacc08
commit b4415f25ac
5 changed files with 157 additions and 9 deletions

View File

@@ -2695,4 +2695,72 @@ input:checked+.slider:before {
color: var(--accent-indigo); color: var(--accent-indigo);
background: rgba(99, 102, 241, 0.1); background: rgba(99, 102, 241, 0.1);
border-color: var(--accent-indigo); border-color: var(--accent-indigo);
}
/* ---- Footer ---- */
.site-footer {
margin-top: 40px;
padding: 30px 28px;
border-top: 1px solid var(--border-color);
background: rgba(10, 14, 26, 0.4);
backdrop-filter: blur(10px);
position: relative;
z-index: 10;
}
:root.light-theme .site-footer {
background: rgba(255, 255, 255, 0.4);
}
.footer-content {
max-width: 1600px;
margin: 0 auto;
display: flex;
justify-content: space-between;
align-items: center;
flex-wrap: wrap;
gap: 20px;
}
.copyright {
font-size: 0.88rem;
color: var(--text-muted);
font-weight: 500;
}
.filings {
display: flex;
align-items: center;
gap: 20px;
}
.filings a {
font-size: 0.82rem;
color: var(--text-muted);
text-decoration: none;
transition: color 0.2s;
display: flex;
align-items: center;
}
.filings a:hover {
color: var(--accent-indigo);
}
@media (max-width: 768px) {
.site-footer {
padding: 24px 16px;
margin-top: 20px;
}
.footer-content {
flex-direction: column;
text-align: center;
gap: 12px;
}
.filings {
flex-direction: column;
gap: 8px;
}
} }

View File

@@ -354,6 +354,20 @@
</section> </section>
</main> </main>
<!-- Footer -->
<footer class="site-footer">
<div class="footer-content">
<div class="copyright">© <span id="copyrightYear"></span> LDNET-GA-Service. All rights reserved.</div>
<div class="filings">
<a href="https://beian.miit.gov.cn/" target="_blank" id="icpFilingDisplay" style="display: none;"></a>
<a href="http://www.beian.gov.cn/portal/registerSystemInfo" target="_blank" id="psFilingDisplay" style="display: none;">
<img src="https://www.beian.gov.cn/img/ghs.png" alt="公安备案" style="width: 14px; height: 14px; vertical-align: middle; margin-right: 2px;">
<span id="psFilingText"></span>
</a>
</div>
</div>
</footer>
<!-- Settings Modal --> <!-- Settings Modal -->
<div class="modal-overlay" id="settingsModal"> <div class="modal-overlay" id="settingsModal">
<div class="modal"> <div class="modal">
@@ -467,6 +481,14 @@
<option value="both">统计上行+下行 (Sum)</option> <option value="both">统计上行+下行 (Sum)</option>
</select> </select>
</div> </div>
<div class="form-group" style="margin-top: 15px;">
<label for="icpFilingInput">ICP 备案号 (如京ICP备12345678号)</label>
<input type="text" id="icpFilingInput" placeholder="请输入 ICP 备案号">
</div>
<div class="form-group" style="margin-top: 15px;">
<label for="psFilingInput">公安备案号 (如:京公网安备 11010102000001号)</label>
<input type="text" id="psFilingInput" placeholder="请输入公安备案号">
</div>
<div class="form-actions" style="margin-top: 25px; display: flex; justify-content: flex-end;"> <div class="form-actions" style="margin-top: 25px; display: flex; justify-content: flex-end;">
<button class="btn btn-add" id="btnSaveSiteSettings">保存基础设置</button> <button class="btn btn-add" id="btnSaveSiteSettings">保存基础设置</button>
</div> </div>

View File

@@ -105,7 +105,14 @@
partitionSummary: document.getElementById('partitionSummary'), partitionSummary: document.getElementById('partitionSummary'),
partitionHeader: document.getElementById('partitionHeader'), partitionHeader: document.getElementById('partitionHeader'),
globeCard: document.getElementById('globeCard'), globeCard: document.getElementById('globeCard'),
btnExpandGlobe: document.getElementById('btnExpandGlobe') btnExpandGlobe: document.getElementById('btnExpandGlobe'),
// Footer & Filing
icpFilingInput: document.getElementById('icpFilingInput'),
psFilingInput: document.getElementById('psFilingInput'),
icpFilingDisplay: document.getElementById('icpFilingDisplay'),
psFilingDisplay: document.getElementById('psFilingDisplay'),
psFilingText: document.getElementById('psFilingText'),
copyrightYear: document.getElementById('copyrightYear')
}; };
// ---- State ---- // ---- State ----
@@ -146,6 +153,11 @@
updateGaugesTime(); updateGaugesTime();
setInterval(updateGaugesTime, 1000); setInterval(updateGaugesTime, 1000);
// Initial footer year
if (dom.copyrightYear) {
dom.copyrightYear.textContent = new Date().getFullYear();
}
// Initial theme check (localStorage handled after site settings load to ensure priority) // Initial theme check (localStorage handled after site settings load to ensure priority)
// Network chart // Network chart
@@ -473,6 +485,8 @@
if (dom.defaultThemeInput) dom.defaultThemeInput.value = window.SITE_SETTINGS.default_theme || 'dark'; if (dom.defaultThemeInput) dom.defaultThemeInput.value = window.SITE_SETTINGS.default_theme || 'dark';
if (dom.show95BandwidthInput) dom.show95BandwidthInput.value = window.SITE_SETTINGS.show_95_bandwidth ? "1" : "0"; if (dom.show95BandwidthInput) dom.show95BandwidthInput.value = window.SITE_SETTINGS.show_95_bandwidth ? "1" : "0";
if (dom.p95TypeSelect) dom.p95TypeSelect.value = window.SITE_SETTINGS.p95_type || 'tx'; if (dom.p95TypeSelect) dom.p95TypeSelect.value = window.SITE_SETTINGS.p95_type || 'tx';
if (dom.icpFilingInput) dom.icpFilingInput.value = window.SITE_SETTINGS.icp_filing || '';
if (dom.psFilingInput) dom.psFilingInput.value = window.SITE_SETTINGS.ps_filing || '';
// Latency routes loaded separately in openSettings or on startup // Latency routes loaded separately in openSettings or on startup
} }
@@ -1639,6 +1653,8 @@
networkChart.draw(); networkChart.draw();
} }
} }
if (dom.icpFilingInput) dom.icpFilingInput.value = settings.icp_filing || '';
if (dom.psFilingInput) dom.psFilingInput.value = settings.ps_filing || '';
// Apply to UI // Apply to UI
applySiteSettings(settings); applySiteSettings(settings);
@@ -1726,6 +1742,25 @@
applyTheme(settings.default_theme); applyTheme(settings.default_theme);
} }
} }
// Filing info
if (dom.icpFilingDisplay) {
if (settings.icp_filing) {
dom.icpFilingDisplay.textContent = settings.icp_filing;
dom.icpFilingDisplay.style.display = 'inline-block';
} else {
dom.icpFilingDisplay.style.display = 'none';
}
}
if (dom.psFilingDisplay) {
if (settings.ps_filing) {
if (dom.psFilingText) dom.psFilingText.textContent = settings.ps_filing;
dom.psFilingDisplay.style.display = 'inline-block';
} else {
dom.psFilingDisplay.style.display = 'none';
}
}
} }
async function saveSiteSettings() { async function saveSiteSettings() {
@@ -1741,7 +1776,9 @@
logo_url: dom.logoUrlInput.value.trim(), logo_url: dom.logoUrlInput.value.trim(),
default_theme: dom.defaultThemeInput.value, default_theme: dom.defaultThemeInput.value,
show_95_bandwidth: dom.show95BandwidthInput.value === "1" ? 1 : 0, show_95_bandwidth: dom.show95BandwidthInput.value === "1" ? 1 : 0,
p95_type: dom.p95TypeSelect.value p95_type: dom.p95TypeSelect.value,
icp_filing: dom.icpFilingInput ? dom.icpFilingInput.value : '',
ps_filing: dom.psFilingInput ? dom.psFilingInput.value : ''
}; };
// If user sets default to auto, we should clear their manual override or set it to auto // If user sets default to auto, we should clear their manual override or set it to auto

View File

@@ -87,6 +87,16 @@ async function checkAndFixDatabase() {
await db.query("ALTER TABLE site_settings ADD COLUMN latency_target VARCHAR(255) AFTER latency_dest"); await db.query("ALTER TABLE site_settings ADD COLUMN latency_target VARCHAR(255) AFTER latency_dest");
console.log(`[Database Integrity] ✅ Column 'latency_target' added.`); console.log(`[Database Integrity] ✅ Column 'latency_target' added.`);
} }
if (!columnNames.includes('icp_filing')) {
console.log(`[Database Integrity] ⚠️ Missing column 'icp_filing' in 'site_settings'. Adding it...`);
await db.query("ALTER TABLE site_settings ADD COLUMN icp_filing VARCHAR(255) AFTER latency_target");
console.log(`[Database Integrity] ✅ Column 'icp_filing' added.`);
}
if (!columnNames.includes('ps_filing')) {
console.log(`[Database Integrity] ⚠️ Missing column 'ps_filing' in 'site_settings'. Adding it...`);
await db.query("ALTER TABLE site_settings ADD COLUMN ps_filing VARCHAR(255) AFTER icp_filing");
console.log(`[Database Integrity] ✅ Column 'ps_filing' added.`);
}
} catch (err) { } catch (err) {
console.error('[Database Integrity] ❌ Error checking integrity:', err.message); console.error('[Database Integrity] ❌ Error checking integrity:', err.message);
} }
@@ -132,6 +142,8 @@ async function createTable(tableName) {
latency_source VARCHAR(100), latency_source VARCHAR(100),
latency_dest VARCHAR(100), latency_dest VARCHAR(100),
latency_target VARCHAR(255), latency_target VARCHAR(255),
icp_filing VARCHAR(255),
ps_filing VARCHAR(255),
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
`); `);

View File

@@ -269,6 +269,8 @@ app.post('/api/setup/init', async (req, res) => {
latency_source VARCHAR(100), latency_source VARCHAR(100),
latency_dest VARCHAR(100), latency_dest VARCHAR(100),
latency_target VARCHAR(255), latency_target VARCHAR(255),
icp_filing VARCHAR(255),
ps_filing VARCHAR(255),
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
`); `);
@@ -412,7 +414,9 @@ const serveIndex = async (req, res) => {
blackbox_source_id: null, blackbox_source_id: null,
latency_source: null, latency_source: null,
latency_dest: null, latency_dest: null,
latency_target: null latency_target: null,
icp_filing: null,
ps_filing: null
}; };
if (isDbInitialized) { if (isDbInitialized) {
@@ -564,7 +568,9 @@ app.get('/api/settings', async (req, res) => {
blackbox_source_id: null, blackbox_source_id: null,
latency_source: null, latency_source: null,
latency_dest: null, latency_dest: null,
latency_target: null latency_target: null,
icp_filing: null,
ps_filing: null
}); });
} }
res.json(rows[0]); res.json(rows[0]);
@@ -576,11 +582,11 @@ app.get('/api/settings', async (req, res) => {
// Update site settings // Update site settings
app.post('/api/settings', requireAuth, async (req, res) => { app.post('/api/settings', requireAuth, async (req, res) => {
const { page_name, title, logo_url, default_theme, show_95_bandwidth, p95_type, blackbox_source_id, latency_source, latency_dest, latency_target } = req.body; const { page_name, title, logo_url, default_theme, show_95_bandwidth, p95_type, blackbox_source_id, latency_source, latency_dest, latency_target, icp_filing, ps_filing } = req.body;
try { try {
await db.query( await db.query(
`INSERT INTO site_settings (id, page_name, title, logo_url, default_theme, show_95_bandwidth, p95_type, blackbox_source_id, latency_source, latency_dest, latency_target) `INSERT INTO site_settings (id, page_name, title, logo_url, default_theme, show_95_bandwidth, p95_type, blackbox_source_id, latency_source, latency_dest, latency_target, icp_filing, ps_filing)
VALUES (1, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) VALUES (1, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
ON DUPLICATE KEY UPDATE ON DUPLICATE KEY UPDATE
page_name = VALUES(page_name), page_name = VALUES(page_name),
title = VALUES(title), title = VALUES(title),
@@ -591,12 +597,15 @@ app.post('/api/settings', requireAuth, async (req, res) => {
blackbox_source_id = VALUES(blackbox_source_id), blackbox_source_id = VALUES(blackbox_source_id),
latency_source = VALUES(latency_source), latency_source = VALUES(latency_source),
latency_dest = VALUES(latency_dest), latency_dest = VALUES(latency_dest),
latency_target = VALUES(latency_target)`, latency_target = VALUES(latency_target),
icp_filing = VALUES(icp_filing),
ps_filing = VALUES(ps_filing)`,
[ [
page_name, title, logo_url, default_theme, page_name, title, logo_url, default_theme,
show_95_bandwidth ? 1 : 0, p95_type || 'tx', show_95_bandwidth ? 1 : 0, p95_type || 'tx',
blackbox_source_id || null, latency_source || null, blackbox_source_id || null, latency_source || null,
latency_dest || null, latency_target || null latency_dest || null, latency_target || null,
icp_filing || null, ps_filing || null
] ]
); );
res.json({ success: true }); res.json({ success: true });