修复无法正常登录的问题

This commit is contained in:
CN-JS-HuiBai
2026-04-05 01:22:25 +08:00
parent af83f42d26
commit 0f4d3a2986
3 changed files with 144 additions and 156 deletions

View File

@@ -362,43 +362,57 @@
function initGlobe() { function initGlobe() {
if (!dom.globeContainer) return; if (!dom.globeContainer) return;
myGlobe = Globe() if (typeof Globe !== 'function') {
(dom.globeContainer) console.warn('[Globe] Globe.gl library not loaded. 3D visualization will be disabled.');
.globeImageUrl('//unpkg.com/three-globe/example/img/earth-dark.jpg') dom.globeContainer.innerHTML = `
.bumpImageUrl('//unpkg.com/three-globe/example/img/earth-topology.png') <div style="height: 100%; display: flex; align-items: center; justify-content: center; color: var(--text-muted); font-size: 0.8rem; text-align: center; padding: 20px;">
.backgroundColor('rgba(0,0,0,0)') Globe.gl 库加载失败<br>请检查网络连接或刷新页面
.showAtmosphere(true)
.atmosphereColor('#6366f1')
.atmosphereDaylightAlpha(0.1)
.pointsData([])
.pointColor(() => '#06b6d4')
.pointAltitude(0.05)
.pointRadius(0.8)
.pointsMerge(true)
.pointLabel(d => `
<div style="background: rgba(10, 14, 26, 0.9); padding: 8px 12px; border: 1px solid var(--accent-indigo); border-radius: 8px; backdrop-filter: blur(8px);">
<div style="font-weight: 700; color: #fff; margin-bottom: 4px;">${escapeHtml(d.job)}</div>
<div style="font-size: 0.75rem; color: var(--text-secondary);">${escapeHtml(d.city || '')}, ${escapeHtml(d.countryName || d.country || '')}</div>
<div style="font-size: 0.75rem; margin-top: 4px;">
<span style="color: var(--accent-indigo);">BW:</span> ↓${formatBandwidth(d.netRx)}${formatBandwidth(d.netTx)}
</div> </div>
</div> `;
`); return;
}
// Resizing try {
const resizeObserver = new ResizeObserver(() => { myGlobe = Globe()
if (myGlobe) { (dom.globeContainer)
const width = dom.globeContainer.clientWidth; .globeImageUrl('//unpkg.com/three-globe/example/img/earth-dark.jpg')
const height = dom.globeContainer.clientHeight; .bumpImageUrl('//unpkg.com/three-globe/example/img/earth-topology.png')
myGlobe.width(width).height(height); .backgroundColor('rgba(0,0,0,0)')
} .showAtmosphere(true)
}); .atmosphereColor('#6366f1')
resizeObserver.observe(dom.globeContainer); .atmosphereDaylightAlpha(0.1)
.pointsData([])
.pointColor(() => '#06b6d4')
.pointAltitude(0.05)
.pointRadius(0.8)
.pointsMerge(true)
.pointLabel(d => `
<div style="background: rgba(10, 14, 26, 0.9); padding: 8px 12px; border: 1px solid var(--accent-indigo); border-radius: 8px; backdrop-filter: blur(8px);">
<div style="font-weight: 700; color: #fff; margin-bottom: 4px;">${escapeHtml(d.job)}</div>
<div style="font-size: 0.75rem; color: var(--text-secondary);">${escapeHtml(d.city || '')}, ${escapeHtml(d.countryName || d.country || '')}</div>
<div style="font-size: 0.75rem; margin-top: 4px;">
<span style="color: var(--accent-indigo);">BW:</span> ↓${formatBandwidth(d.netRx)}${formatBandwidth(d.netTx)}
</div>
</div>
`);
// Initial view // Resizing
myGlobe.controls().autoRotate = true; const resizeObserver = new ResizeObserver(() => {
myGlobe.controls().autoRotateSpeed = 0.5; if (myGlobe) {
myGlobe.controls().enableZoom = false; // Disable zoom to maintain dashboard feel const width = dom.globeContainer.clientWidth;
const height = dom.globeContainer.clientHeight;
myGlobe.width(width).height(height);
}
});
resizeObserver.observe(dom.globeContainer);
// Initial view
myGlobe.controls().autoRotate = true;
myGlobe.controls().autoRotateSpeed = 0.5;
myGlobe.controls().enableZoom = false; // Disable zoom to maintain dashboard feel
} catch (err) {
console.error('[Globe] Initialization failed:', err);
}
} }
function updateGlobe(servers) { function updateGlobe(servers) {

View File

@@ -18,16 +18,9 @@ const REQUIRED_TABLES = [
]; ];
async function checkAndFixDatabase() { async function checkAndFixDatabase() {
// Only run if .env is already configured
const envPath = path.join(__dirname, '..', '.env'); const envPath = path.join(__dirname, '..', '.env');
if (!fs.existsSync(envPath)) return; if (!fs.existsSync(envPath)) return;
const dbHost = process.env.MYSQL_HOST || 'localhost';
const dbUser = process.env.MYSQL_USER || 'root';
const dbPass = process.env.MYSQL_PASSWORD || '';
const dbPort = parseInt(process.env.MYSQL_PORT) || 3306;
const dbName = process.env.MYSQL_DATABASE || 'display_wall';
try { try {
// Check tables // Check tables
const [rows] = await db.query("SHOW TABLES"); const [rows] = await db.query("SHOW TABLES");
@@ -36,110 +29,88 @@ async function checkAndFixDatabase() {
const missingTables = REQUIRED_TABLES.filter(t => !existingTables.includes(t)); const missingTables = REQUIRED_TABLES.filter(t => !existingTables.includes(t));
if (missingTables.length > 0) { if (missingTables.length > 0) {
console.log(`[Database Integrity] ⚠️ Missing tables: ${missingTables.join(', ')}`); console.log(`[Database Integrity] ⚠️ Missing tables: ${missingTables.join(', ')}. Creating them...`);
await recreateDatabase(dbHost, dbPort, dbUser, dbPass, dbName);
} else { for (const table of missingTables) {
// console.log(`[Database Integrity] ✅ All tables accounted for.`); await createTable(table);
}
console.log(`[Database Integrity] ✅ Missing tables created.`);
} }
} catch (err) { } catch (err) {
if (err.code === 'ER_BAD_DB_ERROR') { console.error('[Database Integrity] ❌ Error checking integrity:', err.message);
console.log(`[Database Integrity] ⚠️ Database "${dbName}" does not exist.`);
await recreateDatabase(dbHost, dbPort, dbUser, dbPass, dbName);
} else {
console.error('[Database Integrity] ❌ Error checking integrity:', err.message);
}
} }
} }
async function recreateDatabase(host, port, user, password, dbName) { async function createTable(tableName) {
console.log(`[Database Integrity] 🔄 Re-initializing database "${dbName}"...`); console.log(` - Creating table "${tableName}"...`);
switch (tableName) {
let connection; case 'users':
try { await db.query(`
connection = await mysql.createConnection({ host, port, user, password }); CREATE TABLE IF NOT EXISTS users (
id INT AUTO_INCREMENT PRIMARY KEY,
// Drop and create database username VARCHAR(255) NOT NULL UNIQUE,
await connection.query(`DROP DATABASE IF EXISTS \`${dbName}\``); password VARCHAR(255) NOT NULL,
await connection.query(`CREATE DATABASE \`${dbName}\` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci`); salt VARCHAR(255) NOT NULL,
await connection.query(`USE \`${dbName}\``); created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
// Recreate all tables `);
console.log(' - Creating table "users"...'); break;
await connection.query(` case 'prometheus_sources':
CREATE TABLE IF NOT EXISTS users ( await db.query(`
id INT AUTO_INCREMENT PRIMARY KEY, CREATE TABLE IF NOT EXISTS prometheus_sources (
username VARCHAR(255) NOT NULL UNIQUE, id INT AUTO_INCREMENT PRIMARY KEY,
password VARCHAR(255) NOT NULL, name VARCHAR(255) NOT NULL,
salt VARCHAR(255) NOT NULL, url VARCHAR(500) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP description TEXT,
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
`); updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
console.log(' - Creating table "prometheus_sources"...'); `);
await connection.query(` break;
CREATE TABLE IF NOT EXISTS prometheus_sources ( case 'site_settings':
id INT AUTO_INCREMENT PRIMARY KEY, await db.query(`
name VARCHAR(255) NOT NULL, CREATE TABLE IF NOT EXISTS site_settings (
url VARCHAR(500) NOT NULL, id INT PRIMARY KEY DEFAULT 1,
description TEXT, page_name VARCHAR(255) DEFAULT '数据可视化展示大屏',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, title VARCHAR(255) DEFAULT '数据可视化展示大屏',
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP logo_url TEXT,
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci default_theme VARCHAR(20) DEFAULT 'dark',
`); updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
console.log(' - Creating table "site_settings"...'); `);
await connection.query(` await db.query(`
CREATE TABLE IF NOT EXISTS site_settings ( INSERT IGNORE INTO site_settings (id, page_name, title, default_theme)
id INT PRIMARY KEY DEFAULT 1, VALUES (1, '数据可视化展示大屏', '数据可视化展示大屏', 'dark')
page_name VARCHAR(255) DEFAULT '数据可视化展示大屏', `);
title VARCHAR(255) DEFAULT '数据可视化展示大屏', break;
logo_url TEXT, case 'traffic_stats':
default_theme VARCHAR(20) DEFAULT 'dark', await db.query(`
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP CREATE TABLE IF NOT EXISTS traffic_stats (
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci id INT AUTO_INCREMENT PRIMARY KEY,
`); rx_bytes BIGINT UNSIGNED DEFAULT 0,
await connection.query(` tx_bytes BIGINT UNSIGNED DEFAULT 0,
INSERT INTO site_settings (id, page_name, title, default_theme) rx_bandwidth DOUBLE DEFAULT 0,
VALUES (1, '数据可视化展示大屏', '数据可视化展示大屏', 'dark') tx_bandwidth DOUBLE DEFAULT 0,
`); timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
UNIQUE INDEX (timestamp)
console.log(' - Creating table "traffic_stats"...'); ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
await connection.query(` `);
CREATE TABLE IF NOT EXISTS traffic_stats ( break;
id INT AUTO_INCREMENT PRIMARY KEY, case 'server_locations':
rx_bytes BIGINT UNSIGNED DEFAULT 0, await db.query(`
tx_bytes BIGINT UNSIGNED DEFAULT 0, CREATE TABLE IF NOT EXISTS server_locations (
rx_bandwidth DOUBLE DEFAULT 0, id INT AUTO_INCREMENT PRIMARY KEY,
tx_bandwidth DOUBLE DEFAULT 0, ip VARCHAR(255) NOT NULL UNIQUE,
timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP, country CHAR(2),
UNIQUE INDEX (timestamp) country_name VARCHAR(100),
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci region VARCHAR(100),
`); city VARCHAR(100),
latitude DOUBLE,
console.log(' - Creating table "server_locations"...'); longitude DOUBLE,
await connection.query(` last_updated TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
CREATE TABLE IF NOT EXISTS server_locations ( ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
id INT AUTO_INCREMENT PRIMARY KEY, `);
ip VARCHAR(255) NOT NULL UNIQUE, break;
country CHAR(2),
country_name VARCHAR(100),
region VARCHAR(100),
city VARCHAR(100),
latitude DOUBLE,
longitude DOUBLE,
last_updated TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
`);
console.log(`[Database Integrity] ✅ Re-initialization complete.`);
// Refresh db pool in the main app context
db.initPool();
} catch (err) {
console.error('[Database Integrity] ❌ Critical failure during re-initialization:', err.message);
} finally {
if (connection) await connection.end();
} }
} }

View File

@@ -582,25 +582,28 @@ app.get('/api/metrics/overview', async (req, res) => {
// --- Add Geo Information to Servers --- // --- Add Geo Information to Servers ---
const geoServers = await Promise.all(overview.servers.map(async (server) => { const geoServers = await Promise.all(overview.servers.map(async (server) => {
// The original IP is masked in the response from prometheusService.getOverviewMetrics
// But we can get it back from serverIdMap in prometheusService if we are on the same process
// Actually, prometheusService needs to provide a way to get the real IP back.
// Or we can just modify getOverviewMetrics to return the real IP for internal use.
// Let's look at prometheusService.js's getServerIdMap logic
const realInstance = prometheusService.resolveToken(server.instance); const realInstance = prometheusService.resolveToken(server.instance);
const cleanIp = realInstance.split(':')[0]; const cleanIp = realInstance.split(':')[0];
// Attempt to get location // Try to get from DB cache only (fast)
const location = await geoService.getLocation(cleanIp); try {
if (location) { const [rows] = await db.query('SELECT * FROM server_locations WHERE ip = ?', [cleanIp]);
return { if (rows.length > 0) {
...server, const location = rows[0];
country: location.country, return {
countryName: location.country_name, ...server,
lat: location.latitude, country: location.country,
lng: location.longitude countryName: location.country_name,
}; city: location.city,
lat: location.latitude,
lng: location.longitude
};
} else {
// Trigger background resolution for future requests
geoService.getLocation(cleanIp).catch(() => {});
}
} catch (e) {
// DB error, skip geo for now
} }
return server; return server;
})); }));