feat: 添加修改密码功能
This commit is contained in:
@@ -4,54 +4,37 @@ const multer = require('multer');
|
||||
const initSqlJs = require('sql.js');
|
||||
const { v4: uuidv4 } = require('uuid');
|
||||
const fs = require('fs');
|
||||
const WebSocket = require('ws');
|
||||
|
||||
const app = express();
|
||||
const PORT = 3000;
|
||||
const ADMIN_PORT = 16888;
|
||||
const FRONT_PORT = 80;
|
||||
|
||||
let db;
|
||||
|
||||
async function initDB() {
|
||||
const SQL = await initSqlJs();
|
||||
const dbPath = path.join(__dirname, 'data', 'sqlite.db');
|
||||
|
||||
if (fs.existsSync(dbPath)) {
|
||||
const fileBuffer = fs.readFileSync(dbPath);
|
||||
db = new SQL.Database(fileBuffer);
|
||||
db = new SQL.Database(fs.readFileSync(dbPath));
|
||||
} else {
|
||||
db = new SQL.Database();
|
||||
}
|
||||
db.run(fs.readFileSync(path.join(__dirname, 'database', 'schema.sql'), 'utf8'));
|
||||
|
||||
const defaults = {
|
||||
'feature_enabled': 'false',
|
||||
'current_group_index': '0',
|
||||
'current_group_start_time': '0'
|
||||
};
|
||||
for (const [k, v] of Object.entries(defaults)) {
|
||||
const r = runQuery("SELECT value FROM config WHERE key=?", [k]);
|
||||
if (r.length === 0) db.run("INSERT INTO config (key,value) VALUES (?,?)", [k, v]);
|
||||
}
|
||||
|
||||
const schema = `
|
||||
CREATE TABLE IF NOT EXISTS groups (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
name TEXT NOT NULL,
|
||||
display_seconds INTEGER NOT NULL DEFAULT 30,
|
||||
enabled INTEGER NOT NULL DEFAULT 1,
|
||||
update_time INTEGER NOT NULL DEFAULT 0,
|
||||
created_at TEXT DEFAULT (datetime('now'))
|
||||
);
|
||||
CREATE TABLE IF NOT EXISTS images (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
group_id INTEGER NOT NULL,
|
||||
filename TEXT NOT NULL,
|
||||
original_name TEXT,
|
||||
size INTEGER DEFAULT 0,
|
||||
width INTEGER DEFAULT 280,
|
||||
height INTEGER DEFAULT 280,
|
||||
enabled INTEGER NOT NULL DEFAULT 1,
|
||||
created_at TEXT DEFAULT (datetime('now'))
|
||||
);
|
||||
CREATE TABLE IF NOT EXISTS config (
|
||||
key TEXT PRIMARY KEY,
|
||||
value TEXT
|
||||
);
|
||||
`;
|
||||
db.run(schema);
|
||||
|
||||
// 确保有 config 行
|
||||
const configCheck = db.exec("SELECT COUNT(*) as cnt FROM config WHERE key='feature_enabled'");
|
||||
if (configCheck[0].values[0][0] === 0) {
|
||||
db.run("INSERT INTO config (key, value) VALUES ('feature_enabled', 'false')");
|
||||
// 确保有默认管理员账号
|
||||
const userCheck = runQuery("SELECT COUNT(*) as cnt FROM users WHERE username=?", ['admin']);
|
||||
if (userCheck.length === 0 || userCheck[0].cnt === 0) {
|
||||
db.run("INSERT INTO users (username, password) VALUES (?,?)", ['admin', 'admin123']);
|
||||
}
|
||||
|
||||
saveDB();
|
||||
@@ -59,232 +42,322 @@ async function initDB() {
|
||||
|
||||
function saveDB() {
|
||||
const data = db.export();
|
||||
const buffer = Buffer.from(data);
|
||||
fs.writeFileSync(path.join(__dirname, 'data', 'sqlite.db'), buffer);
|
||||
fs.writeFileSync(path.join(__dirname, 'data', 'sqlite.db'), Buffer.from(data));
|
||||
}
|
||||
|
||||
function runQuery(sql, params) {
|
||||
const stmt = db.prepare(sql);
|
||||
if (params && params.length > 0) {
|
||||
stmt.bind(params);
|
||||
}
|
||||
if (params && params.length > 0) stmt.bind(params);
|
||||
const results = [];
|
||||
while (stmt.step()) {
|
||||
results.push(stmt.getAsObject());
|
||||
}
|
||||
while (stmt.step()) results.push(stmt.getAsObject());
|
||||
stmt.free();
|
||||
return results;
|
||||
}
|
||||
|
||||
function runExec(sql, params) {
|
||||
db.run(sql, params || []);
|
||||
}
|
||||
|
||||
function getConfig(key) {
|
||||
const r = runQuery("SELECT value FROM config WHERE key=?", [key]);
|
||||
return r.length > 0 ? r[0].value : null;
|
||||
}
|
||||
|
||||
function setConfig(key, value) {
|
||||
runExec("INSERT OR REPLACE INTO config (key,value) VALUES (?,?)", [key, String(value)]);
|
||||
saveDB();
|
||||
}
|
||||
|
||||
// Multer配置
|
||||
const storage = multer.diskStorage({
|
||||
destination: function (req, file, cb) {
|
||||
cb(null, path.join(__dirname, 'uploads'));
|
||||
},
|
||||
filename: function (req, file, cb) {
|
||||
const ext = path.extname(file.originalname);
|
||||
cb(null, uuidv4() + ext);
|
||||
function getEnabledGroups() {
|
||||
return runQuery(`
|
||||
SELECT g.*, (SELECT COUNT(*) FROM images WHERE group_id=g.id AND enabled=1) as img_count
|
||||
FROM groups g WHERE g.enabled=1 ORDER BY g.id ASC
|
||||
`).filter(g => g.img_count > 0);
|
||||
}
|
||||
|
||||
// ========== 轮播逻辑 ==========
|
||||
let wsClients = new Set();
|
||||
|
||||
function broadcast(data) {
|
||||
const msg = JSON.stringify(data);
|
||||
wsClients.forEach(ws => {
|
||||
if (ws.readyState === WebSocket.OPEN) ws.send(msg);
|
||||
});
|
||||
}
|
||||
|
||||
function getCurrentState() {
|
||||
const featureEnabled = getConfig('feature_enabled') === 'true';
|
||||
if (!featureEnabled) return { type: 'state', playing: false, groups: [] };
|
||||
|
||||
const groups = getEnabledGroups();
|
||||
if (groups.length === 0) return { type: 'state', playing: false, groups: [] };
|
||||
|
||||
const idx = parseInt(getConfig('current_group_index') || '0') % groups.length;
|
||||
const startTime = parseInt(getConfig('current_group_start_time') || '0');
|
||||
const now = Math.floor(Date.now() / 1000);
|
||||
const currentGroup = groups[idx];
|
||||
|
||||
let remaining = 0;
|
||||
if (currentGroup.display_seconds === 0) {
|
||||
remaining = -1;
|
||||
} else if (startTime > 0) {
|
||||
remaining = Math.max(0, startTime + currentGroup.display_seconds - now);
|
||||
}
|
||||
|
||||
const images = runQuery('SELECT * FROM images WHERE group_id=? AND enabled=1', [currentGroup.id]);
|
||||
|
||||
return {
|
||||
type: 'state',
|
||||
playing: true,
|
||||
current_index: idx,
|
||||
total: groups.length,
|
||||
group_name: currentGroup.name,
|
||||
group_id: currentGroup.id,
|
||||
display_seconds: currentGroup.display_seconds,
|
||||
remaining: remaining,
|
||||
images: images
|
||||
};
|
||||
}
|
||||
|
||||
function broadcastState() {
|
||||
broadcast(getCurrentState());
|
||||
}
|
||||
|
||||
function startCarouselTimer() {
|
||||
setInterval(() => {
|
||||
if (getConfig('feature_enabled') !== 'true') return;
|
||||
|
||||
const groups = getEnabledGroups();
|
||||
if (groups.length === 0) return;
|
||||
|
||||
const idx = parseInt(getConfig('current_group_index') || '0') % groups.length;
|
||||
const startTime = parseInt(getConfig('current_group_start_time') || '0');
|
||||
const currentGroup = groups[idx];
|
||||
|
||||
if (currentGroup.display_seconds === 0) {
|
||||
broadcastState();
|
||||
return;
|
||||
}
|
||||
|
||||
if (startTime === 0) {
|
||||
setConfig('current_group_start_time', Math.floor(Date.now() / 1000));
|
||||
broadcastState();
|
||||
return;
|
||||
}
|
||||
|
||||
const now = Math.floor(Date.now() / 1000);
|
||||
const elapsed = now - startTime;
|
||||
|
||||
if (elapsed >= currentGroup.display_seconds) {
|
||||
const nextIdx = findNextGroupIndex(groups, idx);
|
||||
setConfig('current_group_index', nextIdx);
|
||||
setConfig('current_group_start_time', Math.floor(Date.now() / 1000));
|
||||
}
|
||||
broadcastState();
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
function findNextGroupIndex(groups, currentIdx) {
|
||||
for (let i = 1; i <= groups.length; i++) {
|
||||
const nextIdx = (currentIdx + i) % groups.length;
|
||||
if (groups[nextIdx].display_seconds > 0) return nextIdx;
|
||||
}
|
||||
return currentIdx;
|
||||
}
|
||||
|
||||
function resetCarousel() {
|
||||
setConfig('current_group_index', 0);
|
||||
setConfig('current_group_start_time', Math.floor(Date.now() / 1000));
|
||||
broadcastState();
|
||||
}
|
||||
|
||||
// ========== Multer ==========
|
||||
const storage = multer.diskStorage({
|
||||
destination: (req, file, cb) => cb(null, path.join(__dirname, 'uploads')),
|
||||
filename: (req, file, cb) => cb(null, uuidv4() + path.extname(file.originalname))
|
||||
});
|
||||
const upload = multer({ storage: storage });
|
||||
const upload = multer({ storage });
|
||||
|
||||
// 中间件
|
||||
app.use(express.json());
|
||||
app.use(express.urlencoded({ extended: true }));
|
||||
app.use(express.static(path.join(__dirname, 'public')));
|
||||
app.use('/uploads', express.static(path.join(__dirname, 'uploads')));
|
||||
// ========== 前台服务 (端口 80) ==========
|
||||
const frontApp = express();
|
||||
frontApp.use(express.static(path.join(__dirname, 'public')));
|
||||
frontApp.use('/uploads', express.static(path.join(__dirname, 'uploads')));
|
||||
|
||||
frontApp.get('/', (req, res) => {
|
||||
res.sendFile(path.join(__dirname, 'public', 'index.html'));
|
||||
});
|
||||
|
||||
const frontServer = frontApp.listen(FRONT_PORT, '0.0.0.0', () => {
|
||||
console.log(`前台运行在 http://0.0.0.0:${FRONT_PORT}`);
|
||||
});
|
||||
|
||||
const wss = new WebSocket.Server({ server: frontServer });
|
||||
wss.on('connection', (ws) => {
|
||||
wsClients.add(ws);
|
||||
ws.send(JSON.stringify(getCurrentState()));
|
||||
ws.on('close', () => wsClients.delete(ws));
|
||||
});
|
||||
|
||||
// ========== 后台服务 (端口 18888) ==========
|
||||
const adminApp = express();
|
||||
adminApp.use(express.json());
|
||||
adminApp.use(express.urlencoded({ extended: true }));
|
||||
adminApp.use(express.static(path.join(__dirname, 'public')));
|
||||
adminApp.use('/uploads', express.static(path.join(__dirname, 'uploads')));
|
||||
|
||||
// 认证中间件
|
||||
const authMiddleware = (req, res, next) => {
|
||||
const token = req.headers.authorization?.split(' ')[1];
|
||||
if (!token || token !== 'admin-token') {
|
||||
return res.status(401).json({ error: '未授权' });
|
||||
}
|
||||
if (!token || token !== 'admin-token') return res.status(401).json({ error: '未授权' });
|
||||
next();
|
||||
};
|
||||
|
||||
// 认证API
|
||||
app.post('/api/login', (req, res) => {
|
||||
// 登录
|
||||
adminApp.post('/api/login', (req, res) => {
|
||||
const { username, password } = req.body;
|
||||
if (username === 'admin' && password === 'admin123') {
|
||||
const users = runQuery('SELECT * FROM users WHERE username = ? AND password = ?', [username, password]);
|
||||
if (users.length > 0) {
|
||||
res.json({ success: true, token: 'admin-token' });
|
||||
} else {
|
||||
res.status(401).json({ success: false, error: '用户名或密码错误' });
|
||||
}
|
||||
});
|
||||
|
||||
// 分组管理API
|
||||
app.get('/api/groups', (req, res) => {
|
||||
const groups = runQuery('SELECT * FROM groups ORDER BY id ASC');
|
||||
res.json(groups);
|
||||
// 修改密码
|
||||
adminApp.put('/api/change-password', authMiddleware, (req, res) => {
|
||||
const { oldPassword, newPassword } = req.body;
|
||||
if (!oldPassword || !newPassword) {
|
||||
return res.status(400).json({ error: '请输入旧密码和新密码' });
|
||||
}
|
||||
const users = runQuery('SELECT * FROM users WHERE username = ? AND password = ?', ['admin', oldPassword]);
|
||||
if (users.length === 0) {
|
||||
return res.status(400).json({ error: '旧密码错误' });
|
||||
}
|
||||
runExec('UPDATE users SET password = ? WHERE username = ?', [newPassword, 'admin']);
|
||||
saveDB();
|
||||
res.json({ success: true });
|
||||
});
|
||||
|
||||
app.post('/api/groups', authMiddleware, (req, res) => {
|
||||
// 分组
|
||||
adminApp.get('/api/groups', (req, res) => {
|
||||
res.json(runQuery('SELECT * FROM groups ORDER BY id ASC'));
|
||||
});
|
||||
|
||||
adminApp.post('/api/groups', authMiddleware, (req, res) => {
|
||||
const { name, display_seconds } = req.body;
|
||||
runExec('INSERT INTO groups (name, display_seconds, update_time) VALUES (?, ?, 0)',
|
||||
[name || '新分组', display_seconds || 30]);
|
||||
const result = db.exec("SELECT last_insert_rowid() as id");
|
||||
const id = result[0].values[0][0];
|
||||
runExec('INSERT INTO groups (name, display_seconds) VALUES (?, ?)', [name || '新分组', display_seconds || 30]);
|
||||
saveDB();
|
||||
const id = db.exec("SELECT last_insert_rowid()")[0].values[0][0];
|
||||
broadcastState();
|
||||
res.json({ id });
|
||||
});
|
||||
|
||||
app.put('/api/groups/:id', authMiddleware, (req, res) => {
|
||||
adminApp.put('/api/groups/:id', authMiddleware, (req, res) => {
|
||||
const { name, display_seconds, enabled } = req.body;
|
||||
const id = parseInt(req.params.id);
|
||||
runExec('UPDATE groups SET name = ?, display_seconds = ?, enabled = ? WHERE id = ?',
|
||||
[name, display_seconds, enabled, id]);
|
||||
runExec('UPDATE groups SET name=?, display_seconds=?, enabled=? WHERE id=?',
|
||||
[name, display_seconds, enabled, parseInt(req.params.id)]);
|
||||
saveDB();
|
||||
broadcastState();
|
||||
res.json({ success: true });
|
||||
});
|
||||
|
||||
app.put('/api/groups/:id/settime', authMiddleware, (req, res) => {
|
||||
adminApp.put('/api/groups/:id/settime', authMiddleware, (req, res) => {
|
||||
const { display_seconds } = req.body;
|
||||
const id = parseInt(req.params.id);
|
||||
const now = Math.floor(Date.now() / 1000);
|
||||
runExec('UPDATE groups SET display_seconds = ?, update_time = ? WHERE id = ?',
|
||||
[display_seconds, now, id]);
|
||||
runExec('UPDATE groups SET display_seconds=? WHERE id=?', [display_seconds, parseInt(req.params.id)]);
|
||||
saveDB();
|
||||
broadcastState();
|
||||
res.json({ success: true });
|
||||
});
|
||||
|
||||
app.delete('/api/groups/:id', authMiddleware, (req, res) => {
|
||||
adminApp.delete('/api/groups/:id', authMiddleware, (req, res) => {
|
||||
const id = parseInt(req.params.id);
|
||||
// 先删除分组下的图片文件
|
||||
const images = runQuery('SELECT filename FROM images WHERE group_id = ?', [id]);
|
||||
images.forEach(img => {
|
||||
const filePath = path.join(__dirname, 'uploads', img.filename);
|
||||
if (fs.existsSync(filePath)) fs.unlinkSync(filePath);
|
||||
const imgs = runQuery('SELECT filename FROM images WHERE group_id=?', [id]);
|
||||
imgs.forEach(img => {
|
||||
const fp = path.join(__dirname, 'uploads', img.filename);
|
||||
if (fs.existsSync(fp)) fs.unlinkSync(fp);
|
||||
});
|
||||
runExec('DELETE FROM images WHERE group_id = ?', [id]);
|
||||
runExec('DELETE FROM groups WHERE id = ?', [id]);
|
||||
runExec('DELETE FROM images WHERE group_id=?', [id]);
|
||||
runExec('DELETE FROM groups WHERE id=?', [id]);
|
||||
saveDB();
|
||||
resetCarousel();
|
||||
res.json({ success: true });
|
||||
});
|
||||
|
||||
// 图片上传API
|
||||
app.post('/api/upload', authMiddleware, upload.single('image'), (req, res) => {
|
||||
if (!req.file) {
|
||||
return res.status(400).json({ error: '没有上传文件' });
|
||||
}
|
||||
const group_id = parseInt(req.body.group_id);
|
||||
if (!group_id) {
|
||||
return res.status(400).json({ error: '缺少分组ID' });
|
||||
}
|
||||
const { filename, originalname, size } = req.file;
|
||||
|
||||
runExec('INSERT INTO images (group_id, filename, original_name, size) VALUES (?, ?, ?, ?)',
|
||||
[group_id, filename, originalname, size || 0]);
|
||||
const result = db.exec("SELECT last_insert_rowid() as id");
|
||||
const id = result[0].values[0][0];
|
||||
res.json({ id, filename });
|
||||
});
|
||||
|
||||
// 图片列表API
|
||||
app.get('/api/images', (req, res) => {
|
||||
const group_id = parseInt(req.query.group_id);
|
||||
let images;
|
||||
if (group_id) {
|
||||
images = runQuery('SELECT * FROM images WHERE group_id = ? ORDER BY id DESC', [group_id]);
|
||||
// 图片
|
||||
adminApp.get('/api/images', (req, res) => {
|
||||
const gid = parseInt(req.query.group_id);
|
||||
if (gid) {
|
||||
res.json(runQuery('SELECT * FROM images WHERE group_id=? ORDER BY id DESC', [gid]));
|
||||
} else {
|
||||
images = runQuery('SELECT * FROM images ORDER BY id DESC');
|
||||
res.json(runQuery('SELECT * FROM images ORDER BY id DESC'));
|
||||
}
|
||||
res.json(images);
|
||||
});
|
||||
|
||||
// 图片启用/禁用API
|
||||
app.put('/api/images/:id/enable', authMiddleware, (req, res) => {
|
||||
const id = parseInt(req.params.id);
|
||||
runExec('UPDATE images SET enabled = 1 WHERE id = ?', [id]);
|
||||
res.json({ success: true });
|
||||
adminApp.post('/api/upload', authMiddleware, upload.single('image'), (req, res) => {
|
||||
if (!req.file) return res.status(400).json({ error: '没有上传文件' });
|
||||
const group_id = parseInt(req.body.group_id);
|
||||
if (!group_id) return res.status(400).json({ error: '缺少分组ID' });
|
||||
runExec('INSERT INTO images (group_id, filename, original_name, size) VALUES (?,?,?,?)',
|
||||
[group_id, req.file.filename, req.file.originalname, req.file.size || 0]);
|
||||
saveDB();
|
||||
const id = db.exec("SELECT last_insert_rowid()")[0].values[0][0];
|
||||
broadcastState();
|
||||
res.json({ id, filename: req.file.filename });
|
||||
});
|
||||
|
||||
app.put('/api/images/:id/disable', authMiddleware, (req, res) => {
|
||||
const id = parseInt(req.params.id);
|
||||
runExec('UPDATE images SET enabled = 0 WHERE id = ?', [id]);
|
||||
res.json({ success: true });
|
||||
adminApp.put('/api/images/:id/enable', authMiddleware, (req, res) => {
|
||||
runExec('UPDATE images SET enabled=1 WHERE id=?', [parseInt(req.params.id)]);
|
||||
saveDB(); broadcastState(); res.json({ success: true });
|
||||
});
|
||||
|
||||
// 图片删除API
|
||||
app.delete('/api/images/:id', authMiddleware, (req, res) => {
|
||||
adminApp.put('/api/images/:id/disable', authMiddleware, (req, res) => {
|
||||
runExec('UPDATE images SET enabled=0 WHERE id=?', [parseInt(req.params.id)]);
|
||||
saveDB(); broadcastState(); res.json({ success: true });
|
||||
});
|
||||
|
||||
adminApp.delete('/api/images/:id', authMiddleware, (req, res) => {
|
||||
const id = parseInt(req.params.id);
|
||||
const images = runQuery('SELECT filename FROM images WHERE id = ?', [id]);
|
||||
if (images.length > 0) {
|
||||
const filePath = path.join(__dirname, 'uploads', images[0].filename);
|
||||
if (fs.existsSync(filePath)) fs.unlinkSync(filePath);
|
||||
const imgs = runQuery('SELECT filename FROM images WHERE id=?', [id]);
|
||||
if (imgs.length > 0) {
|
||||
const fp = path.join(__dirname, 'uploads', imgs[0].filename);
|
||||
if (fs.existsSync(fp)) fs.unlinkSync(fp);
|
||||
}
|
||||
runExec('DELETE FROM images WHERE id = ?', [id]);
|
||||
res.json({ success: true });
|
||||
runExec('DELETE FROM images WHERE id=?', [id]);
|
||||
saveDB(); broadcastState(); res.json({ success: true });
|
||||
});
|
||||
|
||||
// 配置API
|
||||
app.get('/api/config', (req, res) => {
|
||||
// 配置
|
||||
adminApp.get('/api/config', (req, res) => {
|
||||
const rows = runQuery('SELECT * FROM config');
|
||||
const config = {};
|
||||
rows.forEach(row => { config[row.key] = row.value; });
|
||||
rows.forEach(r => config[r.key] = r.value);
|
||||
res.json(config);
|
||||
});
|
||||
|
||||
app.put('/api/config', authMiddleware, (req, res) => {
|
||||
const { key, value } = req.body;
|
||||
runExec('INSERT OR REPLACE INTO config (key, value) VALUES (?, ?)', [key, value]);
|
||||
adminApp.put('/api/config', authMiddleware, (req, res) => {
|
||||
setConfig(req.body.key, req.body.value);
|
||||
broadcastState();
|
||||
res.json({ success: true });
|
||||
});
|
||||
|
||||
// 首页展示API
|
||||
app.get('/api/display', (req, res) => {
|
||||
const configRows = runQuery("SELECT value FROM config WHERE key = 'feature_enabled'");
|
||||
const featureEnabled = configRows.length > 0 && configRows[0].value === 'true';
|
||||
|
||||
if (!featureEnabled) {
|
||||
return res.json({ enabled: false, groups: [] });
|
||||
}
|
||||
|
||||
const groups = runQuery('SELECT * FROM groups WHERE enabled = 1 ORDER BY id ASC');
|
||||
const now = Math.floor(Date.now() / 1000);
|
||||
|
||||
const result = groups.filter(group => {
|
||||
// 展示时间为0则一直展示
|
||||
if (group.display_seconds === 0) return true;
|
||||
// update_time为0则不展示(未设置过时间)
|
||||
if (group.update_time === 0) return false;
|
||||
// 计算剩余时间
|
||||
const remaining = group.update_time + group.display_seconds - now;
|
||||
return remaining > 0;
|
||||
}).map(group => {
|
||||
const images = runQuery('SELECT * FROM images WHERE group_id = ? AND enabled = 1', [group.id]);
|
||||
const remaining = group.update_time === 0 ? 0 :
|
||||
Math.max(0, group.update_time + group.display_seconds - now);
|
||||
return {
|
||||
...group,
|
||||
remaining,
|
||||
images
|
||||
};
|
||||
}).filter(group => group.images.length > 0);
|
||||
|
||||
res.json({ enabled: true, groups: result });
|
||||
// 重置轮播
|
||||
adminApp.post('/api/carousel/reset', authMiddleware, (req, res) => {
|
||||
resetCarousel();
|
||||
res.json({ success: true });
|
||||
});
|
||||
|
||||
// 静态文件路由
|
||||
app.get('/', (req, res) => {
|
||||
res.sendFile(path.join(__dirname, 'public', 'index.html'));
|
||||
// 路由 - 后台直接在根路径,未登录跳转登录页
|
||||
adminApp.get('/login', (req, res) => res.sendFile(path.join(__dirname, 'public', 'login.html')));
|
||||
adminApp.get('/', (req, res) => res.sendFile(path.join(__dirname, 'public', 'admin.html')));
|
||||
|
||||
adminApp.listen(ADMIN_PORT, '0.0.0.0', () => {
|
||||
console.log(`后台运行在 http://0.0.0.0:${ADMIN_PORT}`);
|
||||
});
|
||||
|
||||
app.get('/login', (req, res) => {
|
||||
res.sendFile(path.join(__dirname, 'public', 'login.html'));
|
||||
});
|
||||
|
||||
app.get('/admin', (req, res) => {
|
||||
res.sendFile(path.join(__dirname, 'public', 'admin.html'));
|
||||
});
|
||||
|
||||
// 启动服务器
|
||||
// ========== 启动 ==========
|
||||
initDB().then(() => {
|
||||
app.listen(PORT, '0.0.0.0', () => {
|
||||
console.log(`服务器运行在 http://localhost:${PORT}`);
|
||||
});
|
||||
startCarouselTimer();
|
||||
console.log('数据库初始化完成');
|
||||
}).catch(err => {
|
||||
console.error('数据库初始化失败:', err);
|
||||
console.error('启动失败:', err);
|
||||
process.exit(1);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user