tsoa setup

This commit is contained in:
tw-blue
2025-11-01 19:49:12 +01:00
parent 3bbd73d625
commit 9f1e30e6fa
12 changed files with 982 additions and 15 deletions
+4 -1
View File
@@ -7,5 +7,8 @@
"author": "", "author": "",
"license": "UNLICENSED", "license": "UNLICENSED",
"packageManager": "pnpm@10.20.0", "packageManager": "pnpm@10.20.0",
"type": "module" "type": "module",
"devDependencies": {
"tsoa": "^6.6.0"
}
} }
+1
View File
@@ -0,0 +1 @@
generated/
+1
View File
@@ -14,6 +14,7 @@
"typescript": "~5.9.3" "typescript": "~5.9.3"
}, },
"dependencies": { "dependencies": {
"@tsoa/runtime": "^6.6.0",
"express": "^5.1.0" "express": "^5.1.0"
} }
} }
@@ -0,0 +1,20 @@
import { Controller, Get, Route, SuccessResponse } from 'tsoa';
export interface User {
email: string;
name: string;
phoneNumbers: string[];
}
export type UserCreationParams = Pick<User, "email" | "name" | "phoneNumbers">;
@Route('health')
export class HealthCheckController extends Controller {
@Get('ok')
@SuccessResponse('200', 'OK')
public async getOk(
): Promise<string> {
this.setStatus(200);
return 'Ok';
}
}
+5 -3
View File
@@ -1,11 +1,13 @@
import express from 'express'; import express from 'express';
import { RegisterRoutes } from './generated/routes.js';
const app = express(); const app = express();
const port = 3000; const port = 3000;
app.get('/', (_, res) => { app.use(express.urlencoded({ extended: true }));
res.send('Hello World!'); app.use(express.json());
});
RegisterRoutes(app);
app.listen(port, () => { app.listen(port, () => {
console.log(`Server is running at http://localhost:${port}`); console.log(`Server is running at http://localhost:${port}`);
@@ -0,0 +1,10 @@
import * as express from 'express';
export async function expressAuthentication(
request: express.Request,
securityName: string,
scopes?: string[]
): Promise<any> {
console.dir({ request, securityName, scopes });
return {};
}
+16 -1
View File
@@ -40,6 +40,21 @@
"noUncheckedSideEffectImports": true, "noUncheckedSideEffectImports": true,
"moduleDetection": "force", "moduleDetection": "force",
"skipLibCheck": true, "skipLibCheck": true,
// Custom Options
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"useDefineForClassFields": false,
"moduleResolution": "nodenext",
"incremental": true,
"noImplicitAny": true,
"strictNullChecks": true,
"strictFunctionTypes": true,
"strictBindCallApply": true,
"strictPropertyInitialization": true,
"noImplicitThis": true,
"alwaysStrict": true,
"forceConsistentCasingInFileNames": true,
}, },
"files": ["./src/index.ts"], "files": ["./src/index.ts"]
} }
+3
View File
@@ -41,3 +41,6 @@ __screenshots__/
# System files # System files
.DS_Store .DS_Store
Thumbs.db Thumbs.db
###
src/generated-api/
+644 -8
View File
File diff suppressed because it is too large Load Diff
+8 -2
View File
@@ -1,3 +1,9 @@
packages: packages:
- 'packages/*' - packages/*
- 'libs/*' - libs/*
onlyBuiltDependencies:
- '@parcel/watcher'
- esbuild
- lmdb
- msgpackr-extract
+223
View File
@@ -0,0 +1,223 @@
// @ts-nocheck
// This file was copied from https://github.com/lukeautry/tsoa/blob/v6.6.0/packages/cli/src/routeGeneration/templates/express.hbs to add the above line, please keep up to date
/* tslint:disable */
/* eslint-disable */
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
import type { TsoaRoute } from '@tsoa/runtime';
import { fetchMiddlewares, ExpressTemplateService } from '@tsoa/runtime';
{{#each controllers}}
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
import { {{name}} } from '{{modulePath}}.js';
{{/each}}
{{#if authenticationModule}}
import { expressAuthentication } from '{{authenticationModule}}.js';
// @ts-ignore - no great way to install types from subpackage
{{/if}}
{{#if iocModule}}
import { iocContainer } from '{{iocModule}}';
import type { IocContainer, IocContainerFactory } from '@tsoa/runtime';
{{/if}}
import type { Request as ExRequest, Response as ExResponse, RequestHandler, Router } from 'express';
{{#if useFileUploads}}
{{#if esm}}
import multer from 'multer';
{{else}}
const multer = require('multer');
{{/if}}
{{/if}}
{{#if authenticationModule}}
const expressAuthenticationRecasted = expressAuthentication as (req: ExRequest, securityName: string, scopes?: string[], res?: ExResponse) => Promise<any>;
{{/if}}
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
const models: TsoaRoute.Models = {
{{#each models}}
"{{@key}}": {
{{#if enums}}
"dataType": "refEnum",
"enums": {{{json enums}}},
{{/if}}
{{#if properties}}
"dataType": "refObject",
"properties": {
{{#each properties}}
"{{@key}}": {{{json this}}},
{{/each}}
},
"additionalProperties": {{{json additionalProperties}}},
{{/if}}
{{#if type}}
"dataType": "refAlias",
"type": {{{json type}}},
{{/if}}
},
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
{{/each}}
};
const templateService = new ExpressTemplateService(models, {{{ json minimalSwaggerConfig}}});
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
{{#if useFileUploads}}
export function RegisterRoutes(app: Router,opts?:{multer?:ReturnType<typeof multer>}) {
{{else}}
export function RegisterRoutes(app: Router) {
{{/if}}
// ###########################################################################################################
// NOTE: If you do not see routes for all of your controllers in this file, then you might not have informed tsoa of where to look
// Please look into the "controllerPathGlobs" config option described in the readme: https://github.com/lukeautry/tsoa
// ###########################################################################################################
{{#if useFileUploads}}
const upload = opts?.multer || multer({{{json multerOpts}}});
{{/if}}
{{#each controllers}}
{{#each actions}}
const args{{../name}}_{{name}}: Record<string, TsoaRoute.ParameterSchema> = {
{{#each parameters}}
{{@key}}: {{{json this}}},
{{/each}}
};
app.{{method}}('{{fullPath}}',
{{#if security.length}}
authenticateMiddleware({{json security}}),
{{/if}}
{{#if uploadFile}}
upload.fields([
{{#each uploadFileName}}
{
name: {{json name}},
{{#if maxCount}}
maxCount: {{maxCount}}
{{/if}}
}{{#if @last}}{{else}},{{/if}}
{{/each}}
]),
{{/if}}
...(fetchMiddlewares<RequestHandler>({{../name}})),
...(fetchMiddlewares<RequestHandler>({{../name}}.prototype.{{name}})),
async function {{../name}}_{{name}}(request: ExRequest, response: ExResponse, next: any) {
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
let validatedArgs: any[] = [];
try {
validatedArgs = templateService.getValidatedArgs({ args: args{{../name}}_{{name}}, request, response });
{{#if ../../iocModule}}
const container: IocContainer = typeof iocContainer === 'function' ? (iocContainer as IocContainerFactory)(request) : iocContainer;
const controller: any = await container.get<{{../name}}>({{../name}});
if (typeof controller['setStatus'] === 'function') {
controller.setStatus(undefined);
}
{{else}}
const controller = new {{../name}}();
{{/if}}
await templateService.apiHandler({
methodName: '{{name}}',
controller,
response,
next,
validatedArgs,
successStatus: {{successStatus}},
});
} catch (err) {
return next(err);
}
});
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
{{/each}}
{{/each}}
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
{{#if useSecurity}}
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
function authenticateMiddleware(security: TsoaRoute.Security[] = []) {
return async function runAuthenticationMiddleware(request: any, response: any, next: any) {
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
// keep track of failed auth attempts so we can hand back the most
// recent one. This behavior was previously existing so preserving it
// here
const failedAttempts: any[] = [];
const pushAndRethrow = (error: any) => {
failedAttempts.push(error);
throw error;
};
const secMethodOrPromises: Promise<any>[] = [];
for (const secMethod of security) {
if (Object.keys(secMethod).length > 1) {
const secMethodAndPromises: Promise<any>[] = [];
for (const name in secMethod) {
secMethodAndPromises.push(
expressAuthenticationRecasted(request, name, secMethod[name], response)
.catch(pushAndRethrow)
);
}
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
secMethodOrPromises.push(Promise.all(secMethodAndPromises)
.then(users => { return users[0]; }));
} else {
for (const name in secMethod) {
secMethodOrPromises.push(
expressAuthenticationRecasted(request, name, secMethod[name], response)
.catch(pushAndRethrow)
);
}
}
}
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
try {
request['user'] = await Promise.any(secMethodOrPromises);
// Response was sent in middleware, abort
if (response.writableEnded) {
return;
}
next();
}
catch(err) {
// Show most recent error as response
const error = failedAttempts.pop();
error.status = error.status || 401;
// Response was sent in middleware, abort
if (response.writableEnded) {
return;
}
next(error);
}
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
}
}
{{/if}}
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
}
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
+47
View File
@@ -0,0 +1,47 @@
{
"entryFile": "packages/api/src/index.ts",
"noImplicitAdditionalProperties": "throw-on-extras",
"controllerPathGlobs": [
"packages/api/src/controllers/**/*.ts"
],
"routes": {
"routesDir": "packages/api/src/generated",
"middleware": "express",
"authenticationModule": "packages/api/src/middleware/authentication.ts",
"middlewareTemplate": "routes.hbs"
},
"spec": {
"outputDirectory": "packages/frontend/src/generated-api",
"specVersion": 3,
"securityDefinitions": {
"bearerAuth": {
"type": "http",
"scheme": "Bearer",
"description": "JWT token",
"bearerFormat": "JWT"
}
},
"basePath": "/api/v1",
"servers": [
{
"url": "https://localhost:8080/api/v1",
"description": "Development server"
},
{
"url": "https://tcc-member-db-4bfaysdsja-ew.a.run.app/api/v1",
"description": "Production server"
}
],
"info": {
"title": "Tcc Member API",
"description": "API for the TCC Member Database",
"version": "1.0.0",
"contact": {
"name": "TCC",
"url": "https://taekwondo-chemnitz-toni714.de/kontakt",
"email": "toniwalter.blue@gmail.com"
}
}
}
}