feat: 实现游戏核心功能与前端界面
- 添加游戏核心系统:战斗、任务、日志、随机数生成等 - 实现前端主界面、HUD、底部导航和各类卡片组件 - 添加用户认证系统与游戏存档持久化 - 配置项目基础架构与开发环境 - 补充文档说明与Docker部署支持
This commit is contained in:
@@ -0,0 +1,120 @@
|
||||
import { existsSync } from 'node:fs';
|
||||
import Fastify from 'fastify';
|
||||
import cookie from '@fastify/cookie';
|
||||
import cors from '@fastify/cors';
|
||||
import fastifyStatic from '@fastify/static';
|
||||
import { ZodError } from 'zod';
|
||||
import { db } from './shared/database/client';
|
||||
import { env } from './shared/env';
|
||||
import { formatZodError, isAppError } from './shared/errors';
|
||||
import { AuthRepository } from './features/auth/auth.repository';
|
||||
import { AuthService } from './features/auth/auth.service';
|
||||
import { registerAuthRoutes } from './features/auth/auth.routes';
|
||||
import { GameRepository } from './features/game/game.repository';
|
||||
import { GameService } from './features/game/game.service';
|
||||
import { registerGameRoutes } from './features/game/game.routes';
|
||||
|
||||
const app = Fastify({ logger: true });
|
||||
|
||||
await app.register(cookie, {
|
||||
secret: env.sessionSecret,
|
||||
});
|
||||
|
||||
await app.register(cors, {
|
||||
origin: env.webOrigin,
|
||||
credentials: true,
|
||||
});
|
||||
|
||||
const authRepository = new AuthRepository(db);
|
||||
const authService = new AuthService(authRepository);
|
||||
const gameRepository = new GameRepository(db);
|
||||
const gameService = new GameService(gameRepository);
|
||||
|
||||
app.get('/api/health', async () => ({
|
||||
ok: true,
|
||||
service: 'tinywaste-api',
|
||||
}));
|
||||
|
||||
registerAuthRoutes(app, authService);
|
||||
registerGameRoutes(app, authService, gameService);
|
||||
|
||||
const webDistAvailable = existsSync(env.webDistPath);
|
||||
|
||||
if (webDistAvailable) {
|
||||
await app.register(fastifyStatic, {
|
||||
root: env.webDistPath,
|
||||
prefix: '/',
|
||||
maxAge: '30d',
|
||||
immutable: true,
|
||||
});
|
||||
|
||||
app.get('/', async (_request, reply) =>
|
||||
reply.sendFile('index.html', {
|
||||
maxAge: 0,
|
||||
immutable: false,
|
||||
}),
|
||||
);
|
||||
|
||||
app.setNotFoundHandler((request, reply) => {
|
||||
const acceptsHtml = request.headers.accept?.includes('text/html');
|
||||
|
||||
if (request.method === 'GET' && acceptsHtml && !request.url.startsWith('/api/')) {
|
||||
return reply.sendFile('index.html', {
|
||||
maxAge: 0,
|
||||
immutable: false,
|
||||
});
|
||||
}
|
||||
|
||||
return reply.status(404).send({
|
||||
error: {
|
||||
code: 'NOT_FOUND',
|
||||
message: '请求的资源不存在。',
|
||||
},
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
app.setErrorHandler((error, _request, reply) => {
|
||||
if (isAppError(error)) {
|
||||
reply.status(error.statusCode).send({
|
||||
error: {
|
||||
code: error.code,
|
||||
message: error.message,
|
||||
},
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (error instanceof ZodError) {
|
||||
reply.status(400).send({
|
||||
error: {
|
||||
code: 'VALIDATION_ERROR',
|
||||
message: formatZodError(error),
|
||||
},
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
app.log.error(error);
|
||||
reply.status(500).send({
|
||||
error: {
|
||||
code: 'INTERNAL_ERROR',
|
||||
message: '服务器内部错误,请稍后再试。',
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
const start = async () => {
|
||||
try {
|
||||
await app.listen({ port: env.port, host: '0.0.0.0' });
|
||||
app.log.info(`API running on http://localhost:${env.port}`);
|
||||
if (webDistAvailable) {
|
||||
app.log.info(`Web bundle served from ${env.webDistPath}`);
|
||||
}
|
||||
} catch (error) {
|
||||
app.log.error(error);
|
||||
process.exit(1);
|
||||
}
|
||||
};
|
||||
|
||||
await start();
|
||||
Reference in New Issue
Block a user