forked from xiangwang25/whale-town-end-v2
Initial WhaleTown V2 backend
This commit is contained in:
197
src/main.ts
Normal file
197
src/main.ts
Normal file
@@ -0,0 +1,197 @@
|
||||
import { NestFactory } from '@nestjs/core';
|
||||
import { AppModule } from './app.module';
|
||||
import { ValidationPipe } from '@nestjs/common';
|
||||
import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger';
|
||||
import { WsAdapter } from '@nestjs/platform-ws';
|
||||
import * as express from 'express';
|
||||
import { resolve } from 'path';
|
||||
|
||||
/**
|
||||
* 检查数据库配置是否完整 by angjustinl 2025-12-17
|
||||
*
|
||||
* @returns 是否配置了数据库
|
||||
*/
|
||||
function isDatabaseConfigured(): boolean {
|
||||
const requiredEnvVars = ['DB_HOST', 'DB_PORT', 'DB_USERNAME', 'DB_PASSWORD', 'DB_NAME'];
|
||||
return requiredEnvVars.every(varName => process.env[varName]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 打印启动横幅
|
||||
*/
|
||||
function printBanner() {
|
||||
const isDatabaseMode = isDatabaseConfigured();
|
||||
|
||||
console.log('\n' + '='.repeat(70));
|
||||
console.log('🎮 Pixel Game Server');
|
||||
console.log('='.repeat(70));
|
||||
console.log(`📦 存储模式: ${isDatabaseMode ? '数据库模式 (MySQL)' : '内存模式 (Memory)'}`);
|
||||
|
||||
if (!isDatabaseMode) {
|
||||
console.log('⚠️ 警告: 未检测到数据库配置,使用内存存储');
|
||||
console.log('💡 提示: 数据将在服务重启后丢失');
|
||||
console.log('📝 配置: 请在 .env 文件中配置数据库连接信息');
|
||||
} else {
|
||||
console.log('✅ 数据库: 已连接到 MySQL 数据库');
|
||||
}
|
||||
|
||||
console.log('='.repeat(70) + '\n');
|
||||
}
|
||||
|
||||
async function bootstrap() {
|
||||
const port = parseInt(process.env.PORT || '3000', 10);
|
||||
const jsonBodyLimit = process.env.JSON_BODY_LIMIT || '24mb';
|
||||
|
||||
// 打印启动横幅
|
||||
printBanner();
|
||||
|
||||
const app = await NestFactory.create(AppModule, {
|
||||
logger: ['error', 'warn', 'log'],
|
||||
bodyParser: false,
|
||||
});
|
||||
|
||||
app.use(express.json({ limit: jsonBodyLimit }));
|
||||
app.use(express.urlencoded({ limit: jsonBodyLimit, extended: true }));
|
||||
|
||||
app.use('/assets/account', express.static(resolve(process.cwd(), process.env.ACCOUNT_ASSET_DIR || 'generated/account-assets')));
|
||||
|
||||
// 配置原生 WebSocket 适配器
|
||||
app.useWebSocketAdapter(new WsAdapter(app));
|
||||
|
||||
// 允许前端后台(如Vite/React)跨域访问,包括WebSocket
|
||||
app.enableCors({
|
||||
origin: [
|
||||
'http://localhost:3000',
|
||||
'http://localhost:5173', // Vite默认端口
|
||||
'https://whaletownend.xinghangee.icu',
|
||||
/^https:\/\/.*\.xinghangee\.icu$/
|
||||
],
|
||||
credentials: true,
|
||||
methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
|
||||
allowedHeaders: ['Content-Type', 'Authorization', 'X-Requested-With'],
|
||||
});
|
||||
|
||||
// 全局启用校验管道(核心配置)
|
||||
app.useGlobalPipes(
|
||||
new ValidationPipe({
|
||||
whitelist: true, // 过滤掉 DTO 中未定义的字段(比如传了个 `age` 但 DTO 里没有,会自动忽略)
|
||||
forbidNonWhitelisted: true, // 若传了未定义的字段,直接报错(防止传多余参数)
|
||||
transform: true, // 自动把入参转为 DTO 对应的类型(比如前端传的字符串数字 `'1'` 转为数字 `1`)
|
||||
}),
|
||||
);
|
||||
|
||||
// 配置Swagger文档
|
||||
const config = new DocumentBuilder()
|
||||
.setTitle('Pixel Game Server API')
|
||||
.setDescription(`
|
||||
像素游戏服务器 API 文档 - 包含用户认证、聊天系统、玩家状态、商城、房间和 Zulip 集成。
|
||||
|
||||
## 主要功能模块
|
||||
|
||||
### 🔐 用户认证 (auth)
|
||||
- 用户注册、登录
|
||||
- JWT Token 管理
|
||||
- 邮箱验证和密码重置
|
||||
- 验证码登录
|
||||
|
||||
### 💬 聊天系统 (chat)
|
||||
- WebSocket 实时聊天
|
||||
- 聊天历史记录
|
||||
- 系统状态监控
|
||||
- Zulip 集成状态
|
||||
|
||||
### 🔌 WebSocket 接口 (websocket)
|
||||
- 实时消息传输
|
||||
- 位置同步
|
||||
- 地图房间管理
|
||||
- 连接状态监控
|
||||
|
||||
### 👑 管理员后台 (admin)
|
||||
- 用户管理
|
||||
- 系统监控
|
||||
- 日志查看
|
||||
|
||||
## WebSocket 连接
|
||||
|
||||
游戏聊天功能主要通过 WebSocket 实现:
|
||||
|
||||
**连接地址**: \`wss://whaletownend.xinghangee.icu/game\` (原生WebSocket)
|
||||
|
||||
**重要变更**: 已从Socket.IO迁移到原生WebSocket,提升性能和稳定性
|
||||
|
||||
**连接路径**: \`/game\` - 统一的WebSocket入口
|
||||
|
||||
**支持的事件**:
|
||||
- \`login\`: 用户登录(需要 JWT Token)
|
||||
- \`chat\`: 发送聊天消息
|
||||
- \`position\`: 位置更新
|
||||
|
||||
**JWT Token 要求**:
|
||||
- issuer: \`whale-town\`
|
||||
- audience: \`whale-town-users\`
|
||||
- type: \`access\`
|
||||
- 必需字段: \`sub\`, \`username\`, \`email\`, \`role\`
|
||||
|
||||
## Zulip 集成
|
||||
|
||||
系统集成了 Zulip 聊天服务,实现游戏内聊天与 Zulip 社群的双向同步。
|
||||
|
||||
**支持的地图**:
|
||||
- Whale Port (鲸鱼港)
|
||||
- Pumpkin Valley (南瓜谷)
|
||||
- Novice Village (新手村)
|
||||
|
||||
## 最近更新 (v2.1.0)
|
||||
|
||||
### 🚀 WebSocket 架构升级
|
||||
- ✅ 移除Socket.IO依赖,使用原生WebSocket
|
||||
- ✅ 实现地图房间分组管理
|
||||
- ✅ 支持本地和全局消息广播
|
||||
- ✅ 新增实时连接监控
|
||||
|
||||
### 📚 文档完善
|
||||
- ✅ 新增WebSocket专用API文档
|
||||
- ✅ 提供交互式消息格式展示
|
||||
- ✅ 包含测试工具和示例代码
|
||||
- ✅ 完整的开发者指南
|
||||
|
||||
### 🔧 性能优化
|
||||
- ✅ 更高效的消息路由机制
|
||||
- ✅ 优化连接池管理
|
||||
- ✅ 增强错误处理和日志记录
|
||||
`)
|
||||
.setVersion('2.1.0')
|
||||
.addTag('auth', '🔐 用户认证相关接口')
|
||||
.addTag('chat', '💬 聊天系统相关接口')
|
||||
.addTag('websocket', '🔌 WebSocket接口文档和测试')
|
||||
.addTag('admin', '👑 管理员后台相关接口')
|
||||
.addBearerAuth(
|
||||
{
|
||||
type: 'http',
|
||||
scheme: 'bearer',
|
||||
bearerFormat: 'JWT',
|
||||
name: 'JWT',
|
||||
description: '请输入JWT token',
|
||||
in: 'header',
|
||||
},
|
||||
'JWT-auth',
|
||||
)
|
||||
.addServer(`http://localhost:${port}`, '开发环境 - REST API')
|
||||
.addServer('https://whaletownend.xinghangee.icu', '生产环境 - REST API')
|
||||
.addServer('wss://whaletownend.xinghangee.icu/game', '生产环境 - WebSocket')
|
||||
.addServer('ws://localhost:3001/game', '开发环境 - WebSocket')
|
||||
.build();
|
||||
|
||||
const document = SwaggerModule.createDocument(app, config);
|
||||
SwaggerModule.setup('api-docs', app, document, {
|
||||
swaggerOptions: {
|
||||
persistAuthorization: true,
|
||||
},
|
||||
});
|
||||
|
||||
await app.listen(port);
|
||||
console.log(`Pixel Game Server is running on http://localhost:${port}`);
|
||||
console.log(`API Documentation is available at http://localhost:${port}/api-docs`);
|
||||
}
|
||||
|
||||
bootstrap();
|
||||
Reference in New Issue
Block a user