@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.
- package/dist/cli.js +1 -1
- package/dist/{constants-C3xmDWeK.js → constants-B5MuV-iH.js} +1 -1
- package/dist/index.js +188 -134
- package/dist/templates/addons/husky/pre-commit.hbs +11 -26
- package/dist/templates/base/.env.example.hbs +9 -0
- package/dist/templates/base/.env.hbs +7 -0
- package/dist/templates/base/README.md.hbs +16 -0
- package/dist/templates/base/pnpm-workspace.yaml.hbs +6 -0
- package/dist/templates/base/src/app.module.ts.hbs +68 -1
- package/dist/templates/base/src/app.router.ts.hbs +1 -2
- package/dist/templates/base/src/database/adapters/mongo/prisma-mongo.adapter.ts.hbs +11 -3
- package/dist/templates/base/src/database/adapters/sql/prisma-sql.adapter.ts.hbs +1 -1
- package/dist/templates/base/src/database/db.module.ts.hbs +24 -8
- package/dist/templates/base/src/database/db.service.ts.hbs +20 -0
- package/dist/templates/base/src/database/entities/app.entity.ts.hbs +25 -0
- package/dist/templates/base/src/database/index.ts.hbs +19 -0
- package/dist/templates/base/src/database/orm/drizzle/schema.ts.hbs +42 -11
- package/dist/templates/base/src/database/orm/typeorm/data-source.ts.hbs +12 -1
- package/dist/templates/base/src/libs/middlewares/auth.trpc.middleware.ts.hbs +47 -0
- package/dist/templates/base/src/libs/middlewares/rate-limit.trpc.middleware.ts.hbs +43 -0
- package/dist/templates/base/src/libs/trpc/inner.context.ts.hbs +6 -3
- package/dist/templates/base/src/main.ts.hbs +13 -4
- package/dist/templates/base/src/modules/users/entities/user.entity.ts.hbs +22 -0
- package/dist/templates/base/test/app.e2e-spec.ts.hbs +670 -37
- package/dist/templates/base/test/bootstrap.e2e-spec.ts.hbs +11 -1
- package/dist/templates/extras/docker/.dockerignore.hbs +8 -1
- package/dist/templates/extras/docker/Dockerfile.hbs +8 -10
- package/dist/templates/extras/health/src/health/health.controller.ts.hbs +54 -0
- package/dist/templates/extras/health/src/health/health.module.ts.hbs +9 -0
- package/dist/templates/extras/swagger/create-docs.ts.hbs +9 -3
- package/dist/templates/feature/graphql/src/graphql/graphql.module.ts.hbs +6 -17
- package/dist/templates/feature/graphql/src/graphql/orm/drizzle/drizzle-graphql.module.ts.hbs +3 -2
- package/dist/templates/feature/graphql/src/graphql/resolvers/health.resolver.ts.hbs +14 -0
- package/dist/templates/feature/graphql/src/graphql/resolvers/todo.resolver.ts.hbs +165 -53
- package/dist/templates/feature/rest/todo-example/src/api/todo/dto/create-todo.dto.ts.hbs +12 -0
- package/dist/templates/feature/rest/todo-example/src/api/todo/dto/update-todo.dto.ts.hbs +12 -0
- package/dist/templates/feature/rest/todo-example/src/api/todo/todo.controller.ts.hbs +18 -8
- package/dist/templates/feature/rest/todo-example/src/api/todo/todo.router.ts.hbs +41 -13
- package/dist/templates/feature/rest/todo-example/src/api/todo/todo.service.ts.hbs +68 -10
- package/dist/templates/monorepo/apps/api-gateway/package.json.hbs +2 -2
- package/dist/templates/monorepo/apps/api-gateway/src/app.controller.spec.ts.hbs +5 -0
- package/dist/templates/monorepo/apps/api-gateway/src/app.controller.ts.hbs +6 -0
- package/dist/templates/monorepo/apps/api-gateway/src/app.module.ts.hbs +32 -1
- package/dist/templates/monorepo/apps/api-gateway/src/app.service.ts.hbs +1 -1
- package/dist/templates/monorepo/apps/api-gateway/src/main.ts.hbs +13 -4
- package/dist/templates/monorepo/apps/api-gateway/test/app.e2e-spec.ts.hbs +26 -1
- package/dist/templates/monorepo/feature/graphql/src/graphql/graphql.module.ts.hbs +6 -10
- package/dist/templates/monorepo/feature/graphql/src/graphql/resolvers/health.resolver.ts.hbs +14 -0
- package/dist/templates/monorepo/feature/graphql/src/graphql/resolvers/todo.resolver.ts.hbs +147 -47
- package/dist/templates/monorepo/packages/env/src/api-gateway.ts.hbs +7 -0
- package/dist/templates/monorepo/packages/shared/package.json.hbs +1 -0
- package/dist/templates/monorepo/packages/shared/src/auth.ts.hbs +6 -0
- package/dist/templates/monorepo/root/pnpm-workspace.yaml.hbs +10 -2
- package/dist/templates/single-project/architecture/nest-api/.env.hbs +6 -0
- package/dist/templates/single-project/architecture/nest-api/package.json.hbs +1 -1
- package/dist/templates/single-project/architecture/nest-api/src/main.ts.hbs +13 -4
- package/dist/templates/single-project/architecture/nest-gateway/src/main.ts.hbs +3 -3
- 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 +10 -4
- package/dist/templates/single-project/architecture/nest-microservice/src/app.service.ts.hbs +2 -2
- package/dist/templates/single-project/architecture/nest-microservice/src/main.ts.hbs +3 -3
- package/dist/templates/single-project/architecture/nest-microservice/test/microservice.e2e-spec.ts.hbs +7 -1
- package/dist/templates/single-project/shared/config/env.ts.hbs +12 -1
- 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 {
|
|
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
|
-
{
|
|
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
|
-
|
|
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.
|
|
49
|
+
const builder = new GraphRelationBuilder(this.db.typeorm);
|
|
36
50
|
const relations = builder.buildForQuery(Todo, info, "todos");
|
|
37
|
-
|
|
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
|
-
|
|
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
|
-
|
|
56
|
-
|
|
57
|
-
|
|
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
|
-
|
|
68
|
-
|
|
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
|
-
|
|
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(
|
|
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
|
-
|
|
83
|
-
|
|
84
|
-
|
|
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(
|
|
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
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
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(
|
|
109
|
-
{{#if
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
});
|
|
@@ -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
|
-
"@
|
|
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
|
-
|
|
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 {
|
|
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 {
|
|
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
|
+
}
|
package/dist/templates/single-project/architecture/nest-microservice/src/app.controller.ts.hbs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Controller, Get } from "@nestjs/common";
|
|
2
|
-
import {
|
|
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
|
-
|
|
28
|
-
|
|
29
|
-
|
|
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>):
|
|
21
|
-
|
|
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 {
|
|
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
|
-
},
|
|
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.
|
|
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.
|
|
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
|
},
|