@nest-arch/tui 0.4.0 → 0.4.3

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.
Files changed (64) hide show
  1. package/dist/cli.js +1 -1
  2. package/dist/{constants-C3xmDWeK.js → constants-B5MuV-iH.js} +1 -1
  3. package/dist/index.js +188 -134
  4. package/dist/templates/addons/husky/pre-commit.hbs +11 -26
  5. package/dist/templates/base/.env.example.hbs +9 -0
  6. package/dist/templates/base/.env.hbs +7 -0
  7. package/dist/templates/base/README.md.hbs +16 -0
  8. package/dist/templates/base/pnpm-workspace.yaml.hbs +6 -0
  9. package/dist/templates/base/src/app.module.ts.hbs +68 -1
  10. package/dist/templates/base/src/app.router.ts.hbs +1 -2
  11. package/dist/templates/base/src/database/adapters/mongo/prisma-mongo.adapter.ts.hbs +11 -3
  12. package/dist/templates/base/src/database/adapters/sql/prisma-sql.adapter.ts.hbs +1 -1
  13. package/dist/templates/base/src/database/db.module.ts.hbs +24 -8
  14. package/dist/templates/base/src/database/db.service.ts.hbs +20 -0
  15. package/dist/templates/base/src/database/entities/app.entity.ts.hbs +25 -0
  16. package/dist/templates/base/src/database/index.ts.hbs +19 -0
  17. package/dist/templates/base/src/database/orm/drizzle/schema.ts.hbs +42 -11
  18. package/dist/templates/base/src/database/orm/typeorm/data-source.ts.hbs +12 -1
  19. package/dist/templates/base/src/libs/middlewares/auth.trpc.middleware.ts.hbs +47 -0
  20. package/dist/templates/base/src/libs/middlewares/rate-limit.trpc.middleware.ts.hbs +43 -0
  21. package/dist/templates/base/src/libs/trpc/inner.context.ts.hbs +6 -3
  22. package/dist/templates/base/src/main.ts.hbs +13 -4
  23. package/dist/templates/base/src/modules/users/entities/user.entity.ts.hbs +22 -0
  24. package/dist/templates/base/test/app.e2e-spec.ts.hbs +670 -37
  25. package/dist/templates/base/test/bootstrap.e2e-spec.ts.hbs +11 -1
  26. package/dist/templates/extras/docker/.dockerignore.hbs +8 -1
  27. package/dist/templates/extras/docker/Dockerfile.hbs +8 -10
  28. package/dist/templates/extras/health/src/health/health.controller.ts.hbs +54 -0
  29. package/dist/templates/extras/health/src/health/health.module.ts.hbs +9 -0
  30. package/dist/templates/extras/swagger/create-docs.ts.hbs +9 -3
  31. package/dist/templates/feature/graphql/src/graphql/graphql.module.ts.hbs +6 -17
  32. package/dist/templates/feature/graphql/src/graphql/orm/drizzle/drizzle-graphql.module.ts.hbs +3 -2
  33. package/dist/templates/feature/graphql/src/graphql/resolvers/health.resolver.ts.hbs +14 -0
  34. package/dist/templates/feature/graphql/src/graphql/resolvers/todo.resolver.ts.hbs +165 -53
  35. package/dist/templates/feature/rest/todo-example/src/api/todo/dto/create-todo.dto.ts.hbs +12 -0
  36. package/dist/templates/feature/rest/todo-example/src/api/todo/dto/update-todo.dto.ts.hbs +12 -0
  37. package/dist/templates/feature/rest/todo-example/src/api/todo/todo.controller.ts.hbs +18 -8
  38. package/dist/templates/feature/rest/todo-example/src/api/todo/todo.router.ts.hbs +41 -13
  39. package/dist/templates/feature/rest/todo-example/src/api/todo/todo.service.ts.hbs +68 -10
  40. package/dist/templates/monorepo/apps/api-gateway/package.json.hbs +2 -2
  41. package/dist/templates/monorepo/apps/api-gateway/src/app.controller.spec.ts.hbs +5 -0
  42. package/dist/templates/monorepo/apps/api-gateway/src/app.controller.ts.hbs +6 -0
  43. package/dist/templates/monorepo/apps/api-gateway/src/app.module.ts.hbs +32 -1
  44. package/dist/templates/monorepo/apps/api-gateway/src/app.service.ts.hbs +1 -1
  45. package/dist/templates/monorepo/apps/api-gateway/src/main.ts.hbs +13 -4
  46. package/dist/templates/monorepo/apps/api-gateway/test/app.e2e-spec.ts.hbs +26 -1
  47. package/dist/templates/monorepo/feature/graphql/src/graphql/graphql.module.ts.hbs +6 -10
  48. package/dist/templates/monorepo/feature/graphql/src/graphql/resolvers/health.resolver.ts.hbs +14 -0
  49. package/dist/templates/monorepo/feature/graphql/src/graphql/resolvers/todo.resolver.ts.hbs +147 -47
  50. package/dist/templates/monorepo/packages/env/src/api-gateway.ts.hbs +7 -0
  51. package/dist/templates/monorepo/packages/shared/package.json.hbs +1 -0
  52. package/dist/templates/monorepo/packages/shared/src/auth.ts.hbs +6 -0
  53. package/dist/templates/monorepo/root/pnpm-workspace.yaml.hbs +10 -2
  54. package/dist/templates/single-project/architecture/nest-api/.env.hbs +6 -0
  55. package/dist/templates/single-project/architecture/nest-api/package.json.hbs +1 -1
  56. package/dist/templates/single-project/architecture/nest-api/src/main.ts.hbs +13 -4
  57. package/dist/templates/single-project/architecture/nest-gateway/src/main.ts.hbs +3 -3
  58. package/dist/templates/single-project/architecture/nest-microservice/proto/app.proto.hbs +13 -0
  59. package/dist/templates/single-project/architecture/nest-microservice/src/app.controller.ts.hbs +10 -4
  60. package/dist/templates/single-project/architecture/nest-microservice/src/app.service.ts.hbs +2 -2
  61. package/dist/templates/single-project/architecture/nest-microservice/src/main.ts.hbs +3 -3
  62. package/dist/templates/single-project/architecture/nest-microservice/test/microservice.e2e-spec.ts.hbs +7 -1
  63. package/dist/templates/single-project/shared/config/env.ts.hbs +12 -1
  64. package/package.json +2 -2
