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 @@
+
+
+
+
+
+ 系统初始化 - 数据可视化展示大屏
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
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 ====================