d263c7bf48
- GameGlobal.js: keep upstream SERVER_URL with /ws suffix - en.js/zh.js: merge both settings.nickname and settings.profile keys - SettingsScene.js: keep both nickname row and profile button - server/index.js: merge express app + content security proxy with noServer WebSocket mode and path validation - Add .gitignore for node_modules and .codebuddy
141 lines
5.0 KiB
JavaScript
141 lines
5.0 KiB
JavaScript
/**
|
|
* Content Security Microservice - Standalone Entry Point
|
|
*
|
|
* Independent content security service for UGC moderation.
|
|
* Shared by multiple mini-games via game_id tenant isolation.
|
|
*
|
|
* API Endpoints:
|
|
* POST /api/content/check-text - Text content security check
|
|
* POST /api/content/check-image - Image content security check
|
|
* GET /api/content/sensitive-words - Get sensitive word list
|
|
* GET /api/user/mute-status - Check if a user is muted
|
|
* GET /api/user/violation-summary - Get violation summary
|
|
* POST /api/content/report - Submit a content report
|
|
* GET /api/health - Health check
|
|
* GET /api/metrics - Service metrics
|
|
*/
|
|
|
|
const express = require('express');
|
|
const http = require('http');
|
|
const WechatTokenManager = require('./services/wechatTokenManager');
|
|
const AuditLogger = require('./services/auditLogger');
|
|
const { createContentSecurityRouter } = require('./services/contentSecurityRoutes');
|
|
const ViolationService = require('./services/violationService');
|
|
const ReportService = require('./services/reportService');
|
|
|
|
// ============================================================
|
|
// Configuration
|
|
// ============================================================
|
|
const PORT = process.env.PORT || 3000;
|
|
const HOST = process.env.HOST || '0.0.0.0';
|
|
const DEFAULT_GAME_ID = process.env.DEFAULT_GAME_ID || 'tankwar';
|
|
|
|
// ============================================================
|
|
// Express App
|
|
// ============================================================
|
|
const app = express();
|
|
|
|
// Parse JSON bodies
|
|
app.use(express.json({ limit: '2mb' }));
|
|
|
|
// Request logging middleware
|
|
app.use((req, res, next) => {
|
|
const start = Date.now();
|
|
res.on('finish', () => {
|
|
const duration = Date.now() - start;
|
|
const game_id = req.headers['x-game-id'] || req.body?.game_id || DEFAULT_GAME_ID;
|
|
console.log(`[HTTP] ${req.method} ${req.url} ${res.statusCode} ${duration}ms game_id=${game_id}`);
|
|
});
|
|
next();
|
|
});
|
|
|
|
// ============================================================
|
|
// Health Check
|
|
// ============================================================
|
|
app.get('/api/health', (req, res) => {
|
|
res.json({
|
|
status: 'ok',
|
|
service: 'content-security',
|
|
version: '1.0.0',
|
|
timestamp: Date.now(),
|
|
tokenManagerAvailable: tokenManager ? tokenManager.isAvailable() : false,
|
|
});
|
|
});
|
|
|
|
// ============================================================
|
|
// Metrics Endpoint
|
|
// ============================================================
|
|
app.get('/api/metrics', (req, res) => {
|
|
res.json({
|
|
uptime: process.uptime(),
|
|
memory: process.memoryUsage(),
|
|
tokenManagerAvailable: tokenManager ? tokenManager.isAvailable() : false,
|
|
activeViolations: violationService ? violationService._records.size : 0,
|
|
activeMutes: violationService ? violationService._mutes.size : 0,
|
|
activeReports: reportService ? reportService._reports.size : 0,
|
|
timestamp: Date.now(),
|
|
});
|
|
});
|
|
|
|
// ============================================================
|
|
// Initialize Services
|
|
// ============================================================
|
|
const auditLogger = new AuditLogger();
|
|
const tokenManager = new WechatTokenManager();
|
|
const violationService = new ViolationService({ logger: auditLogger });
|
|
const reportService = new ReportService({ logger: auditLogger, violationService });
|
|
|
|
// Mount content security routes
|
|
const contentSecurityRouter = createContentSecurityRouter({
|
|
tokenManager,
|
|
logger: auditLogger,
|
|
violationService,
|
|
reportService,
|
|
});
|
|
app.use('/api/content', contentSecurityRouter);
|
|
|
|
// ============================================================
|
|
// Start Server
|
|
// ============================================================
|
|
const server = http.createServer(app);
|
|
|
|
server.listen(PORT, HOST, async () => {
|
|
console.log(`[Content Security Service] Running on ${HOST}:${PORT}`);
|
|
console.log(`[Content Security Service] Default game_id: ${DEFAULT_GAME_ID}`);
|
|
console.log(`[Content Security Service] API URL: http://${HOST}:${PORT}`);
|
|
|
|
// Initialize token manager
|
|
const success = await tokenManager.init();
|
|
if (success) {
|
|
console.log('[Content Security Service] WeChat token manager initialized successfully');
|
|
} else {
|
|
console.warn('[Content Security Service] WeChat token manager unavailable (token fetch failed)');
|
|
console.warn('[Content Security Service] Content checks will be rejected until token is obtained');
|
|
}
|
|
});
|
|
|
|
// Graceful shutdown
|
|
process.on('SIGTERM', () => {
|
|
console.log('[Content Security Service] SIGTERM received, shutting down gracefully...');
|
|
tokenManager.destroy();
|
|
auditLogger.destroy();
|
|
violationService.destroy();
|
|
server.close(() => {
|
|
console.log('[Content Security Service] Server closed');
|
|
process.exit(0);
|
|
});
|
|
});
|
|
|
|
process.on('SIGINT', () => {
|
|
console.log('[Content Security Service] SIGINT received, shutting down gracefully...');
|
|
tokenManager.destroy();
|
|
auditLogger.destroy();
|
|
violationService.destroy();
|
|
server.close(() => {
|
|
console.log('[Content Security Service] Server closed');
|
|
process.exit(0);
|
|
});
|
|
});
|
|
|
|
module.exports = { app, server };
|