feat(server): add tsoa to generate api swaggers

This commit is contained in:
dayjoy
2025-09-26 12:20:02 +08:00
parent 3ef3fcbff9
commit 9551f6aab9
11 changed files with 320 additions and 439 deletions

1
.gitignore vendored
View File

@@ -27,3 +27,4 @@ build
*.njsproj
*.sln
*.sw?
/packages/server/src/routes/routes.ts

View File

@@ -4,7 +4,7 @@
"description": "",
"main": "index.js",
"scripts": {
"build": "tsc && tsc-alias",
"build": "tsoa spec-and-routes && tsc && tsc-alias",
"serve": "node ./build/index.js",
"start": "nodemon"
},
@@ -16,10 +16,11 @@
"compression": "^1.7.5",
"cors": "^2.8.5",
"express": "^4.21.2",
"express-oas-generator": "^1.0.48",
"nocache": "^4.0.0",
"sequelize": "^6.37.7",
"sqlite3": "^5.1.7"
"sqlite3": "^5.1.7",
"tsoa": "^5.0.0",
"swagger-ui-express": "^5.0.0"
},
"devDependencies": {
"@types/axios": "^0.9.36",

View File

@@ -1,24 +0,0 @@
import { Express } from "express";
import {userInfoMiddleware, groupInfoMiddleware} from "@/middleware/auth";
export const createApis = (app: Express) => {
app.get("/api/test", async (req, res) => {
res.status(200).json({
message: "success",
data: null,
});
});
// 同时使用用户信息和群组信息中间件
app.get("/api/user-group/info", userInfoMiddleware, groupInfoMiddleware, async (req, res) => {
res.status(200).json({
message: "success",
data: {
user: req.userInfo,
group: req.groupInfo
}
});
});
/** add apis here */
};

View File

@@ -0,0 +1,20 @@
import { Controller, Get, Route, Response, Tags } from 'tsoa';
import { ApiResponse } from '../types/api';
@Route('api')
@Tags('Test')
export class TestController extends Controller {
/**
* 测试接口
* @summary 测试接口
* @description 用于测试API连通性的基础接口
*/
@Get('/test')
@Response<ApiResponse>(200, 'Success')
public async getTest(): Promise<ApiResponse> {
return {
message: 'success',
data: null,
};
}
}

View File

@@ -0,0 +1,30 @@
import { Controller, Get, Route, Response, Tags, Middlewares, Request } from 'tsoa';
import { ApiResponse, UserGroupInfo } from '../types/api';
import { userInfoMiddleware, groupInfoMiddleware } from '../middleware/auth';
import type { Request as ExpressRequest } from 'express';
@Route('api')
@Tags('User Group')
export class UserGroupController extends Controller {
/**
* 获取用户群组信息
* @summary 获取用户群组信息
* @description 获取当前用户的个人信息和所在群组的所有用户信息
*/
@Get('/user-group/info')
@Middlewares([userInfoMiddleware, groupInfoMiddleware])
@Response<ApiResponse<UserGroupInfo>>(200, 'Success')
@Response(401, 'Unauthorized')
public async getUserGroupInfo(@Request() req: ExpressRequest): Promise<ApiResponse<UserGroupInfo>> {
const userInfo = req.userInfo;
const groupInfo = req.groupInfo;
return {
message: 'success',
data: {
user: userInfo || null,
group: groupInfo || null
}
};
}
}

View File

@@ -4,16 +4,12 @@ import cors from "cors";
import compression from "compression";
import nocache from "nocache";
import path from "path";
import expressOasGenerator, {
OpenAPIV3,
SPEC_OUTPUT_FILE_BEHAVIOR,
} from "express-oas-generator";
dotenv.config({ path: path.resolve(__dirname, "../../../.env") });
import "./database";
import { sequelize } from "@/database/instance";
import { createApis } from "./api";
import { RegisterTsoaRoutes } from "./middleware/tsoa.middleware";
const port = process.env.PORT || 3005;
@@ -26,29 +22,9 @@ app.use(compression());
app.use(express.static(path.resolve(__dirname, "client")));
createApis(app);
// Register tsoa routes
RegisterTsoaRoutes(app);
expressOasGenerator.handleResponses(app, {
specOutputFileBehavior: SPEC_OUTPUT_FILE_BEHAVIOR.RECREATE,
swaggerDocumentOptions: {},
predefinedSpec: (spec: OpenAPIV3.Document) => {
if (spec.info) {
spec.info.description = "Egret App Server";
}
if (spec.paths) {
delete spec.paths["/v3/api-docs"];
}
return spec;
},
});
expressOasGenerator.handleRequests();
app.get("/v3/api-docs", async (req, res) => {
expressOasGenerator.getSpecV3((err, doc) => {
res.status(200).json(doc);
});
});
const host = "0.0.0.0";
app.listen(Number(port), host, async () => {

View File

@@ -0,0 +1,12 @@
import swaggerUi from 'swagger-ui-express';
import path from 'path';
export function RegisterTsoaRoutes(app: any) {
// Register tsoa routes - 动态导入以避免编译时错误
const { RegisterRoutes } = require('../routes/routes');
RegisterRoutes(app);
// Serve swagger documentation - 动态导入swagger文档
const swaggerDocument = require(path.join(__dirname, '../swagger.json'));
app.use('/v3/api-docs', swaggerUi.serve, swaggerUi.setup(swaggerDocument));
}

View File

@@ -0,0 +1,14 @@
import { UserInfo } from '../middleware/auth';
export interface ApiResponse<T = any> {
message: string;
data: T | null;
}
export interface UserGroupInfo {
user: UserInfo | null;
group: {
groupId: number;
users: UserInfo[];
} | null;
}

View File

@@ -9,7 +9,10 @@
"paths": {
"@/*": ["src/*"]
},
"outDir": "./build"
"outDir": "./build",
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"useDefineForClassFields": false
},
"include": ["src"],
"exclude": ["node_modules", "build"],

18
packages/server/tsoa.json Normal file
View File

@@ -0,0 +1,18 @@
{
"entryFile": "src/index.ts",
"noImplicitAdditionalProperties": "throw-on-extras",
"controllerPathGlobs": ["src/controllers/**/*Controller.ts"],
"spec": {
"outputDirectory": "build",
"specVersion": 3,
"name": "Egret App Server",
"description": "Egret App Server API Documentation",
"version": "1.0.0",
"basePath": "/",
"schemes": ["http", "https"],
"host": "localhost:3005"
},
"routes": {
"routesDir": "src/routes"
}
}

598
yarn.lock

File diff suppressed because it is too large Load Diff