Files
mqsrv/src/index.ts
2026-05-10 15:46:17 +08:00

256 lines
7.0 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import express, { Request, Response } from 'express';
import cors from 'cors';
import helmet from 'helmet';
import rateLimit from 'express-rate-limit';
import dotenv from 'dotenv';
dotenv.config();
import authRoutes from './routes/auth';
import userRoutes from './routes/user';
import schemeRoutes from './routes/schemes';
import schemeAobRoutes from './routes/schemesAob';
import filterRoutes from './routes/filters';
import vipRoutes from './routes/vip';
import favoriteRoutes from './routes/favorites';
const app = express();
const PORT = process.env.PORT || 3001;
// ============================================
// 中间件
// ============================================
// 安全头 - 开发模式禁用 CSP
app.use(helmet({
contentSecurityPolicy: false,
crossOriginEmbedderPolicy: false,
}));
// CORS
app.use(cors({
origin: true, // 允许所有来源(开发模式)
credentials: true,
}));
// JSON 解析
app.use(express.json({ limit: '10mb' }));
app.use(express.urlencoded({ extended: true }));
// 速率限制
const limiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 分钟
max: 100, // 每个 IP 最多 100 次请求
message: { success: false, message: '请求过于频繁,请稍后再试' },
});
app.use('/api/', limiter);
// 登录接口更严格的限制
const loginLimiter = rateLimit({
windowMs: 15 * 60 * 1000,
max: 10,
message: { success: false, message: '登录尝试过于频繁,请稍后再试' },
});
app.use('/api/login', loginLimiter);
app.use('/api/register', loginLimiter);
// ============================================
// 路由
// ============================================
app.get('/api/activity/ping', (req, res) => {
res.json({ success: true, message: 'pong', timestamp: Date.now() });
});
// 版本/健康检查 (用于验证部署)
app.get('/api/version', (req, res) => {
res.json({ version: 'd823421' });
});
app.use('/api', authRoutes);
app.use('/api/user', userRoutes);
app.use('/api/schemes', schemeRoutes);
app.use('/api/schemes_aob', schemeAobRoutes);
app.use('/api/filter-shares', filterRoutes);
app.use('/api', vipRoutes);
// 收藏计数(放在 favorites 路由之前,避免 auth 中间件拦截)
app.get('/api/favorites/count', async (req: Request, res: Response) => {
try {
const { PrismaClient } = require('@prisma/client');
const prisma = new PrismaClient();
const count = await prisma.favorite.count();
res.json({ success: true, count });
} catch (e) {
res.json({ success: true, count: 0 });
}
});
app.use('/api/favorites', favoriteRoutes);
// ============================================
// 缺失的存根接口(防止前端 404
// ============================================
// 二维码
app.get('/api/qrcode/public/current', (req, res) => {
res.json({ success: true, data: { qrcode: null, url: '' } });
});
// 弹窗 (通用参数化路由)
app.get('/api/popups/:name', (req, res) => {
const { name } = req.params;
const popups: Record<string, any> = {
test_beta_01: { id: 'test_beta_01', title: '测试弹窗', content: '', shown: false },
};
res.json({ success: true, data: popups[name] ?? null });
});
// 嘉豪弹窗
app.get('/api/jiahao', (req, res) => {
res.json({
success: true,
data: {
enabled: false,
message: '',
imageUrl: '',
},
});
});
// 售后教程弹窗
app.get('/api/aftersale-tutorial-popup', (req, res) => {
res.json({ success: true, data: { shown: false } });
});
// 软件版本广告空数据dock 无广告项)
app.get('/api/software-version-ad', (req, res) => {
res.json({ success: true, data: { items: [] } });
});
// 游戏地图密码缓存
app.get('/api/game/map-password/cached', (req, res) => {
res.json({ success: true, data: [] });
});
// 筛选分类
app.get('/api/filter-share/categories', (req, res) => {
res.json({ success: true, data: [] });
});
// ============================================
// 武器调谐窗 (weapon-tuner) 所需的 API
// ============================================
// 武器分类列表
app.get('/api/weapon-categories', (req, res) => {
const categories = [
{ category: 'AR', name: '突击步枪' },
{ category: 'SMG', name: '冲锋枪' },
{ category: 'SR', name: '狙击步枪' },
{ category: 'LMG', name: '轻机枪' },
{ category: 'SG', name: '霰弹枪' },
{ category: 'Pistol', name: '手枪' },
{ category: 'Launcher', name: '发射器' },
];
res.json({ success: true, data: categories });
});
// 武器列表(按分类筛选)
app.get('/api/weapons', (req, res) => {
res.json({ success: true, data: [] });
});
// 分类下的武器
app.get('/api/category/:code', (req, res) => {
res.json({ success: true, data: [] });
});
// 广告列表
app.get('/api/adverts/list', (req, res) => {
res.json({ success: true, data: [
{
id: 1,
title: '码枪堂2.0 新版发布',
description: '全新界面,更多功能,一键优化三角洲行动游戏体验!',
author: '码枪堂官方',
avatar: null,
image_url: '',
link_url: 'https://wwamt.lanzout.com/b00odpq4wb',
isAdvert: true,
isVip: true,
shareTime: new Date().toISOString(),
}
] });
});
// 广告点击
app.post('/api/adverts/:id/click', (req, res) => {
res.json({ success: true, message: 'ok' });
});
// 头像列表
app.get('/api/avatars', (req, res) => {
res.json({ success: true, data: [] });
});
// ============================================
// 更新服务 (electron-updater)
// ============================================
// 更新配置
app.get('/update-config.json', (req, res) => {
res.json({
version: '7.0.4',
url: '',
notes: '',
mandatory: false,
});
});
// latest.yml (electron-updater 标准格式)
app.get('/latest.yml', (req, res) => {
res.type('text/yaml');
res.send(`version: 7.0.4
files: []
releaseDate: '2024-01-01T00:00:00.000Z'
`);
});
// ============================================
// 错误处理
// ============================================
app.use((err: any, req: express.Request, res: express.Response, next: express.NextFunction) => {
console.error('Error:', err);
if (err.name === 'UnauthorizedError') {
return res.status(401).json({ success: false, message: '访问令牌无效', code: 'INVALID_TOKEN' });
}
if (err.name === 'ZodError') {
return res.status(400).json({ success: false, message: '参数验证失败', errors: err.errors });
}
res.status(500).json({ success: false, message: '服务器内部错误' });
});
// 404
app.use((req, res) => {
res.status(404).json({ success: false, message: '接口不存在' });
});
// ============================================
// 启动
// ============================================
app.listen(PORT, () => {
console.log(`🚀 码枪堂 API 运行在 http://localhost:${PORT}`);
console.log(`📋 可用接口:`);
console.log(` POST /api/login`);
console.log(` POST /api/register`);
console.log(` POST /api/activate-vip`);
console.log(` GET /api/session-status`);
console.log(` GET /api/vip-status`);
});
export default app;