初始提交
This commit is contained in:
+128
@@ -0,0 +1,128 @@
|
||||
const express = require('express');
|
||||
const http = require('http');
|
||||
const path = require('path');
|
||||
const cors = require('cors');
|
||||
const ioModule = require('./io');
|
||||
const DataService = require('./services/data');
|
||||
const { jsonStringifyUnicode } = require('./services/unicode-json');
|
||||
|
||||
const app = express();
|
||||
const server = http.createServer(app);
|
||||
|
||||
// Initialize Socket.IO
|
||||
const io = ioModule.init(server);
|
||||
|
||||
app.use(express.json());
|
||||
app.use(cors());
|
||||
|
||||
app.use((req, res, next) => {
|
||||
const origJson = res.json.bind(res);
|
||||
res.json = (data) => {
|
||||
if (data === undefined) return origJson(data);
|
||||
const body = jsonStringifyUnicode(data);
|
||||
res.set('Content-Type', 'application/json; charset=utf-8');
|
||||
return res.send(body);
|
||||
};
|
||||
next();
|
||||
});
|
||||
|
||||
// Mount API Routes
|
||||
app.use('/api/public', require('./routes/public'));
|
||||
app.use('/api/files', require('./routes/files'));
|
||||
app.use('/api/assets', require('./routes/assets'));
|
||||
app.use('/api', require('./routes/api'));
|
||||
|
||||
// Serve Audio Files Statically
|
||||
const AUDIO_DIR = path.join(__dirname, '../Audio');
|
||||
app.use('/audio', express.static(AUDIO_DIR));
|
||||
|
||||
const ASSETS_DIR = path.join(__dirname, '../Assets');
|
||||
app.use('/assets', express.static(ASSETS_DIR));
|
||||
|
||||
// Serve Lua Scripts
|
||||
const ROOT_DIR = path.join(__dirname, '..');
|
||||
app.get('/scripts/:name', (req, res) => {
|
||||
const name = req.params.name;
|
||||
if (!name.match(/^[a-zA-Z0-9_]+\.lua$/)) return res.status(400).send('Invalid script name');
|
||||
const allowed = ['gate.lua', 'ticketmachine.lua', 'startup.lua', 'install_gate.lua', 'install_machine.lua', 'update_gate.lua', 'update_machine.lua', 'server.lua', 'installer.lua', 'installer_bi.lua'];
|
||||
if (!allowed.includes(name)) return res.status(404).send('Script not found');
|
||||
res.sendFile(path.join(ROOT_DIR, name));
|
||||
});
|
||||
|
||||
// Serve Static Files
|
||||
const WEB_DIR = path.join(__dirname, '../web');
|
||||
|
||||
// Path Routing for single domain
|
||||
app.get('/', (req, res, next) => {
|
||||
const host = req.hostname;
|
||||
console.log(`[Router] Accessing / with host: ${host}`);
|
||||
|
||||
if (host === 'ticket.fse-media.group' || !host.includes('fse-media.group')) {
|
||||
return res.sendFile(path.join(WEB_DIR, 'home.html'));
|
||||
}
|
||||
next();
|
||||
});
|
||||
|
||||
app.get('/order', (req, res) => res.sendFile(path.join(WEB_DIR, 'ticket-order.html')));
|
||||
app.get('/search', (req, res) => res.sendFile(path.join(WEB_DIR, 'ticket-search.html')));
|
||||
app.get('/ic-card/search', (req, res) => res.sendFile(path.join(WEB_DIR, 'ic-card-search.html')));
|
||||
app.get('/ic-card/order', (req, res) => res.sendFile(path.join(WEB_DIR, 'ic-card-order.html')));
|
||||
app.get('/ic/:card_id', (req, res) => res.sendFile(path.join(WEB_DIR, 'ic-card-detail.html')));
|
||||
app.get('/token', (req, res) => res.sendFile(path.join(WEB_DIR, 'token.html')));
|
||||
app.get('/admin', (req, res) => res.sendFile(path.join(WEB_DIR, 'index.html')));
|
||||
app.get('/admin/ic-card', (req, res) => res.redirect('/admin?view=iccards'));
|
||||
app.get('/route', (req, res) => res.sendFile(path.join(WEB_DIR, 'ticket-route.html')));
|
||||
|
||||
app.use('/', express.static(WEB_DIR));
|
||||
|
||||
// Fallback for static routes (ticket-search, ticket-order) - Legacy support
|
||||
app.get('/ticket-search/:id', (req, res) => { res.sendFile(path.join(WEB_DIR, 'ticket-search.html')); });
|
||||
app.get('/ticket-search', (req, res) => { res.sendFile(path.join(WEB_DIR, 'ticket-search.html')); });
|
||||
app.get('/ticket-order', (req, res) => { res.sendFile(path.join(WEB_DIR, 'ticket-order.html')); });
|
||||
app.get('/ic-card-admin', (req, res) => { res.redirect('/admin?view=iccards'); });
|
||||
app.get('/ic-card-search', (req, res) => { res.sendFile(path.join(WEB_DIR, 'ic-card-search.html')); });
|
||||
app.get('/ic-card-order', (req, res) => { res.sendFile(path.join(WEB_DIR, 'ic-card-order.html')); });
|
||||
|
||||
// Ticket Board
|
||||
// Handles ticket.fse-media.group/detail/TicketID
|
||||
app.get('/detail/:ticket_id', (req, res, next) => {
|
||||
const id = req.params.ticket_id;
|
||||
// If it's a known static file extension, let express.static handle it
|
||||
if (id.includes('.')) return next();
|
||||
|
||||
return res.sendFile(path.join(WEB_DIR, 'ticket-board.html'));
|
||||
});
|
||||
|
||||
// Start Server
|
||||
const PORT = process.env.PORT || 23333;
|
||||
const HOST = process.env.HOST || '0.0.0.0';
|
||||
|
||||
(async () => {
|
||||
await DataService.init();
|
||||
|
||||
// Start periodic export
|
||||
// setInterval(DataService.saveExport, 10000);
|
||||
|
||||
// Global Error Handler
|
||||
app.use((err, req, res, next) => {
|
||||
console.error(err.stack);
|
||||
try {
|
||||
DataService.appendLog({
|
||||
ts: new Date().toISOString(),
|
||||
ip: (req.headers['x-forwarded-for'] || '').toString().split(',')[0].trim() || req.ip || req.connection?.remoteAddress || '',
|
||||
ua: String(req.headers['user-agent'] || ''),
|
||||
method: req.method,
|
||||
path: req.originalUrl || req.path || '',
|
||||
category: 'system',
|
||||
level: 'error',
|
||||
type: 'unhandled_error',
|
||||
detail: { error: err?.message || String(err) }
|
||||
});
|
||||
} catch (e) {}
|
||||
res.status(500).json({ ok: false, error: 'Internal Server Error' });
|
||||
});
|
||||
|
||||
server.listen(PORT, HOST, () => {
|
||||
console.log(`ftc admin server running at http://${HOST}:${PORT}/`);
|
||||
});
|
||||
})();
|
||||
Reference in New Issue
Block a user