feat: 实现游戏核心功能与前端界面

- 添加游戏核心系统:战斗、任务、日志、随机数生成等
- 实现前端主界面、HUD、底部导航和各类卡片组件
- 添加用户认证系统与游戏存档持久化
- 配置项目基础架构与开发环境
- 补充文档说明与Docker部署支持
This commit is contained in:
2026-04-28 22:16:58 +08:00
parent 2e1b58bada
commit e783e6e533
124 changed files with 9759 additions and 1 deletions
+120
View File
@@ -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();