diff --git a/public/init.html b/public/init.html new file mode 100644 index 0000000..4c224ee --- /dev/null +++ b/public/init.html @@ -0,0 +1,131 @@ + + + + + + 系统初始化 - 数据可视化展示大屏 + + + + + + + + +
+
+
+ +
+
+

系统初始化

+

请配置您的 MySQL 数据库连接信息以完成首次设置

+
+ +
+
+
+ + +
+
+ + +
+
+ +
+
+ + +
+
+ +
+
+ + +
+
+ +
+
+ + +
+
+ +
+ +
+ + +
+
+
+ + + + diff --git a/public/js/init.js b/public/js/init.js new file mode 100644 index 0000000..d40e099 --- /dev/null +++ b/public/js/init.js @@ -0,0 +1,79 @@ +document.addEventListener('DOMContentLoaded', () => { + const hostInput = document.getElementById('host'); + const portInput = document.getElementById('port'); + const userInput = document.getElementById('user'); + const passwordInput = document.getElementById('password'); + const databaseInput = document.getElementById('database'); + + const btnTest = document.getElementById('btnTest'); + const btnInit = document.getElementById('btnInit'); + const messageBox = document.getElementById('messageBox'); + + function showMessage(msg, isError = false) { + messageBox.textContent = msg; + messageBox.className = 'form-message ' + (isError ? 'error' : 'success'); + } + + btnTest.addEventListener('click', async () => { + btnTest.disabled = true; + const oldText = btnTest.textContent; + btnTest.textContent = '测试中...'; + try { + const res = await fetch('/api/setup/test', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + host: hostInput.value, + port: portInput.value, + user: userInput.value, + password: passwordInput.value + }) + }); + const data = await res.json(); + if (data.success) { + showMessage('连接成功!可以进行初始化。'); + } else { + showMessage('连接失败: ' + (data.error || '未知错误'), true); + } + } catch (err) { + showMessage('请求失败: ' + err.message, true); + } finally { + btnTest.disabled = false; + btnTest.textContent = oldText; + } + }); + + btnInit.addEventListener('click', async () => { + btnInit.disabled = true; + const oldText = btnInit.textContent; + btnInit.textContent = '初始化中...'; + try { + const res = await fetch('/api/setup/init', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + host: hostInput.value, + port: portInput.value, + user: userInput.value, + password: passwordInput.value, + database: databaseInput.value + }) + }); + const data = await res.json(); + if (data.success) { + showMessage('初始化成功!即将跳转到系统首页面...'); + setTimeout(() => { + window.location.href = '/'; + }, 1500); + } else { + showMessage('初始化失败: ' + (data.error || '未知错误'), true); + btnInit.disabled = false; + btnInit.textContent = oldText; + } + } catch (err) { + showMessage('请求失败: ' + err.message, true); + btnInit.disabled = false; + btnInit.textContent = oldText; + } + }); +}); diff --git a/server/db.js b/server/db.js index ba71496..1c49bd4 100644 --- a/server/db.js +++ b/server/db.js @@ -1,14 +1,26 @@ const mysql = require('mysql2/promise'); -const pool = mysql.createPool({ - host: process.env.MYSQL_HOST || 'localhost', - port: parseInt(process.env.MYSQL_PORT) || 3306, - user: process.env.MYSQL_USER || 'root', - password: process.env.MYSQL_PASSWORD || '', - database: process.env.MYSQL_DATABASE || 'display_wall', - waitForConnections: true, - connectionLimit: 10, - queueLimit: 0 -}); +let pool; -module.exports = pool; +function initPool() { + if (pool) { + pool.end().catch(e => console.error('Error closing pool:', e)); + } + pool = mysql.createPool({ + host: process.env.MYSQL_HOST || 'localhost', + port: parseInt(process.env.MYSQL_PORT) || 3306, + user: process.env.MYSQL_USER || 'root', + password: process.env.MYSQL_PASSWORD || '', + database: process.env.MYSQL_DATABASE || 'display_wall', + waitForConnections: true, + connectionLimit: 10, + queueLimit: 0 + }); +} + +initPool(); + +module.exports = { + query: (...args) => pool.query(...args), + initPool +}; diff --git a/server/index.js b/server/index.js index 6b991bc..d64964d 100644 --- a/server/index.js +++ b/server/index.js @@ -10,6 +10,122 @@ const PORT = process.env.PORT || 3000; app.use(cors()); app.use(express.json()); +const fs = require('fs'); + +let isDbInitialized = false; + +async function checkDb() { + try { + const [rows] = await db.query("SHOW TABLES LIKE 'prometheus_sources'"); + if (rows.length > 0) { + isDbInitialized = true; + } else { + isDbInitialized = false; + } + } catch (err) { + isDbInitialized = false; + } +} + +checkDb(); + +// Setup API Routes +app.post('/api/setup/test', async (req, res) => { + const { host, port, user, password } = req.body; + try { + const mysql = require('mysql2/promise'); + const connection = await mysql.createConnection({ + host: host || 'localhost', + port: parseInt(port) || 3306, + user: user || 'root', + password: password || '' + }); + await connection.ping(); + await connection.end(); + res.json({ success: true, message: 'Connection successful' }); + } catch (err) { + res.status(400).json({ success: false, error: err.message }); + } +}); + +app.post('/api/setup/init', async (req, res) => { + const { host, port, user, password, database } = req.body; + try { + const mysql = require('mysql2/promise'); + const connection = await mysql.createConnection({ + host: host || 'localhost', + port: parseInt(port) || 3306, + user: user || 'root', + password: password || '' + }); + + const dbName = database || 'display_wall'; + + // Create database + await connection.query(`CREATE DATABASE IF NOT EXISTS \`${dbName}\` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci`); + await connection.query(`USE \`${dbName}\``); + + // Create tables + await connection.query(` + CREATE TABLE IF NOT EXISTS prometheus_sources ( + id INT AUTO_INCREMENT PRIMARY KEY, + name VARCHAR(255) NOT NULL, + url VARCHAR(500) NOT NULL, + description TEXT DEFAULT '', + 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 + `); + + await connection.end(); + + // Save to .env + const envContent = `MYSQL_HOST=${host || 'localhost'} +MYSQL_PORT=${port || '3306'} +MYSQL_USER=${user || 'root'} +MYSQL_PASSWORD=${password || ''} +MYSQL_DATABASE=${dbName} +PORT=${process.env.PORT || 3000} +REFRESH_INTERVAL=${process.env.REFRESH_INTERVAL || 5000} +`; + fs.writeFileSync(path.join(__dirname, '..', '.env'), envContent); + + // Update process.env + process.env.MYSQL_HOST = host; + process.env.MYSQL_PORT = port; + process.env.MYSQL_USER = user; + process.env.MYSQL_PASSWORD = password; + process.env.MYSQL_DATABASE = dbName; + + // Re-initialize pool + db.initPool(); + + isDbInitialized = true; + res.json({ success: true, message: 'Initialization complete' }); + } catch (err) { + console.error('Initialization error:', err); + res.status(500).json({ success: false, error: err.message }); + } +}); + +// Middleware to protect routes +app.use((req, res, next) => { + if (!isDbInitialized) { + if (req.path.startsWith('/api/setup') || req.path === '/init.html' || req.path.startsWith('/css/') || req.path.startsWith('/js/') || req.path.startsWith('/fonts/')) { + return next(); + } + if (req.path.startsWith('/api/')) { + return res.status(503).json({ error: 'Database not initialized', needSetup: true }); + } + return res.redirect('/init.html'); + } + + if (req.path === '/init.html') { + return res.redirect('/'); + } + next(); +}); + app.use(express.static(path.join(__dirname, '..', 'public'))); // ==================== Prometheus Source CRUD ====================