@@ -1,28 +1,39 @@
1
+ {{#if hasDatabase}}
1
2
  {{#if (and hasAuth hasDatabase)}}
2
- import { UseGuards } from "@nestjs/common";
3
+ import { BadRequestException, NotFoundException, UseGuards } from "@nestjs/common";
4
+ {{else}}
5
+ import { BadRequestException, NotFoundException } from "@nestjs/common";
3
6
  {{/if}}
4
- import { Args, Mutation, Query, Resolver } from "@nestjs/graphql";
5
7
  {{#if (and hasAuth hasDatabase)}}
6
8
  import { UserEntity } from "../../auth/decorators/index.js";
7
9
  import { GqlAuthGuard } from "../../auth/guards/gql-auth.guard.js";
8
10
  {{/if}}
9
- import { DbService } from "@repo/db";
10
- import { CreateTodoInput } from "../dto/create-todo.input.js";
11
- import { UpdateTodoInput } from "../dto/update-todo.input.js";
11
+ import { Args, Mutation, Query, Resolver } from "@nestjs/graphql";
12
12
  {{#if useTypeorm}}
13
- import { GraphRelationBuilder } from "typeorm-relations-graphql";
14
13
  import { GraphQLResolveInfo } from "graphql";
14
+ import { GraphRelationBuilder } from "typeorm-relations-graphql";
15
+ import { Todo as TodoEntity } from "@repo/db/src/entities/todo.entity.js";
15
16
  {{/if}}
16
17
  {{#if useDrizzle}}
18
+ import { and, eq } from "drizzle-orm";
17
19
  import * as schema from "@repo/db/src/orm/drizzle/schema.js";
18
20
  {{/if}}
19
- {{#if (and hasAuth hasDatabase)}}
21
+ import { DbService } from "@repo/db";
22
+ import { CreateTodoInput } from "../dto/create-todo.input.js";
23
+ import { UpdateTodoInput } from "../dto/update-todo.input.js";
24
+ import { Todo } from "../models/todo.model.js";
25
+ {{else}}
26
+ import { Args, Mutation, Query, Resolver } from "@nestjs/graphql";
27
+ import { CreateTodoInput } from "../dto/create-todo.input.js";
28
+ import { UpdateTodoInput } from "../dto/update-todo.input.js";
20
29
  import { Todo } from "../models/todo.model.js";
21
30
  {{/if}}
22
31
 
23
32
  @Resolver(() => Todo)
24
33
  export class TodoResolver {
34
+ {{#if hasDatabase}}
25
35
  constructor(private readonly db: DbService) {}
36
+ {{/if}}
26
37
 
27
38
  @Query(() => String)
28
39
  hello(): string {
@@ -30,93 +41,182 @@ export class TodoResolver {
30
41
  }{{#if hasDatabase}}
31
42
 
32
43
  @Query(() => [Todo])
33
- todos({{#if useTypeorm}}info: GraphQLResolveInfo{{/if}}) {
44
+ {{#if hasAuth}}
45
+ @UseGuards(GqlAuthGuard)
46
+ {{/if}}
47
+ todos({{#if useTypeorm}}info: GraphQLResolveInfo, {{/if}}{{#if hasAuth}}@UserEntity() user: { id: string }{{/if}}) {
34
48
  {{#if useTypeorm}}
35
- const builder = new GraphRelationBuilder(this.db.getDataSource());
49
+ const builder = new GraphRelationBuilder(this.db.typeorm);
36
50
  const relations = builder.buildForQuery(Todo, info, "todos");
37
- return this.db.getRepository(Todo).find({ relations });
51
+ {{#if hasAuth}}
52
+ return this.db.typeorm.getRepository(TodoEntity).find({ relations, where: { authorId: user.id } });
53
+ {{else}}
54
+ return this.db.typeorm.getRepository(TodoEntity).find({ relations });
55
+ {{/if}}
38
56
  {{else if isPrisma8}}
57
+ {{#if hasAuth}}
58
+ return ((this.db.prisma.orm.public as any).Todo).where({ authorId: user.id }).all();
59
+ {{else}}
39
60
  return ((this.db.prisma.orm.public as any).Todo).all();
61
+ {{/if}}
40
62
  {{else if usePrisma}}
63
+ {{#if hasAuth}}
64
+ return this.db.prisma.todo.findMany({ where: { authorId: user.id } });
65
+ {{else}}
41
66
  return this.db.prisma.todo.findMany();
67
+ {{/if}}
42
68
  {{else if useDrizzle}}
43
- return this.db.select().from(schema.todos);
69
+ {{#if hasAuth}}
70
+ return this.db.drizzle.select().from(schema.todos).where(eq(schema.todos.authorId, user.id));
71
+ {{else}}
72
+ return this.db.drizzle.select().from(schema.todos);
73
+ {{/if}}
44
74
  {{else}}
45
75
  return [];
46
76
  {{/if}}
47
77
  }
48
78
 
79
+ {{#if hasAuth}}
49
80
  @UseGuards(GqlAuthGuard)
81
+ {{/if}}
50
82
  @Mutation(() => Todo)
51
- createTodo(
83
+ async createTodo(
84
+ {{#if hasAuth}}
52
85
  @UserEntity() user: { id: string },
86
+ {{/if}}
53
87
  @Args("input") input: CreateTodoInput{{#if useTypeorm}}, info: GraphQLResolveInfo{{/if}}
54
88
  ) {
55
- {{#if useTypeorm}}
56
- const entity = this.db.getRepository(Todo).create(input);
57
- const saved = await this.db.getRepository(Todo).save(entity);
58
- const builder = new GraphRelationBuilder(this.db.getDataSource());
59
- const relations = builder.buildForQuery(Todo, info, "createTodo");
60
- return this.db.getRepository(Todo).findOneOrFail({
61
- where: { id: saved.id },
62
- relations,
63
- });
64
- {{else if isPrisma8}}
89
+ if (!input.title.trim()) throw new BadRequestException("Title must not be empty");
90
+ {{#if isPrisma8}}
91
+ {{#if hasAuth}}
65
92
  return ((this.db.prisma.orm.public as any).Todo).create({ ...input, authorId: user.id });
93
+ {{else}}
94
+ return ((this.db.prisma.orm.public as any).Todo).create(input);
95
+ {{/if}}
66
96
  {{else if usePrisma}}
67
- return this.db.prisma.todo.create({
68
- data: { ...input, authorId: user.id },
97
+ {{#if hasAuth}}
98
+ const todo = await this.db.prisma.todo.create({ data: { ...input, authorId: user.id } });
99
+ {{else}}
100
+ const todo = await this.db.prisma.todo.create({ data: input });
101
+ {{/if}}
102
+ return todo;
103
+ {{else if useTypeorm}}
104
+ {{#if hasAuth}}
105
+ const entity = this.db.typeorm.getRepository(TodoEntity).create({ ...input, authorId: user.id });
106
+ {{else}}
107
+ const entity = this.db.typeorm.getRepository(TodoEntity).create(input);
108
+ {{/if}}
109
+ const saved = await this.db.typeorm.getRepository(TodoEntity).save(entity);
110
+ const builder = new GraphRelationBuilder(this.db.typeorm);
111
+ const relations = builder.buildForQuery(Todo, info, "createTodo");
112
+ return this.db.typeorm.getRepository(TodoEntity).findOneOrFail({
113
+ relations,
114
+ where: { id: saved.id },
69
115
  });
70
116
  {{else if useDrizzle}}
71
- const [todo] = await this.db.insert(schema.todos).values(input).returning();
117
+ {{#if hasAuth}}
118
+ const [todo] = await this.db.drizzle.insert(schema.todos).values({ ...input, authorId: user.id }).returning();
119
+ {{else}}
120
+ const [todo] = await this.db.drizzle.insert(schema.todos).values(input).returning();
121
+ {{/if}}
122
+ if (!todo) throw new NotFoundException("Todo not found");
72
123
  return todo;
73
124
  {{else}}
74
125
  return { id: "1", ...input };
75
126
  {{/if}}
76
127
  }
77
128
 
129
+ {{#if hasAuth}}
78
130
  @UseGuards(GqlAuthGuard)
131
+ {{/if}}
79
132
  @Mutation(() => Todo)
80
- updateTodo(@Args("input") input: UpdateTodoInput{{#if useTypeorm}}, info: GraphQLResolveInfo{{/if}}) {
133
+ async updateTodo(
134
+ {{#if hasAuth}}
135
+ @UserEntity() user: { id: string },
136
+ {{/if}}
137
+ @Args("input") input: UpdateTodoInput{{#if useTypeorm}}, info: GraphQLResolveInfo{{/if}}
138
+ ) {
81
139
  const { id, ...data } = input;
82
- {{#if useTypeorm}}
83
- await this.db.getRepository(Todo).update(id, data);
84
- const builder = new GraphRelationBuilder(this.db.getDataSource());
140
+ if (data.title !== undefined && !data.title.trim()) throw new BadRequestException("Title must not be empty");
141
+ {{#if isPrisma8}}
142
+ {{#if hasAuth}}
143
+ return ((this.db.prisma.orm.public as any).Todo).where({ authorId: user.id, id }).update(data);
144
+ {{else}}
145
+ return ((this.db.prisma.orm.public as any).Todo).where({ id }).update(data);
146
+ {{/if}}
147
+ {{else if usePrisma}}
148
+ {{#if hasAuth}}
149
+ const result = await this.db.prisma.todo.update({ data, where: { authorId: user.id, id } });
150
+ {{else}}
151
+ const result = await this.db.prisma.todo.update({ data, where: { id } });
152
+ {{/if}}
153
+ return result;
154
+ {{else if useTypeorm}}
155
+ {{#if hasAuth}}
156
+ const updateResult = await this.db.typeorm.getRepository(TodoEntity).update({ authorId: user.id, id }, data);
157
+ {{else}}
158
+ const updateResult = await this.db.typeorm.getRepository(TodoEntity).update(id, data);
159
+ {{/if}}
160
+ if ((updateResult.affected ?? 0) === 0) throw new NotFoundException("Todo not found");
161
+ const builder = new GraphRelationBuilder(this.db.typeorm);
85
162
  const relations = builder.buildForQuery(Todo, info, "updateTodo");
86
- return this.db.getRepository(Todo).findOneOrFail({
87
- where: { id },
163
+ return this.db.typeorm.getRepository(TodoEntity).findOneOrFail({
88
164
  relations,
165
+ where: { id },
89
166
  });
90
- {{else if isPrisma8}}
91
- return ((this.db.prisma.orm.public as any).Todo).where({ id }).update(data);
92
- {{else if usePrisma}}
93
- return this.db.prisma.todo.update({ where: { id }, data });
94
167
  {{else if useDrizzle}}
95
- const [todo] = await this.db
96
- .update(schema.todos)
97
- .set(data)
98
- .where(eq(schema.todos.id, id))
99
- .returning();
168
+ {{#if hasAuth}}
169
+ const [todo] = await this.db.drizzle.update(schema.todos).set(data).where(and(eq(schema.todos.authorId, user.id), eq(schema.todos.id, Number(id)))).returning();
170
+ {{else}}
171
+ const [todo] = await this.db.drizzle.update(schema.todos).set(data).where(eq(schema.todos.id, Number(id))).returning();
172
+ {{/if}}
173
+ if (!todo) throw new NotFoundException("Todo not found");
100
174
  return todo;
101
175
  {{else}}
102
176
  return { id, ...data };
103
177
  {{/if}}
104
178
  }
105
179
 
180
+ {{#if hasAuth}}
106
181
  @UseGuards(GqlAuthGuard)
182
+ {{/if}}
107
183
  @Mutation(() => Boolean)
108
- async deleteTodo(@Args("id") id: string): Promise<boolean> {
109
- {{#if useTypeorm}}
110
- const result = await this.db.getRepository(Todo).delete(id);
111
- return (result.affected ?? 0) > 0;
112
- {{else if isPrisma8}}
184
+ async deleteTodo(
185
+ {{#if hasAuth}}
186
+ @UserEntity() user: { id: string },
187
+ {{/if}}
188
+ @Args("id") id: string
189
+ ): Promise<boolean> {
190
+ {{#if isPrisma8}}
191
+ {{#if hasAuth}}
192
+ await ((this.db.prisma.orm.public as any).Todo).where({ authorId: user.id, id }).delete();
193
+ {{else}}
113
194
  await ((this.db.prisma.orm.public as any).Todo).where({ id }).delete();
195
+ {{/if}}
114
196
  return true;
115
197
  {{else if usePrisma}}
116
- await this.db.prisma.todo.delete({ where: { id } });
198
+ {{#if hasAuth}}
199
+ const result = await this.db.prisma.todo.deleteMany({ where: { authorId: user.id, id } });
200
+ {{else}}
201
+ const result = await this.db.prisma.todo.deleteMany({ where: { id } });
202
+ {{/if}}
203
+ if (result.count === 0) throw new NotFoundException("Todo not found");
204
+ return true;
205
+ {{else if useTypeorm}}
206
+ {{#if hasAuth}}
207
+ const result = await this.db.typeorm.getRepository(TodoEntity).delete({ authorId: user.id, id });
208
+ {{else}}
209
+ const result = await this.db.typeorm.getRepository(TodoEntity).delete(id);
210
+ {{/if}}
211
+ if ((result.affected ?? 0) === 0) throw new NotFoundException("Todo not found");
117
212
  return true;
118
213
  {{else if useDrizzle}}
119
- await this.db.delete(schema.todos).where(eq(schema.todos.id, id));
214
+ {{#if hasAuth}}
215
+ const deleted = await this.db.drizzle.delete(schema.todos).where(and(eq(schema.todos.authorId, user.id), eq(schema.todos.id, Number(id)))).returning();
216
+ {{else}}
217
+ const deleted = await this.db.drizzle.delete(schema.todos).where(eq(schema.todos.id, Number(id))).returning();
218
+ {{/if}}
219
+ if (deleted.length === 0) throw new NotFoundException("Todo not found");
120
220
  return true;
121
221
  {{else}}
122
222
  return true;
@@ -33,6 +33,7 @@ export const env = createEnv({
33
33
  .string()
34
34
  .transform(Number)
35
35
  .default(3000),
36
+ TRUSTED_PROXY_IPS: z.string().default("{{#each trustedProxyIps}}{{#unless @first}},{{/unless}}{{this}}{{/each}}"),
36
37
  {{#if hasPostgres}}
37
38
  POSTGRES_URL: z.url(),
38
39
  {{/if}}
@@ -60,6 +61,12 @@ export const env = createEnv({
60
61
  {{#if (or (eq auth "better-auth") (eq auth "better-auth:stateless"))}}
61
62
  BETTER_AUTH_SECRET: z.string().min(32),
62
63
  BETTER_AUTH_URL: z.url(),
64
+ {{#if (eq auth "better-auth:stateless")}}
65
+ OIDC_PROVIDER_ID: z.string().min(1),
66
+ OIDC_CLIENT_ID: z.string().min(1),
67
+ OIDC_CLIENT_SECRET: z.string().min(1),
68
+ OIDC_DISCOVERY_URL: z.url(),
69
+ {{/if}}
63
70
  {{/if}}
64
71
  },
65
72
  });
@@ -4,6 +4,7 @@
4
4
  "private": true,
5
5
  "type": "module",
6
6
  "exports": {
7
+ "./auth": "./src/auth.ts",
7
8
  "./*": "./src/*"
8
9
  },
9
10
  "dependencies": {
@@ -0,0 +1,6 @@
1
+ /** Public authentication profile data safe to share with client packages. */
2
+ export interface AuthUserProfile {
3
+ id: string;
4
+ email: string;
5
+ name?: string;
6
+ }
@@ -8,6 +8,10 @@ allowBuilds:
8
8
  protobufjs: true
9
9
  '@nestjs/core': true
10
10
  esbuild: true
11
+ {{#if usePrisma}}
12
+ prisma: true
13
+ '@prisma/engines': true
14
+ {{/if}}
11
15
  {{#if swagger}}
12
16
  '@scarf/scarf': true
13
17
  {{/if}}
@@ -26,7 +30,11 @@ allowBuilds:
26
30
  workerd: true
27
31
  {{/if}}
28
32
  catalog:
29
- "@types/node": ^22.13.14
33
+ "@nestjs/common": ^12.0.1
34
+ "@nestjs/core": ^12.0.1
35
+ "@types/node": ^24.0.0
30
36
  dotenv: ^17.2.2
31
- typescript: ^6
37
+ reflect-metadata: ^0.2.2
38
+ rxjs: ^7.8.1
39
+ typescript: {{#if useTypescript7}}^7.0.0{{else}}^6.0.2{{/if}}
32
40
  zod: ^4.4.3
@@ -25,4 +25,10 @@ JWT_REFRESH_SECRET=change-me-to-a-different-random-secret-at-least-32-characters
25
25
  {{#if (or (eq auth "better-auth") (eq auth "better-auth:stateless"))}}
26
26
  BETTER_AUTH_SECRET=change-me-to-a-random-secret-at-least-32-characters-long
27
27
  BETTER_AUTH_URL=http://localhost:3000
28
+ {{#if (eq auth "better-auth:stateless")}}
29
+ OIDC_PROVIDER_ID=company-oidc
30
+ OIDC_CLIENT_ID=replace-with-provider-client-id
31
+ OIDC_CLIENT_SECRET=replace-with-provider-client-secret
32
+ OIDC_DISCOVERY_URL=https://identity.example.com/.well-known/openid-configuration
33
+ {{/if}}
28
34
  {{/if}}
@@ -20,7 +20,7 @@
20
20
  "test:debug": "vitest --inspect-brk --no-file-parallelism",
21
21
  "test:e2e": "vitest run --config ./vitest.config.e2e.ts"{{#if hasUltracite}},
22
22
  "check": "ultracite check",
23
- "fix": "ultracite fix"{{/if}}{{#if hasHusky}},
23
+ "fix": "ultracite fix --ignore-pattern migrations/**"{{/if}}{{#if hasHusky}},
24
24
  "prepare": "husky"{{/if}}
25
25
  },
26
26
  "dependencies": {
@@ -24,14 +24,23 @@ import { apiReference } from "@scalar/nestjs-api-reference";
24
24
  import { SwaggerModule } from "@nestjs/swagger";
25
25
  {{/if}}
26
26
  {{#if (and swagger (or (eq auth "better-auth") (eq auth "better-auth:stateless")))}}
27
- import { auth } from "./auth/auth.js";
27
+ import { AuthService } from "@thallesp/nestjs-better-auth";
28
28
  {{/if}}
29
29
 
30
30
  async function bootstrap() {
31
+ const trustedProxyIps = env.TRUSTED_PROXY_IPS
32
+ .split(",")
33
+ .map((ip) => ip.trim())
34
+ .filter(Boolean);
31
35
  {{#if isPrisma8}}
32
36
  await connectDatabase();
33
37
  {{/if}}
34
- const app = await NestFactory.create{{#if (eq httpProvider "fastify")}}<NestFastifyApplication>(AppModule, new FastifyAdapter()){{else}}(AppModule){{/if}};
38
+ const app = await NestFactory.create{{#if (eq httpProvider "fastify")}}<NestFastifyApplication>(AppModule, new FastifyAdapter({ trustProxy: trustedProxyIps })){{else}}(AppModule){{/if}};
39
+ {{#unless (eq httpProvider "fastify")}}
40
+ if (trustedProxyIps.length > 0) {
41
+ app.getHttpAdapter().getInstance().set("trust proxy", trustedProxyIps);
42
+ }
43
+ {{/unless}}
35
44
  app.enableCors({
36
45
  origin: env.CORS_ORIGIN,
37
46
  });
@@ -58,7 +67,7 @@ async function bootstrap() {
58
67
  integrations: {
59
68
  {{#if (or (eq auth "better-auth") (eq auth "better-auth:stateless"))}}
60
69
  betterAuth: {
61
- auth,
70
+ auth: app.get(AuthService).instance,
62
71
  pathPrefix: "/api/auth",
63
72
  },
64
73
  {{/if}}
@@ -85,7 +94,7 @@ async function bootstrap() {
85
94
  integrations: {
86
95
  {{#if (or (eq auth "better-auth") (eq auth "better-auth:stateless"))}}
87
96
  betterAuth: {
88
- auth,
97
+ auth: app.get(AuthService).instance,
89
98
  pathPrefix: "/api/auth",
90
99
  },
91
100
  {{/if}}
@@ -16,7 +16,7 @@ import { apiReference } from "@scalar/nestjs-api-reference";
16
16
  import { SwaggerModule } from "@nestjs/swagger";
17
17
  {{/if}}
18
18
  {{#if (and swagger (or (eq auth "better-auth") (eq auth "better-auth:stateless")))}}
19
- import { auth } from "./auth/auth.js";
19
+ import { AuthService } from "@thallesp/nestjs-better-auth";
20
20
  {{/if}}
21
21
 
22
22
  async function bootstrap() {
@@ -39,7 +39,7 @@ async function bootstrap() {
39
39
  integrations: {
40
40
  {{#if (or (eq auth "better-auth") (eq auth "better-auth:stateless"))}}
41
41
  betterAuth: {
42
- auth,
42
+ auth: app.get(AuthService).instance,
43
43
  pathPrefix: "/api/auth",
44
44
  },
45
45
  {{/if}}
@@ -66,7 +66,7 @@ async function bootstrap() {
66
66
  integrations: {
67
67
  {{#if (or (eq auth "better-auth") (eq auth "better-auth:stateless"))}}
68
68
  betterAuth: {
69
- auth,
69
+ auth: app.get(AuthService).instance,
70
70
  pathPrefix: "/api/auth",
71
71
  },
72
72
  {{/if}}
@@ -4,6 +4,7 @@ package app;
4
4
 
5
5
  service AppService {
6
6
  rpc Ping (PingRequest) returns (PingResponse);
7
+ rpc Log (LogRequest) returns (LogResponse);
7
8
  }
8
9
 
9
10
  message PingRequest {}
@@ -11,3 +12,15 @@ message PingRequest {}
11
12
  message PingResponse {
12
13
  string message = 1;
13
14
  }
15
+
16
+ message LogRequest {
17
+ string id = 1;
18
+ string source = 2;
19
+ string payload = 3;
20
+ }
21
+
22
+ message LogResponse {
23
+ string id = 1;
24
+ string source = 2;
25
+ string payload = 3;
26
+ }
@@ -1,5 +1,5 @@
1
1
  import { Controller, Get } from "@nestjs/common";
2
- import { EventPattern, {{#if (includes microservices "grpc")}}GrpcMethod{{else}}MessagePattern{{/if}} } from "@nestjs/microservices";
2
+ import { {{#if (includes microservices "grpc")}}GrpcMethod{{else}}MessagePattern{{/if}} } from "@nestjs/microservices";
3
3
 
4
4
  import { AppService } from "./app.service.js";
5
5
 
@@ -17,6 +17,10 @@ export class AppController {
17
17
  handleGrpcPing(): { message: string } {
18
18
  return { message: this.appService.handlePing() };
19
19
  }
20
+ @GrpcMethod("AppService", "Log")
21
+ handleGrpcLog(data: { id: string; source: string; payload: string }): { id: string; source: string; payload: string } {
22
+ return data;
23
+ }
20
24
  {{else}}
21
25
  @MessagePattern("ping")
22
26
  handlePing(): string {
@@ -24,8 +28,10 @@ export class AppController {
24
28
  }
25
29
  {{/if}}
26
30
 
27
- @EventPattern("log")
28
- handleLog(data: Record<string, unknown>): void {
29
- this.appService.handleLog(data);
31
+ {{#unless (includes microservices "grpc")}}
32
+ @MessagePattern("log")
33
+ handleLog(data: Record<string, unknown>): { received: Record<string, unknown> } {
34
+ return this.appService.handleLog(data);
30
35
  }
36
+ {{/unless}}
31
37
  }
@@ -17,7 +17,7 @@ export class AppService {
17
17
  return "pong";
18
18
  }
19
19
 
20
- handleLog(data: Record<string, unknown>): void {
21
- console.log("Microservice received:", data);
20
+ handleLog(data: Record<string, unknown>): { received: Record<string, unknown> } {
21
+ return { received: data };
22
22
  }
23
23
  }
@@ -17,7 +17,7 @@ import { apiReference } from "@scalar/nestjs-api-reference";
17
17
  import { SwaggerModule } from "@nestjs/swagger";
18
18
  {{/if}}
19
19
  {{#if (and swagger (or (eq auth "better-auth") (eq auth "better-auth:stateless")))}}
20
- import { auth } from "./auth/auth.js";
20
+ import { AuthService } from "@thallesp/nestjs-better-auth";
21
21
  {{/if}}
22
22
 
23
23
  async function bootstrap() {
@@ -56,7 +56,7 @@ async function bootstrap() {
56
56
  integrations: {
57
57
  {{#if (or (eq auth "better-auth") (eq auth "better-auth:stateless"))}}
58
58
  betterAuth: {
59
- auth,
59
+ auth: app.get(AuthService).instance,
60
60
  pathPrefix: "/api/auth",
61
61
  },
62
62
  {{/if}}
@@ -83,7 +83,7 @@ async function bootstrap() {
83
83
  integrations: {
84
84
  {{#if (or (eq auth "better-auth") (eq auth "better-auth:stateless"))}}
85
85
  betterAuth: {
86
- auth,
86
+ auth: app.get(AuthService).instance,
87
87
  pathPrefix: "/api/auth",
88
88
  },
89
89
  {{/if}}
@@ -35,6 +35,7 @@ it("sends ping through the selected message transport", async () => {
35
35
  await app.init();
36
36
  {{#if (includes microservices "kafka")}}
37
37
  client.subscribeToResponseOf("ping");
38
+ client.subscribeToResponseOf("log");
38
39
  {{/if}}
39
40
  {{#unless (includes microservices "grpc")}}
40
41
  {{#if (includes microservices "kafka")}}
@@ -51,12 +52,17 @@ it("sends ping through the selected message transport", async () => {
51
52
  {{#if (includes microservices "grpc")}}
52
53
  const service = client.getService<{
53
54
  ping: (request: Record<string, never>) => Observable<{ message: string }>;
55
+ log: (request: { id: string; source: string; payload: string }) => Observable<{ id: string; source: string; payload: string }>;
54
56
  }>("AppService");
55
57
  await expect(firstValueFrom(service.ping({}))).resolves.toEqual({ message: "pong" });
58
+ const event = { id: "event-1", source: "scenario", payload: "healthy" };
59
+ await expect(firstValueFrom(service.log(event))).resolves.toEqual(event);
56
60
  {{else}}
57
61
  await expect(firstValueFrom(client.send<string, Record<string, never>>("ping", {}))).resolves.toBe("pong");
62
+ const event = { id: "event-1", source: "scenario", payload: { healthy: true } };
63
+ await expect(firstValueFrom(client.send("log", event))).resolves.toEqual({ received: event });
58
64
  {{/if}}
59
- }, 20_000);
65
+ }, 60_000);
60
66
 
61
67
  afterAll(async () => {
62
68
  await app?.close();
@@ -33,6 +33,7 @@ export const env = createEnv({
33
33
  .string()
34
34
  .transform(Number)
35
35
  .default(3000),
36
+ TRUSTED_PROXY_IPS: z.string().default("{{#each trustedProxyIps}}{{#unless @first}},{{/unless}}{{this}}{{/each}}"),
36
37
  {{#if (includes microservices "redis")}}
37
38
  REDIS_URL: z.url().default("redis://127.0.0.1:6379"),
38
39
  {{/if}}
@@ -71,17 +72,27 @@ export const env = createEnv({
71
72
  .default(true),
72
73
  {{/if}}
73
74
  {{#if (eq auth "passport")}}
75
+ {{#if hasDatabase}}
74
76
  JWT_ACCESS_SECRET: z.string().min(32),
75
77
  JWT_ISSUER: z.string().default("{{projectName}}"),
76
78
  JWT_AUDIENCE: z.string().default("{{projectName}}-api"),
77
79
  JWT_KEY_ID: z.string().default("primary"),
78
- {{#if hasDatabase}}
79
80
  JWT_REFRESH_SECRET: z.string().min(32),
81
+ {{else}}
82
+ OIDC_JWKS_URI: z.url(),
83
+ OIDC_ISSUER: z.url(),
84
+ OIDC_AUDIENCE: z.string().min(1),
80
85
  {{/if}}
81
86
  {{/if}}
82
87
  {{#if (or (eq auth "better-auth") (eq auth "better-auth:stateless"))}}
83
88
  BETTER_AUTH_SECRET: z.string().min(32),
84
89
  BETTER_AUTH_URL: z.url(),
90
+ {{#if (eq auth "better-auth:stateless")}}
91
+ OIDC_PROVIDER_ID: z.string().min(1),
92
+ OIDC_CLIENT_ID: z.string().min(1),
93
+ OIDC_CLIENT_SECRET: z.string().min(1),
94
+ OIDC_DISCOVERY_URL: z.url(),
95
+ {{/if}}
85
96
  {{/if}}
86
97
  },
87
98
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nest-arch/tui",
3
- "version": "0.4.0",
3
+ "version": "0.4.3",
4
4
  "private": false,
5
5
  "description": "Interactive terminal UI for generating NestJS projects",
6
6
  "keywords": [
@@ -51,7 +51,7 @@
51
51
  "pkgroll": "^2.5.2",
52
52
  "tsx": "^4.19.2",
53
53
  "vitest": "^4.1.7",
54
- "@nest-arch/core": "0.4.0",
54
+ "@nest-arch/core": "0.4.3",
55
55
  "@nest-arch/env": "0.0.0",
56
56
  "@nest-arch/shared": "0.0.0"
57
57
  },