@nest-arch/tui 0.5.0 → 0.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +31 -0
- package/dist/cli.js +2 -1
- package/dist/{constants-DX5wiqrq.js → constants-CUTbfu9C.js} +1 -1
- package/dist/index.js +6 -417
- package/dist/nonInteractive-C1KZ0b_6.js +5 -0
- package/dist/nonInteractiveOptions-CIaoLAWg.js +24 -0
- package/dist/projectGeneratorService-CeWJK-HA.js +420 -0
- package/dist/projectName-CcE90t2V.js +1 -0
- package/dist/templates/base/.env.example.hbs +23 -0
- package/dist/templates/base/prisma.config.ts.hbs +6 -6
- package/dist/templates/base/src/app.controller.spec.ts.hbs +6 -0
- package/dist/templates/base/src/app.module.ts.hbs +1 -1
- package/dist/templates/base/src/database/adapters/interfaces/db-adapter.interface.ts.hbs +5 -5
- package/dist/templates/base/src/database/adapters/mongo/raw-mongo.adapter.ts.hbs +2 -2
- package/dist/templates/base/src/database/adapters/mongo/typeorm-mongo.adapter.ts.hbs +27 -0
- package/dist/templates/base/src/database/db.module.ts.hbs +22 -16
- package/dist/templates/base/src/database/db.service.ts.hbs +7 -2
- package/dist/templates/base/src/database/entities/todo.entity.ts.hbs +15 -3
- package/dist/templates/base/src/database/orm/drizzle/schema.ts.hbs +38 -14
- package/dist/templates/base/src/database/orm/typeorm/data-source.ts.hbs +12 -6
- package/dist/templates/base/src/libs/middlewares/rate-limit.trpc.middleware.ts.hbs +2 -1
- package/dist/templates/base/test/app.e2e-spec.ts.hbs +14 -10
- package/dist/templates/extras/docker/Dockerfile.hbs +2 -2
- package/dist/templates/extras/health/src/health/health.controller.ts.hbs +5 -0
- package/dist/templates/feature/rest/todo-example/src/api/todo/todo.router.ts.hbs +4 -4
- package/dist/templates/feature/rest/todo-example/src/api/todo/todo.service.ts.hbs +135 -11
- package/dist/templates/single-project/architecture/nest-microservice/.env.hbs +24 -1
- package/dist/templates/single-project/architecture/nest-microservice/proto/app.proto.hbs +13 -0
- package/dist/templates/single-project/architecture/nest-microservice/src/app.controller.ts.hbs +24 -0
- package/dist/templates/single-project/architecture/nest-microservice/src/app.module.ts.hbs +33 -0
- package/dist/templates/single-project/architecture/nest-microservice/src/app.service.ts.hbs +16 -4
- package/dist/templates/single-project/architecture/nest-microservice/src/main.ts.hbs +25 -6
- package/dist/templates/single-project/architecture/nest-microservice/test/microservice.e2e-spec.ts.hbs +7 -122
- package/dist/templates/single-project/shared/config/env.ts.hbs +14 -14
- package/package.json +2 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var a=Object.defineProperty;var r=(e,t)=>a(e,"name",{value:t,configurable:!0});const s="my-nest-app",m=64,n=/^[a-z][a-z0-9-]*$/u;function E(e){return e.trim()||s}r(E,"resolveProjectName");function o(e){const t=e.trim();return t?t.length>64?"Project name must be at most 64 characters":n.test(t)?null:"Use lowercase letters, numbers, and hyphens; must start with a letter":null}r(o,"validateProjectName");export{s as D,m as M,n as P,E as r,o as v};
|
|
@@ -5,6 +5,29 @@ CORS_ORIGIN=http://localhost:3000
|
|
|
5
5
|
# Comma-separated trusted reverse proxy IP addresses or CIDRs
|
|
6
6
|
TRUSTED_PROXY_IPS={{#each trustedProxyIps}}{{#unless @first}},{{/unless}}{{this}}{{/each}}
|
|
7
7
|
|
|
8
|
+
{{#if (eq architecture "nest-microservice")}}
|
|
9
|
+
# Microservice transport
|
|
10
|
+
MICROSERVICE_PORT=3001
|
|
11
|
+
{{/if}}
|
|
12
|
+
{{#if (includes microservices "redis")}}
|
|
13
|
+
REDIS_URL=redis://127.0.0.1:6379
|
|
14
|
+
{{/if}}
|
|
15
|
+
{{#if (includes microservices "mqtt")}}
|
|
16
|
+
MQTT_URL=mqtt://127.0.0.1:1883
|
|
17
|
+
{{/if}}
|
|
18
|
+
{{#if (includes microservices "nats")}}
|
|
19
|
+
NATS_URL=nats://127.0.0.1:4222
|
|
20
|
+
{{/if}}
|
|
21
|
+
{{#if (includes microservices "rabbitmq")}}
|
|
22
|
+
RABBITMQ_URL=amqp://127.0.0.1:5672
|
|
23
|
+
{{/if}}
|
|
24
|
+
{{#if (includes microservices "kafka")}}
|
|
25
|
+
KAFKA_BROKER=127.0.0.1:9092
|
|
26
|
+
{{/if}}
|
|
27
|
+
{{#if (includes microservices "grpc")}}
|
|
28
|
+
GRPC_URL=0.0.0.0:5000
|
|
29
|
+
{{/if}}
|
|
30
|
+
|
|
8
31
|
{{#if hasDatabase}}
|
|
9
32
|
# Database
|
|
10
33
|
{{/if}}
|
|
@@ -26,12 +26,6 @@ export default definePrismaConfig({
|
|
|
26
26
|
import { defineConfig } from "prisma/config";
|
|
27
27
|
|
|
28
28
|
export default defineConfig({
|
|
29
|
-
schema: "src/database/orm/prisma{{#unless isBetterAuth}}/schema.prisma{{/unless}}",
|
|
30
|
-
{{#unless isMonorepo}}
|
|
31
|
-
migrations: {
|
|
32
|
-
path: "src/database/orm/prisma/migrations",
|
|
33
|
-
},
|
|
34
|
-
{{/unless}}
|
|
35
29
|
datasource: {
|
|
36
30
|
{{#if hasPostgres}}url: env.POSTGRES_URL,{{/if}}
|
|
37
31
|
{{#if hasMysql}}url: env.MYSQL_URL,{{/if}}
|
|
@@ -39,5 +33,11 @@ export default defineConfig({
|
|
|
39
33
|
{{#if hasSqlserver}}url: `sqlserver://${env.SQLSERVER_HOST}:${env.SQLSERVER_PORT};database=${env.SQLSERVER_DATABASE};user={${env.SQLSERVER_USER.replaceAll("}", "}}")}};password={${env.SQLSERVER_PASSWORD.replaceAll("}", "}}")}};encrypt=${env.SQLSERVER_ENCRYPT};trustServerCertificate=${env.SQLSERVER_TRUST_SERVER_CERTIFICATE}`,{{/if}}
|
|
40
34
|
{{#if hasMongodb}}url: env.MONGODB_URL,{{/if}}
|
|
41
35
|
},
|
|
36
|
+
{{#unless isMonorepo}}
|
|
37
|
+
migrations: {
|
|
38
|
+
path: "src/database/orm/prisma/migrations",
|
|
39
|
+
},
|
|
40
|
+
{{/unless}}
|
|
41
|
+
schema: "src/database/orm/prisma{{#unless isBetterAuth}}/schema.prisma{{/unless}}",
|
|
42
42
|
});
|
|
43
43
|
{{/if}}
|
|
@@ -7,6 +7,9 @@ import { AppController } from "./app.controller.js";
|
|
|
7
7
|
import { AppService } from "./app.service.js";
|
|
8
8
|
{{#if (or hasDatabase hasOrm)}}
|
|
9
9
|
import { DbService } from "./database/db.service.js";
|
|
10
|
+
{{#if (and (eq architecture "nest-microservice") hasTodoExample)}}
|
|
11
|
+
import { TodoService } from "./api/todo/todo.service.js";
|
|
12
|
+
{{/if}}
|
|
10
13
|
{{/if}}
|
|
11
14
|
|
|
12
15
|
|
|
@@ -21,6 +24,9 @@ describe(AppController, () => {
|
|
|
21
24
|
AppService,
|
|
22
25
|
{{#if (or hasDatabase hasOrm)}}
|
|
23
26
|
{ provide: DbService, useValue: {} },
|
|
27
|
+
{{#if (and (eq architecture "nest-microservice") hasTodoExample)}}
|
|
28
|
+
{ provide: TodoService, useValue: {} },
|
|
29
|
+
{{/if}}
|
|
24
30
|
{{/if}}
|
|
25
31
|
],
|
|
26
32
|
}).compile();
|
|
@@ -69,7 +69,7 @@ import { AppService } from "./app.service.js";
|
|
|
69
69
|
{{#if hasPostgres}}postgresUrl: env.POSTGRES_URL,{{/if}}
|
|
70
70
|
{{#if hasMysql}}mysqlUrl: env.MYSQL_URL,{{/if}}
|
|
71
71
|
{{#if hasSqlite}}sqlitePath: env.SQLITE_PATH,{{/if}}
|
|
72
|
-
{{#if hasSqlserver}}sqlserver: {
|
|
72
|
+
{{#if hasSqlserver}}sqlserver: { database: env.SQLSERVER_DATABASE, options: { encrypt: env.SQLSERVER_ENCRYPT, trustServerCertificate: env.SQLSERVER_TRUST_SERVER_CERTIFICATE }, password: env.SQLSERVER_PASSWORD, port: env.SQLSERVER_PORT, server: env.SQLSERVER_HOST, user: env.SQLSERVER_USER },{{/if}}
|
|
73
73
|
{{#if hasMongodb}}mongodbUrl: env.MONGODB_URL,{{/if}}
|
|
74
74
|
}),
|
|
75
75
|
{{/if}}
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
{{#if (and hasMongodb (or useRawSql useTypeorm))}}
|
|
2
|
+
import type { Collection, Document } from "mongodb";
|
|
3
|
+
{{/if}}
|
|
1
4
|
{{#if (or hasPostgres hasMysql hasSqlite hasSqlserver)}}
|
|
2
5
|
export interface ISqlAdapter<TClient = unknown> {
|
|
3
6
|
query: <T = unknown>(sql: string, params?: unknown[]) => Promise<T[]>;
|
|
@@ -7,12 +10,9 @@ export interface ISqlAdapter<TClient = unknown> {
|
|
|
7
10
|
{{/if}}
|
|
8
11
|
|
|
9
12
|
{{#if hasMongodb}}
|
|
10
|
-
{{#if useRawSql}}
|
|
11
|
-
import type { Collection, Document } from "mongodb";
|
|
12
|
-
{{/if}}
|
|
13
13
|
export interface IMongoAdapter<TClient = unknown> {
|
|
14
|
-
{{#if useRawSql}}
|
|
15
|
-
collection: <T
|
|
14
|
+
{{#if (or useRawSql useTypeorm)}}
|
|
15
|
+
collection: <T = unknown>(name: string) => Collection<T & Document>;
|
|
16
16
|
{{else}}
|
|
17
17
|
collection: <T = unknown>(name: string) => T;
|
|
18
18
|
{{/if}}
|
|
@@ -7,8 +7,8 @@ import type { IMongoAdapter } from "../interfaces/db-adapter.interface.js";
|
|
|
7
7
|
@Injectable()
|
|
8
8
|
export class RawMongoAdapter implements IMongoAdapter<Db>, OnModuleInit, OnModuleDestroy {
|
|
9
9
|
constructor(@Inject("MONGODB_CLIENT") private readonly client: MongoClient) {}
|
|
10
|
-
collection<T
|
|
11
|
-
return this.client.db().collection<T>(name);
|
|
10
|
+
collection<T = unknown>(name: string): Collection<T & Document> {
|
|
11
|
+
return this.client.db().collection<T & Document>(name);
|
|
12
12
|
}
|
|
13
13
|
getClient(): Db { return this.client.db(); }
|
|
14
14
|
async onModuleInit(): Promise<void> {
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { Inject, Injectable } from "@nestjs/common";
|
|
2
|
+
import type { OnModuleDestroy } from "@nestjs/common";
|
|
3
|
+
import type { DataSource } from "typeorm";
|
|
4
|
+
import type { Collection, Document } from "mongodb";
|
|
5
|
+
import type { IMongoAdapter } from "../interfaces/db-adapter.interface.js";
|
|
6
|
+
|
|
7
|
+
@Injectable()
|
|
8
|
+
export class TypeormMongoAdapter implements IMongoAdapter<DataSource>, OnModuleDestroy {
|
|
9
|
+
constructor(@Inject("TYPEORM_DATA_SOURCE") private readonly dataSource: DataSource) {}
|
|
10
|
+
|
|
11
|
+
collection<T = unknown>(name: string): Collection<T & Document> {
|
|
12
|
+
const databaseName = this.dataSource.options.database;
|
|
13
|
+
return this.dataSource.mongoManager.mongoQueryRunner.databaseConnection
|
|
14
|
+
.db(typeof databaseName === "string" ? databaseName : undefined)
|
|
15
|
+
.collection(name) as unknown as Collection<T & Document>;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
getClient(): DataSource {
|
|
19
|
+
return this.dataSource;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
async onModuleDestroy(): Promise<void> {
|
|
23
|
+
if (this.dataSource.isInitialized) {
|
|
24
|
+
await this.dataSource.destroy();
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -73,7 +73,11 @@ import sql from 'mssql';
|
|
|
73
73
|
{{/if}}
|
|
74
74
|
|
|
75
75
|
{{#if useTypeorm}}
|
|
76
|
+
{{#if hasMongodb}}
|
|
77
|
+
import { TypeormMongoAdapter } from './adapters/mongo/typeorm-mongo.adapter.js';
|
|
78
|
+
{{else}}
|
|
76
79
|
import { TypeormSqlAdapter } from './adapters/sql/typeorm-sql.adapter.js';
|
|
80
|
+
{{/if}}
|
|
77
81
|
import { AppDataSource } from "./orm/typeorm/data-source.js";
|
|
78
82
|
{{/if}}
|
|
79
83
|
|
|
@@ -159,8 +163,7 @@ export class DbModule {
|
|
|
159
163
|
providers.push({
|
|
160
164
|
provide: 'PRISMA_SQL_CLIENT',
|
|
161
165
|
useValue: prismaSql,
|
|
162
|
-
}
|
|
163
|
-
providers.push({
|
|
166
|
+
}, {
|
|
164
167
|
provide: 'SQL_ADAPTER',
|
|
165
168
|
useClass: PrismaSqlAdapter,
|
|
166
169
|
});
|
|
@@ -177,8 +180,7 @@ export class DbModule {
|
|
|
177
180
|
providers.push({
|
|
178
181
|
provide: 'PRISMA_MONGO_CLIENT',
|
|
179
182
|
useValue: prismaMongo,
|
|
180
|
-
}
|
|
181
|
-
providers.push({
|
|
183
|
+
}, {
|
|
182
184
|
provide: 'MONGO_ADAPTER',
|
|
183
185
|
useClass: PrismaMongoAdapter,
|
|
184
186
|
});
|
|
@@ -194,15 +196,15 @@ export class DbModule {
|
|
|
194
196
|
return drizzle({ client, schema });
|
|
195
197
|
{{else if hasMysql}}
|
|
196
198
|
const client = mysql.createPool({ uri: options.mysqlUrl });
|
|
197
|
-
return drizzle(client, {
|
|
199
|
+
return drizzle(client, { mode: "default", schema });
|
|
198
200
|
{{else if hasSqlserver}}
|
|
199
201
|
const client = await new sql.ConnectionPool({
|
|
200
|
-
server: options.sqlserver?.server ?? 'localhost',
|
|
201
|
-
port: options.sqlserver?.port ?? 1433,
|
|
202
202
|
database: options.sqlserver?.database ?? 'app_db',
|
|
203
|
-
user: options.sqlserver?.user ?? 'sa',
|
|
204
|
-
password: options.sqlserver?.password ?? 'DevOnly_Strong_Password_2026!X9',
|
|
205
203
|
options: options.sqlserver?.options ?? { encrypt: true, trustServerCertificate: true },
|
|
204
|
+
password: options.sqlserver?.password ?? 'DevOnly_Strong_Password_2026!X9',
|
|
205
|
+
port: options.sqlserver?.port ?? 1433,
|
|
206
|
+
server: options.sqlserver?.server ?? 'localhost',
|
|
207
|
+
user: options.sqlserver?.user ?? 'sa',
|
|
206
208
|
}).connect();
|
|
207
209
|
return drizzle({ client, schema });
|
|
208
210
|
{{else}}
|
|
@@ -210,18 +212,21 @@ export class DbModule {
|
|
|
210
212
|
return drizzle({ client, schema });
|
|
211
213
|
{{/if}}
|
|
212
214
|
},
|
|
213
|
-
});
|
|
214
|
-
providers.push({ provide: 'SQL_ADAPTER', useClass: DrizzleSqlAdapter });
|
|
215
|
+
}, { provide: 'SQL_ADAPTER', useClass: DrizzleSqlAdapter });
|
|
215
216
|
{{/if}}
|
|
216
217
|
|
|
217
218
|
{{#if useTypeorm}}
|
|
218
219
|
providers.push({
|
|
219
220
|
provide: 'TYPEORM_DATA_SOURCE',
|
|
220
221
|
useFactory: () => AppDataSource.initialize(),
|
|
221
|
-
}
|
|
222
|
-
|
|
222
|
+
}, {
|
|
223
|
+
{{#if hasMongodb}}
|
|
224
|
+
provide: 'MONGO_ADAPTER',
|
|
225
|
+
useClass: TypeormMongoAdapter,
|
|
226
|
+
{{else}}
|
|
223
227
|
provide: 'SQL_ADAPTER',
|
|
224
228
|
useClass: TypeormSqlAdapter,
|
|
229
|
+
{{/if}}
|
|
225
230
|
});
|
|
226
231
|
{{/if}}
|
|
227
232
|
|
|
@@ -260,12 +265,13 @@ export class DbModule {
|
|
|
260
265
|
{{/if}}
|
|
261
266
|
|
|
262
267
|
{{#if hasMongodb}}
|
|
263
|
-
if (!options.mongodbUrl)
|
|
268
|
+
if (!options.mongodbUrl) {
|
|
269
|
+
throw new Error("A MongoDB URL is required");
|
|
270
|
+
}
|
|
264
271
|
providers.push({
|
|
265
272
|
provide: 'MONGODB_CLIENT',
|
|
266
273
|
useFactory: () => new MongoClient(options.mongodbUrl!, { serverSelectionTimeoutMS: 5000 }),
|
|
267
|
-
}
|
|
268
|
-
providers.push({
|
|
274
|
+
}, {
|
|
269
275
|
provide: 'MONGO_ADAPTER',
|
|
270
276
|
useClass: RawMongoAdapter,
|
|
271
277
|
});
|
|
@@ -61,6 +61,7 @@ import type * as schema from './orm/drizzle/schema.js';
|
|
|
61
61
|
{{#if useTypeorm}}
|
|
62
62
|
import type { DataSource } from 'typeorm';
|
|
63
63
|
{{/if}}
|
|
64
|
+
|
|
64
65
|
{{#if (or hasPostgres hasMysql hasSqlite hasSqlserver)}}
|
|
65
66
|
{{#if usePrisma}}
|
|
66
67
|
type SqlClient = PrismaClient;
|
|
@@ -78,6 +79,8 @@ type SqlClient = unknown;
|
|
|
78
79
|
{{#if hasMongodb}}
|
|
79
80
|
{{#if usePrisma}}
|
|
80
81
|
type MongoClient = PrismaClient;
|
|
82
|
+
{{else if useTypeorm}}
|
|
83
|
+
type MongoClient = DataSource;
|
|
81
84
|
{{else}}
|
|
82
85
|
type MongoClient = import('mongodb').Db;
|
|
83
86
|
{{/if}}
|
|
@@ -135,11 +138,13 @@ export class DbService {
|
|
|
135
138
|
{{/if}}
|
|
136
139
|
{{/if}}
|
|
137
140
|
{{#if useTypeorm}}
|
|
138
|
-
{{#if (or hasPostgres hasMysql hasSqlite hasSqlserver)}}
|
|
139
141
|
get typeorm(): DataSource {
|
|
142
|
+
{{#if hasMongodb}}
|
|
143
|
+
return this.mongo.getClient();
|
|
144
|
+
{{else}}
|
|
140
145
|
return this.sql.getClient();
|
|
146
|
+
{{/if}}
|
|
141
147
|
}
|
|
142
|
-
{{/if}}
|
|
143
148
|
{{/if}}
|
|
144
149
|
}
|
|
145
150
|
{{/if}}
|
|
@@ -6,26 +6,38 @@ import {
|
|
|
6
6
|
Column,
|
|
7
7
|
CreateDateColumn,
|
|
8
8
|
Entity,
|
|
9
|
+
{{#if hasMongodb}}
|
|
10
|
+
ObjectIdColumn,
|
|
11
|
+
{{else}}
|
|
9
12
|
PrimaryGeneratedColumn,
|
|
13
|
+
{{/if}}
|
|
10
14
|
UpdateDateColumn,
|
|
11
15
|
} from "typeorm";
|
|
16
|
+
{{#if hasMongodb}}
|
|
17
|
+
import type { ObjectId } from "mongodb";
|
|
18
|
+
{{/if}}
|
|
12
19
|
|
|
13
20
|
@Entity("todos")
|
|
14
21
|
export class Todo {
|
|
22
|
+
{{#if hasMongodb}}
|
|
23
|
+
@ObjectIdColumn()
|
|
24
|
+
id: ObjectId;
|
|
25
|
+
{{else}}
|
|
15
26
|
@PrimaryGeneratedColumn("{{#if hasSqlserver}}increment{{else}}uuid{{/if}}")
|
|
16
27
|
id: string;
|
|
28
|
+
{{/if}}
|
|
17
29
|
|
|
18
|
-
@Column({
|
|
30
|
+
@Column({ length: 255, nullable: true, type: "varchar" })
|
|
19
31
|
description?: string;
|
|
20
32
|
|
|
21
|
-
@Column({ type: "varchar"
|
|
33
|
+
@Column({ length: 255, type: "varchar" })
|
|
22
34
|
title: string;
|
|
23
35
|
|
|
24
36
|
@Column({ default: false, name: "is_completed", type: "{{#if hasSqlserver}}bit{{else}}boolean{{/if}}" })
|
|
25
37
|
completed: boolean;
|
|
26
38
|
|
|
27
39
|
{{#if hasAuth}}
|
|
28
|
-
@Column({ type: "varchar"
|
|
40
|
+
@Column({ nullable: true, type: "varchar" })
|
|
29
41
|
authorId?: string;
|
|
30
42
|
{{/if}}
|
|
31
43
|
|
|
@@ -1,15 +1,27 @@
|
|
|
1
1
|
// oxlint-disable typescript/no-explicit-any
|
|
2
2
|
{{#if hasPostgres}}
|
|
3
|
-
|
|
3
|
+
{{#if (or hasTodoExample isPassportAuth (eq auth "better-auth:stateless"))}}
|
|
4
|
+
import { {{#if hasTodoExample}}boolean, {{/if}}pgTable{{#if (or hasTodoExample (eq auth "better-auth:stateless"))}}, serial{{/if}}, text{{#if (or isPassportAuth (eq auth "better-auth:stateless"))}}, timestamp{{/if}} } from 'drizzle-orm/pg-core';
|
|
5
|
+
|
|
6
|
+
{{/if}}
|
|
4
7
|
{{/if}}
|
|
5
8
|
{{#if hasMysql}}
|
|
6
|
-
|
|
9
|
+
{{#if (or hasTodoExample isPassportAuth (eq auth "better-auth:stateless"))}}
|
|
10
|
+
import { {{#if hasTodoExample}}boolean, {{/if}}mysqlTable{{#if (or hasTodoExample (eq auth "better-auth:stateless"))}}, int{{/if}}, text{{#if (or isPassportAuth (eq auth "better-auth:stateless"))}}, timestamp, varchar{{/if}} } from 'drizzle-orm/mysql-core';
|
|
11
|
+
|
|
12
|
+
{{/if}}
|
|
7
13
|
{{/if}}
|
|
8
14
|
{{#if hasSqlite}}
|
|
9
|
-
|
|
15
|
+
{{#if (or hasTodoExample isPassportAuth (eq auth "better-auth:stateless"))}}
|
|
16
|
+
import { sqliteTable{{#if (or hasTodoExample (eq auth "better-auth:stateless"))}}, integer{{/if}}, text } from 'drizzle-orm/sqlite-core';
|
|
17
|
+
|
|
18
|
+
{{/if}}
|
|
10
19
|
{{/if}}
|
|
11
20
|
{{#if hasSqlserver}}
|
|
12
|
-
|
|
21
|
+
{{#if (or hasTodoExample isPassportAuth (eq auth "better-auth:stateless"))}}
|
|
22
|
+
import { {{#if hasTodoExample}}bit, {{/if}}{{#if (or hasTodoExample (eq auth "better-auth:stateless"))}}int, {{/if}}mssqlTable, nvarchar } from 'drizzle-orm/mssql-core';
|
|
23
|
+
|
|
24
|
+
{{/if}}
|
|
13
25
|
{{/if}}
|
|
14
26
|
{{#if (eq auth "better-auth")}}
|
|
15
27
|
// oxlint-disable-next-line oxc/no-barrel-file
|
|
@@ -18,18 +30,22 @@ export * from "./auth-schema.js";
|
|
|
18
30
|
{{#if hasPostgres}}
|
|
19
31
|
{{#if (or isPassportAuth (eq auth "better-auth:stateless"))}}
|
|
20
32
|
export const users = pgTable('users', {
|
|
33
|
+
{{#if (or isPassportAuth (eq auth "better-auth:stateless"))}}
|
|
34
|
+
createdAt: timestamp('created_at').defaultNow(),
|
|
35
|
+
{{/if}}
|
|
21
36
|
email: text('email').unique(),
|
|
22
37
|
{{#if isPassportAuth}}
|
|
38
|
+
firstname: text('firstname'),
|
|
23
39
|
id: text('id').primaryKey(),
|
|
24
40
|
{{else}}
|
|
25
41
|
id: serial('id').primaryKey(),
|
|
42
|
+
{{/if}}
|
|
43
|
+
{{#if isPassportAuth}}
|
|
44
|
+
lastname: text('lastname'),
|
|
26
45
|
{{/if}}
|
|
27
46
|
name: text('name'),
|
|
28
|
-
createdAt: timestamp('created_at').defaultNow(),
|
|
29
47
|
{{#if isPassportAuth}}
|
|
30
48
|
password: text('password').notNull(),
|
|
31
|
-
firstname: text('firstname'),
|
|
32
|
-
lastname: text('lastname'),
|
|
33
49
|
role: text('role').default('USER'),
|
|
34
50
|
{{/if}}
|
|
35
51
|
});
|
|
@@ -49,18 +65,22 @@ export const todos = pgTable('todos', {
|
|
|
49
65
|
{{#if hasMysql}}
|
|
50
66
|
{{#if (or isPassportAuth (eq auth "better-auth:stateless"))}}
|
|
51
67
|
export const users = mysqlTable('users', {
|
|
68
|
+
{{#if (or isPassportAuth (eq auth "better-auth:stateless"))}}
|
|
69
|
+
createdAt: timestamp('created_at').defaultNow(),
|
|
70
|
+
{{/if}}
|
|
52
71
|
email: varchar('email', { length: 320 }).unique(),
|
|
53
72
|
{{#if isPassportAuth}}
|
|
73
|
+
firstname: text('firstname'),
|
|
54
74
|
id: varchar('id', { length: 255 }).primaryKey(),
|
|
55
75
|
{{else}}
|
|
56
76
|
id: int('id').autoincrement().primaryKey(),
|
|
77
|
+
{{/if}}
|
|
78
|
+
{{#if isPassportAuth}}
|
|
79
|
+
lastname: text('lastname'),
|
|
57
80
|
{{/if}}
|
|
58
81
|
name: text('name'),
|
|
59
|
-
createdAt: timestamp('created_at').defaultNow(),
|
|
60
82
|
{{#if isPassportAuth}}
|
|
61
83
|
password: text('password').notNull(),
|
|
62
|
-
firstname: text('firstname'),
|
|
63
|
-
lastname: text('lastname'),
|
|
64
84
|
role: text('role').default('USER'),
|
|
65
85
|
{{/if}}
|
|
66
86
|
});
|
|
@@ -82,15 +102,17 @@ export const todos = mysqlTable('todos', {
|
|
|
82
102
|
export const users = sqliteTable('users', {
|
|
83
103
|
email: text('email').unique(),
|
|
84
104
|
{{#if isPassportAuth}}
|
|
105
|
+
firstname: text('firstname'),
|
|
85
106
|
id: text('id').primaryKey(),
|
|
86
107
|
{{else}}
|
|
87
108
|
id: integer('id').primaryKey({ autoIncrement: true }),
|
|
109
|
+
{{/if}}
|
|
110
|
+
{{#if isPassportAuth}}
|
|
111
|
+
lastname: text('lastname'),
|
|
88
112
|
{{/if}}
|
|
89
113
|
name: text('name'),
|
|
90
114
|
{{#if isPassportAuth}}
|
|
91
115
|
password: text('password').notNull(),
|
|
92
|
-
firstname: text('firstname'),
|
|
93
|
-
lastname: text('lastname'),
|
|
94
116
|
role: text('role').default('USER'),
|
|
95
117
|
{{/if}}
|
|
96
118
|
});
|
|
@@ -112,15 +134,17 @@ export const todos = sqliteTable('todos', {
|
|
|
112
134
|
export const users = mssqlTable('users', {
|
|
113
135
|
email: nvarchar('email', { length: 320 }).unique(),
|
|
114
136
|
{{#if isPassportAuth}}
|
|
137
|
+
firstname: nvarchar('firstname', { length: 255 }),
|
|
115
138
|
id: nvarchar('id', { length: 255 }).primaryKey(),
|
|
116
139
|
{{else}}
|
|
117
140
|
id: int('id').identity().primaryKey(),
|
|
141
|
+
{{/if}}
|
|
142
|
+
{{#if isPassportAuth}}
|
|
143
|
+
lastname: nvarchar('lastname', { length: 255 }),
|
|
118
144
|
{{/if}}
|
|
119
145
|
name: nvarchar('name', { length: 255 }),
|
|
120
146
|
{{#if isPassportAuth}}
|
|
121
147
|
password: nvarchar('password', { length: 255 }).notNull(),
|
|
122
|
-
firstname: nvarchar('firstname', { length: 255 }),
|
|
123
|
-
lastname: nvarchar('lastname', { length: 255 }),
|
|
124
148
|
role: nvarchar('role', { length: 32 }).default('USER'),
|
|
125
149
|
{{/if}}
|
|
126
150
|
});
|
|
@@ -7,7 +7,7 @@ import { env } from "@repo/env/api-gateway";
|
|
|
7
7
|
import { env } from "../../../config/env.js";
|
|
8
8
|
{{/if}}
|
|
9
9
|
import { DataSource } from "typeorm";
|
|
10
|
-
{{#if hasTodoExample}}
|
|
10
|
+
{{#if (or hasTodoExample hasMongodb)}}
|
|
11
11
|
import { Todo } from "../../entities/todo.entity.js";
|
|
12
12
|
{{/if}}
|
|
13
13
|
{{#if (and useTypeorm isPassportAuth)}}
|
|
@@ -17,7 +17,7 @@ import { UserEntity } from "../../../modules/users/entities/user.entity.js";
|
|
|
17
17
|
import { AppEntity } from "../../entities/app.entity.js";
|
|
18
18
|
{{/if}}
|
|
19
19
|
|
|
20
|
-
{{#unless (or hasSqlite hasSqlserver)}}
|
|
20
|
+
{{#unless (or hasSqlite hasSqlserver hasMongodb)}}
|
|
21
21
|
const databaseUrl = env.{{#if hasPostgres}}POSTGRES_URL{{/if}}{{#if hasMysql}}MYSQL_URL{{/if}};
|
|
22
22
|
const parsedUrl = new URL(databaseUrl);
|
|
23
23
|
{{/unless}}
|
|
@@ -31,7 +31,10 @@ export const AppDataSource = new DataSource({
|
|
|
31
31
|
database: env.SQLSERVER_DATABASE,
|
|
32
32
|
{{/if}}
|
|
33
33
|
// Register entities explicitly for consistent metadata in Node and test runners.
|
|
34
|
-
entities: [{{#if hasTodoExample}}Todo{{/if}}{{#if (and hasTodoExample (and useTypeorm isPassportAuth))}}, {{/if}}{{#if (and useTypeorm isPassportAuth)}}UserEntity{{/if}}{{#if (eq auth "better-auth:stateless")}}{{#if (or hasTodoExample (and useTypeorm isPassportAuth))}}, {{/if}}AppEntity{{/if}}],
|
|
34
|
+
entities: [{{#if (or hasTodoExample hasMongodb)}}Todo{{/if}}{{#if (and hasTodoExample (and useTypeorm isPassportAuth))}}, {{/if}}{{#if (and useTypeorm isPassportAuth)}}UserEntity{{/if}}{{#if (eq auth "better-auth:stateless")}}{{#if (or hasTodoExample (and useTypeorm isPassportAuth))}}, {{/if}}AppEntity{{/if}}],
|
|
35
|
+
{{#if hasMongodb}}
|
|
36
|
+
extra: { serverSelectionTimeoutMS: 5000 },
|
|
37
|
+
{{/if}}
|
|
35
38
|
{{#if hasSqlserver}}
|
|
36
39
|
host: env.SQLSERVER_HOST,
|
|
37
40
|
{{/if}}
|
|
@@ -45,14 +48,17 @@ export const AppDataSource = new DataSource({
|
|
|
45
48
|
port: env.SQLSERVER_PORT,
|
|
46
49
|
{{/if}}
|
|
47
50
|
synchronize: false,
|
|
48
|
-
type: "{{#if hasPostgres}}postgres{{/if}}{{#if hasMysql}}mysql{{/if}}{{#if hasSqlite}}better-sqlite3{{/if}}{{#if hasSqlserver}}mssql{{/if}}",
|
|
51
|
+
type: "{{#if hasPostgres}}postgres{{/if}}{{#if hasMysql}}mysql{{/if}}{{#if hasSqlite}}better-sqlite3{{/if}}{{#if hasSqlserver}}mssql{{/if}}{{#if hasMongodb}}mongodb{{/if}}",
|
|
52
|
+
{{#if hasMongodb}}
|
|
53
|
+
url: env.MONGODB_URL,
|
|
54
|
+
{{/if}}
|
|
49
55
|
{{#if hasSqlserver}}
|
|
50
56
|
username: env.SQLSERVER_USER,
|
|
51
57
|
{{/if}}
|
|
52
|
-
{{#unless (or hasSqlite hasSqlserver)}}
|
|
58
|
+
{{#unless (or hasSqlite hasSqlserver hasMongodb)}}
|
|
53
59
|
url: databaseUrl,
|
|
54
60
|
{{/unless}}
|
|
55
|
-
{{#unless (or hasSqlite hasSqlserver)}}
|
|
61
|
+
{{#unless (or hasSqlite hasSqlserver hasMongodb)}}
|
|
56
62
|
...(parsedUrl.username ? { username: decodeURIComponent(parsedUrl.username) } : {}),
|
|
57
63
|
...(parsedUrl.password ? { password: decodeURIComponent(parsedUrl.password) } : {}),
|
|
58
64
|
{{/unless}}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
{{#if hasRateLimiting}}
|
|
2
2
|
import { Inject, Injectable } from "@nestjs/common";
|
|
3
|
-
import { ThrottlerStorage
|
|
3
|
+
import { ThrottlerStorage } from "@nestjs/throttler";
|
|
4
|
+
import type { ThrottlerStorage as ThrottlerStorageService } from "@nestjs/throttler";
|
|
4
5
|
import { TRPCError } from "@trpc/server";
|
|
5
6
|
import {
|
|
6
7
|
MiddlewareOptions,
|
|
@@ -40,6 +40,9 @@ import { eq } from "drizzle-orm";
|
|
|
40
40
|
{{else if (and hasHealthCheck hasDatabase (or usePrisma useTypeorm useDrizzle))}}
|
|
41
41
|
import { DbService } from "../src/database/db.service.js";
|
|
42
42
|
{{/if}}
|
|
43
|
+
{{#if (and hasMongodb useTypeorm hasHealthCheck)}}
|
|
44
|
+
import { Todo } from "../src/database/entities/todo.entity.js";
|
|
45
|
+
{{/if}}
|
|
43
46
|
|
|
44
47
|
describe("AppController (e2e)", () => {
|
|
45
48
|
let app: INestApplication<App>;
|
|
@@ -232,8 +235,8 @@ describe("AppController (e2e)", () => {
|
|
|
232
235
|
{{#if useSwaggerScalar}}
|
|
233
236
|
app.use("/docs", apiReference({
|
|
234
237
|
content: generatedOpenApi,
|
|
235
|
-
{{#if (eq httpProvider "fastify")}}withFastify: true,{{/if}}
|
|
236
238
|
theme: "default",
|
|
239
|
+
{{#if (eq httpProvider "fastify")}}withFastify: true,{{/if}}
|
|
237
240
|
}));
|
|
238
241
|
{{/if}}
|
|
239
242
|
{{/if}}
|
|
@@ -418,8 +421,8 @@ describe("AppController (e2e)", () => {
|
|
|
418
421
|
{{#if (and hasHealthCheck hasDatabase (or usePrisma useTypeorm useDrizzle))}}
|
|
419
422
|
it("reports database readiness failure with HTTP 503", async () => {
|
|
420
423
|
const db = app.get(DbService);
|
|
421
|
-
const database = {{#if hasMongodb}}db.raw.mongo{{else if isPrisma8}}db.prisma.orm.public.Todo{{else if usePrisma}}db.prisma{{else if useTypeorm}}db.typeorm{{else}}db.drizzle{{/if}};
|
|
422
|
-
const query = vi.spyOn(database, {{#if hasMongodb}}{{#if usePrisma}}"$runCommandRaw"{{else}}"command"{{/if}}{{else if isPrisma8}}"all"{{else if usePrisma}}"$queryRaw"{{else if useTypeorm}}"query"{{else if hasSqlite}}"get"{{else}}"execute"{{/if}})
|
|
424
|
+
const database = {{#if hasMongodb}}{{#if useTypeorm}}db.typeorm.getMongoRepository(Todo){{else}}db.raw.mongo{{/if}}{{else if isPrisma8}}db.prisma.orm.public.Todo{{else if usePrisma}}db.prisma{{else if useTypeorm}}db.typeorm{{else}}db.drizzle{{/if}};
|
|
425
|
+
const query = vi.spyOn(database, {{#if hasMongodb}}{{#if usePrisma}}"$runCommandRaw"{{else if useTypeorm}}"countDocuments"{{else}}"command"{{/if}}{{else if isPrisma8}}"all"{{else if usePrisma}}"$queryRaw"{{else if useTypeorm}}"query"{{else if hasSqlite}}"get"{{else}}"execute"{{/if}})
|
|
423
426
|
.mockRejectedValue(new Error("scenario database outage"));
|
|
424
427
|
try {
|
|
425
428
|
const response = await request(getRequestTarget()).get("/health").expect(503);
|
|
@@ -434,7 +437,8 @@ describe("AppController (e2e)", () => {
|
|
|
434
437
|
{{#if hasTrpc}}
|
|
435
438
|
it("executes a tRPC query through HTTP", async () => {
|
|
436
439
|
const response = await request(getRequestTarget())
|
|
437
|
-
.get("/trpc/appRouter.getHello")
|
|
440
|
+
.get("/trpc/appRouter.getHello")
|
|
441
|
+
.expect(200);
|
|
438
442
|
expect(response.body.result.data).toBe("Hello World!");
|
|
439
443
|
});
|
|
440
444
|
{{/if}}
|
|
@@ -464,10 +468,10 @@ describe("AppController (e2e)", () => {
|
|
|
464
468
|
.set("Origin", "http://localhost:3000").expect(302);
|
|
465
469
|
const cookies = session.headers["set-cookie"] as string[];
|
|
466
470
|
expect(cookies).toBeTruthy();
|
|
467
|
-
const created = await call("create", { title: "OIDC tRPC todo"
|
|
471
|
+
const created = await call("create", { completed: false, title: "OIDC tRPC todo" }, cookies).expect(200);
|
|
468
472
|
const todo = (created.body.result?.data?.json ?? created.body.result?.data) as { id: string | number };
|
|
469
473
|
await call("findAll", undefined, cookies).expect(200);
|
|
470
|
-
await call("update", {
|
|
474
|
+
await call("update", { data: { completed: true }, id: todo.id }, cookies).expect(200);
|
|
471
475
|
await call("delete", { id: todo.id }, cookies).expect(200);
|
|
472
476
|
});
|
|
473
477
|
{{else if (and hasTrpc hasTodoExample hasAuth)}}
|
|
@@ -530,19 +534,19 @@ describe("AppController (e2e)", () => {
|
|
|
530
534
|
const server = app.getHttpServer();
|
|
531
535
|
const create = await request(getRequestTarget(server))
|
|
532
536
|
.post("/trpc/todoRouter.create")
|
|
533
|
-
.send({ title: "Public tRPC todo"
|
|
537
|
+
.send({ completed: false, title: "Public tRPC todo" })
|
|
534
538
|
.expect(200);
|
|
535
539
|
const created = create.body.result?.data?.json ?? create.body.result?.data;
|
|
536
540
|
expect(created.title).toBe("Public tRPC todo");
|
|
537
541
|
const id = created.id as string | number;
|
|
538
542
|
|
|
539
543
|
const list = await request(getRequestTarget(server)).get("/trpc/todoRouter.findAll").expect(200);
|
|
540
|
-
const
|
|
541
|
-
expect(
|
|
544
|
+
const todoResults = list.body.result?.data?.json ?? list.body.result?.data;
|
|
545
|
+
expect(todoResults).toEqual(expect.arrayContaining([expect.objectContaining({ id })]));
|
|
542
546
|
|
|
543
547
|
const update = await request(getRequestTarget(server))
|
|
544
548
|
.post("/trpc/todoRouter.update")
|
|
545
|
-
.send({
|
|
549
|
+
.send({ data: { completed: true }, id })
|
|
546
550
|
.expect(200);
|
|
547
551
|
const updated = update.body.result?.data?.json ?? update.body.result?.data;
|
|
548
552
|
expect(updated.completed).toBe(true);
|
|
@@ -5,6 +5,9 @@ import { AllowAnonymous } from "@thallesp/nestjs-better-auth";
|
|
|
5
5
|
{{/if}}
|
|
6
6
|
{{#if hasDatabase}}
|
|
7
7
|
import { DbService } from "../database/db.service.js";
|
|
8
|
+
{{#if (and hasMongodb useTypeorm)}}
|
|
9
|
+
import { Todo } from "../database/entities/todo.entity.js";
|
|
10
|
+
{{/if}}
|
|
8
11
|
{{#if useDrizzle}}
|
|
9
12
|
import { sql } from "drizzle-orm";
|
|
10
13
|
{{/if}}
|
|
@@ -31,6 +34,8 @@ export class HealthController {
|
|
|
31
34
|
{{#if hasMongodb}}
|
|
32
35
|
{{#if usePrisma}}
|
|
33
36
|
await this.db.raw.mongo.$runCommandRaw({ ping: 1 });
|
|
37
|
+
{{else if useTypeorm}}
|
|
38
|
+
await this.db.typeorm.getMongoRepository(Todo).countDocuments({}, { limit: 1 });
|
|
34
39
|
{{else}}
|
|
35
40
|
await this.db.raw.mongo.command({ ping: 1 });
|
|
36
41
|
{{/if}}
|
|
@@ -12,16 +12,16 @@ import { CreateTodoDto } from "./dto/create-todo.dto.js";
|
|
|
12
12
|
import { UpdateTodoDto } from "./dto/update-todo.dto.js";
|
|
13
13
|
|
|
14
14
|
const todoSchema = z.object({
|
|
15
|
+
authorId: z.string().nullable().optional(),
|
|
16
|
+
completed: z.boolean(),
|
|
17
|
+
description: z.string().nullable().optional(),
|
|
15
18
|
id: z.union([z.string(), z.number()]),
|
|
16
19
|
title: z.string(),
|
|
17
|
-
description: z.string().nullable().optional(),
|
|
18
|
-
completed: z.boolean(),
|
|
19
|
-
authorId: z.string().nullable().optional(),
|
|
20
20
|
});
|
|
21
21
|
|
|
22
22
|
const updateTodoInput = z.object({
|
|
23
|
-
id: z.union([z.string(), z.number()]),
|
|
24
23
|
data: UpdateTodoDto.schema,
|
|
24
|
+
id: z.union([z.string(), z.number()]),
|
|
25
25
|
});
|
|
26
26
|
|
|
27
27
|
@Router({ alias: "todoRouter" })
|