@nest-arch/tui 0.4.5 → 0.5.0
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-DX5wiqrq.js +1 -0
- package/dist/index.js +158 -145
- package/dist/templates/addons/husky/noop-hook.hbs +2 -0
- package/dist/templates/addons/husky/pre-commit.hbs +1 -1
- package/dist/templates/base/.env.example.hbs +16 -2
- package/dist/templates/base/.env.hbs +16 -2
- package/dist/templates/base/README.md.hbs +23 -2
- package/dist/templates/base/pnpm-workspace.yaml.hbs +9 -4
- package/dist/templates/base/prisma.config.ts.hbs +6 -0
- package/dist/templates/base/src/app.module.ts.hbs +6 -22
- package/dist/templates/base/src/database/adapters/interfaces/db-adapter.interface.ts.hbs +1 -1
- package/dist/templates/base/src/database/adapters/mongo/raw-mongo.adapter.ts.hbs +4 -2
- package/dist/templates/base/src/database/db.module.ts.hbs +30 -3
- package/dist/templates/base/src/database/db.service.ts.hbs +11 -8
- package/dist/templates/base/src/database/entities/todo.entity.ts.hbs +2 -2
- package/dist/templates/base/src/database/index.ts.hbs +2 -0
- package/dist/templates/base/src/database/orm/drizzle/schema.ts.hbs +48 -12
- package/dist/templates/base/src/database/orm/typeorm/data-source.ts.hbs +26 -10
- package/dist/templates/base/src/libs/middlewares/graphql-throttler.guard.ts.hbs +22 -0
- package/dist/templates/base/src/libs/trpc/inner.context.ts.hbs +2 -2
- package/dist/templates/base/src/main.ts.hbs +7 -6
- package/dist/templates/base/test/app.e2e-spec.ts.hbs +374 -198
- package/dist/templates/base/test/bootstrap.e2e-spec.ts.hbs +2 -2
- package/dist/templates/extras/docker/Dockerfile.hbs +8 -1
- package/dist/templates/extras/health/src/health/health.controller.ts.hbs +7 -1
- package/dist/templates/extras/swagger/create-docs.ts.hbs +6 -1
- package/dist/templates/feature/graphql/src/graphql/models/todo.model.ts.hbs +1 -1
- package/dist/templates/feature/graphql/src/graphql/resolvers/todo.resolver.ts.hbs +1 -1
- package/dist/templates/feature/rest/todo-example/src/api/todo/todo.controller.ts.hbs +80 -7
- package/dist/templates/feature/rest/todo-example/src/api/todo/todo.service.ts.hbs +181 -25
- package/dist/templates/monorepo/apps/api-gateway/src/main.ts.hbs +10 -9
- package/dist/templates/monorepo/root/README.md.hbs +8 -13
- package/dist/templates/single-project/architecture/nest-api/.env.hbs +15 -2
- package/dist/templates/single-project/architecture/nest-api/pnpm-workspace.yaml.hbs +6 -0
- package/dist/templates/single-project/architecture/nest-api/src/main.ts.hbs +10 -9
- package/dist/templates/single-project/architecture/nest-gateway/src/main.ts.hbs +10 -9
- package/dist/templates/single-project/architecture/nest-microservice/.env.hbs +6 -0
- package/dist/templates/single-project/architecture/nest-microservice/src/app.controller.ts.hbs +2 -2
- package/dist/templates/single-project/architecture/nest-microservice/src/app.module.ts.hbs +28 -1
- package/dist/templates/single-project/architecture/nest-microservice/src/http-throttler.guard.ts.hbs +13 -0
- package/dist/templates/single-project/architecture/nest-microservice/src/main.ts.hbs +10 -9
- package/dist/templates/single-project/architecture/nest-microservice/test/microservice.e2e-spec.ts.hbs +35 -0
- package/dist/templates/single-project/shared/config/env.ts.hbs +9 -0
- package/package.json +5 -5
- package/dist/constants-BbC7KTI7.js +0 -1
|
@@ -19,7 +19,7 @@ async function getFreePort(): Promise<number> {
|
|
|
19
19
|
return address.port;
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
-
async function waitForServer(url: string, application: ChildProcess, deadline = Date.now() +
|
|
22
|
+
async function waitForServer(url: string, application: ChildProcess, deadline = Date.now() + 60000): Promise<Response> {
|
|
23
23
|
if (application.exitCode !== null) throw new Error(`Generated application exited during startup with code ${application.exitCode}.`);
|
|
24
24
|
try {
|
|
25
25
|
return await fetch(url, { signal: AbortSignal.timeout(500) });
|
|
@@ -81,4 +81,4 @@ it("boots the compiled application through main.ts", async () => {
|
|
|
81
81
|
const body = await response.json();
|
|
82
82
|
expect(body.data.hello).toBe("Hello GraphQL!");
|
|
83
83
|
{{/if}}
|
|
84
|
-
},
|
|
84
|
+
}, 65_000);
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
FROM node:22-alpine
|
|
2
2
|
|
|
3
|
+
{{#if hasSqlite}}
|
|
4
|
+
RUN apk add --no-cache python3 make g++
|
|
5
|
+
{{/if}}
|
|
6
|
+
|
|
3
7
|
WORKDIR /app
|
|
4
8
|
|
|
5
9
|
{{#if (eq packageManager "pnpm")}}
|
|
@@ -10,7 +14,10 @@ RUN corepack enable
|
|
|
10
14
|
{{/if}}
|
|
11
15
|
|
|
12
16
|
COPY . .
|
|
13
|
-
RUN POSTGRES_URL=postgresql://postgres:postgres@postgres:5432/build_db MYSQL_URL=mysql://root:mysql@mysql:3306/build_db MONGODB_URL=mongodb://mongodb:27017/build_db {{#if (eq auth "passport")}}JWT_ACCESS_SECRET=build-only-access-secret-that-is-long-enough JWT_REFRESH_SECRET=build-only-refresh-secret-that-is-long-enough {{/if}}{{#if (or (eq auth "better-auth") (eq auth "better-auth:stateless"))}}BETTER_AUTH_SECRET=build-only-better-auth-secret-that-is-long-enough BETTER_AUTH_URL=http://localhost:{{#if (eq httpProvider "fastify")}}3001{{else}}3000{{/if}} {{/if}}{{#if (eq packageManager "pnpm")}}pnpm install --no-lockfile{{else if (eq packageManager "yarn")}}yarn install{{else if (eq packageManager "bun")}}bun install{{
|
|
17
|
+
RUN POSTGRES_URL=postgresql://postgres:postgres@postgres:5432/build_db MYSQL_URL=mysql://root:mysql@mysql:3306/build_db MONGODB_URL=mongodb://mongodb:27017/build_db {{#if hasSqlserver}}SQLSERVER_HOST=localhost SQLSERVER_PORT=1433 SQLSERVER_DATABASE=build_db SQLSERVER_USER=sa SQLSERVER_PASSWORD=Build_only_Password_2026!X9 SQLSERVER_ENCRYPT=true SQLSERVER_TRUST_SERVER_CERTIFICATE=true {{/if}}{{#if (eq auth "passport")}}JWT_ACCESS_SECRET=build-only-access-secret-that-is-long-enough JWT_REFRESH_SECRET=build-only-refresh-secret-that-is-long-enough {{/if}}{{#if (or (eq auth "better-auth") (eq auth "better-auth:stateless"))}}BETTER_AUTH_SECRET=build-only-better-auth-secret-that-is-long-enough BETTER_AUTH_URL=http://localhost:{{#if (eq httpProvider "fastify")}}3001{{else}}3000{{/if}} {{#if (eq auth "better-auth:stateless")}}OIDC_PROVIDER_ID=build-only OIDC_CLIENT_ID=build-only OIDC_CLIENT_SECRET=build-only OIDC_DISCOVERY_URL=https://example.invalid/.well-known/openid-configuration {{/if}}{{/if}}{{#if (eq packageManager "pnpm")}}pnpm install --no-lockfile{{else if (eq packageManager "yarn")}}yarn install{{else if (eq packageManager "bun")}}bun install{{/if}}
|
|
18
|
+
{{#each dockerBuildSetupCommands}}
|
|
19
|
+
RUN POSTGRES_URL=postgresql://postgres:postgres@postgres:5432/build_db MYSQL_URL=mysql://root:mysql@mysql:3306/build_db MONGODB_URL=mongodb://mongodb:27017/build_db SQLITE_PATH=/tmp/build.db {{#if (eq ../auth "passport")}}JWT_ACCESS_SECRET=build-only-access-secret-that-is-long-enough JWT_REFRESH_SECRET=build-only-refresh-secret-that-is-long-enough {{/if}}{{#if (or (eq ../auth "better-auth") (eq ../auth "better-auth:stateless"))}}BETTER_AUTH_SECRET=build-only-better-auth-secret-that-is-long-enough BETTER_AUTH_URL=http://localhost:{{#if (eq ../httpProvider "fastify")}}3001{{else}}3000{{/if}} {{#if (eq ../auth "better-auth:stateless")}}OIDC_PROVIDER_ID=build-only OIDC_CLIENT_ID=build-only OIDC_CLIENT_SECRET=build-only OIDC_DISCOVERY_URL=https://example.invalid/.well-known/openid-configuration {{/if}}{{/if}}{{this}}
|
|
20
|
+
{{/each}}
|
|
14
21
|
RUN {{#if (eq packageManager "pnpm")}}pnpm run{{else if (eq packageManager "yarn")}}yarn{{else if (eq packageManager "bun")}}bun run{{else}}npm run{{/if}} build
|
|
15
22
|
|
|
16
23
|
ENV NODE_ENV=production
|
|
@@ -34,7 +34,13 @@ export class HealthController {
|
|
|
34
34
|
{{else}}
|
|
35
35
|
await this.db.raw.mongo.command({ ping: 1 });
|
|
36
36
|
{{/if}}
|
|
37
|
-
{{else if
|
|
37
|
+
{{else if isPrisma8}}
|
|
38
|
+
{{#if hasTodoExample}}
|
|
39
|
+
await this.db.prisma.orm.public.Todo.all();
|
|
40
|
+
{{else}}
|
|
41
|
+
await this.db.prisma.connect();
|
|
42
|
+
{{/if}}
|
|
43
|
+
{{else if usePrisma}}
|
|
38
44
|
await this.db.prisma.$queryRaw`SELECT 1`;
|
|
39
45
|
{{else if useDrizzle}}
|
|
40
46
|
{{#if hasSqlite}}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
|
|
2
2
|
import type { INestApplication } from "@nestjs/common";
|
|
3
|
+
import { Logger } from "@nestjs/common";
|
|
3
4
|
import { DocumentBuilder, SwaggerModule } from "@nestjs/swagger";
|
|
4
5
|
import type { OpenAPIObject } from "@nestjs/swagger";
|
|
5
6
|
|
|
@@ -168,7 +169,11 @@ async function applyTrpcSpec(options?: NonNullable<DocsIntegrations["trpc"]>) {
|
|
|
168
169
|
}
|
|
169
170
|
|
|
170
171
|
return trpcSpec;
|
|
171
|
-
} catch {
|
|
172
|
+
} catch (error) {
|
|
173
|
+
Logger.warn(
|
|
174
|
+
`Could not generate tRPC documentation from ${options.routerFilePath}: ${error instanceof Error ? error.message : String(error)}`,
|
|
175
|
+
"SwaggerDocs"
|
|
176
|
+
);
|
|
172
177
|
return null;
|
|
173
178
|
}
|
|
174
179
|
}
|
|
@@ -52,7 +52,7 @@ import { GraphRelationBuilder } from "typeorm-relations-graphql";
|
|
|
52
52
|
import { Todo as TodoEntity } from "../../database/entities/todo.entity.js";
|
|
53
53
|
{{/if}}
|
|
54
54
|
{{#if (and hasDatabase useDrizzle)}}
|
|
55
|
-
import { and, eq } from "drizzle-orm";
|
|
55
|
+
import { {{#if hasAuth}}and, {{/if}}eq } from "drizzle-orm";
|
|
56
56
|
import * as schema from "../../database/orm/drizzle/schema.js";
|
|
57
57
|
{{/if}}
|
|
58
58
|
{{#if hasDatabase}}
|
|
@@ -6,7 +6,10 @@ import {
|
|
|
6
6
|
Param,
|
|
7
7
|
Post,
|
|
8
8
|
Put,
|
|
9
|
-
{{#if (
|
|
9
|
+
{{#if (eq auth "better-auth:stateless")}}
|
|
10
|
+
UnauthorizedException,
|
|
11
|
+
{{/if}}
|
|
12
|
+
{{#if isPassportAuth}}
|
|
10
13
|
UseGuards,
|
|
11
14
|
{{/if}}
|
|
12
15
|
{{#if (and isBetterAuth hasDatabase hasGraphql)}}
|
|
@@ -31,7 +34,11 @@ import {
|
|
|
31
34
|
import { Session } from "@thallesp/nestjs-better-auth";
|
|
32
35
|
import type { UserSession } from "../../auth/auth.js";
|
|
33
36
|
{{/if}}
|
|
34
|
-
{{#if (and
|
|
37
|
+
{{#if (and isBetterAuth (not hasDatabase))}}
|
|
38
|
+
import { Session } from "@thallesp/nestjs-better-auth";
|
|
39
|
+
import type { UserSession } from "../../auth/auth.js";
|
|
40
|
+
{{/if}}
|
|
41
|
+
{{#if isPassportAuth}}
|
|
35
42
|
import { RestAuthGuard } from "../../auth/guards/rest-auth.guard.js";
|
|
36
43
|
import { CurrentUser } from "../../auth/decorators/current-user.decorator.js";
|
|
37
44
|
{{/if}}
|
|
@@ -41,16 +48,29 @@ import { RestAuthGuard } from "../../auth/guards/rest-auth.guard.js";
|
|
|
41
48
|
import { TodoService } from "./todo.service.js";
|
|
42
49
|
import { CreateTodoDto } from "./dto/create-todo.dto.js";
|
|
43
50
|
import { UpdateTodoDto } from "./dto/update-todo.dto.js";
|
|
51
|
+
{{#if isPrisma8}}
|
|
52
|
+
import type { Models } from "../../prisma/contract.js";
|
|
53
|
+
|
|
54
|
+
type Todo = Models.public_Todo;
|
|
55
|
+
{{/if}}
|
|
44
56
|
|
|
45
57
|
{{#if swagger}}
|
|
46
58
|
@ApiTags("Todos")
|
|
47
59
|
{{/if}}
|
|
48
60
|
@Controller("todos")
|
|
49
|
-
{{#if (or
|
|
61
|
+
{{#if (or isPassportAuth (and isBetterAuth hasDatabase hasGraphql))}}
|
|
50
62
|
@UseGuards(RestAuthGuard)
|
|
51
63
|
{{/if}}
|
|
52
64
|
export class TodoController {
|
|
53
65
|
constructor(private readonly todoService: TodoService) {}
|
|
66
|
+
{{#if (eq auth "better-auth:stateless")}}
|
|
67
|
+
private requireUserId(session: UserSession | null | undefined): string {
|
|
68
|
+
if (!session?.user?.id) {
|
|
69
|
+
throw new UnauthorizedException("Authentication required");
|
|
70
|
+
}
|
|
71
|
+
return session.user.id;
|
|
72
|
+
}
|
|
73
|
+
{{/if}}
|
|
54
74
|
|
|
55
75
|
@Get()
|
|
56
76
|
{{#if swagger}}
|
|
@@ -63,7 +83,7 @@ export class TodoController {
|
|
|
63
83
|
@ApiUnauthorizedResponse({ description: "Unauthorized" })
|
|
64
84
|
{{/if}}
|
|
65
85
|
{{/if}}
|
|
66
|
-
{{#if
|
|
86
|
+
{{#if isPassportAuth}}
|
|
67
87
|
findAll(@CurrentUser() user: { id: string }) {
|
|
68
88
|
return this.todoService.findAll(user.id);
|
|
69
89
|
}
|
|
@@ -75,10 +95,20 @@ export class TodoController {
|
|
|
75
95
|
{{/if}}
|
|
76
96
|
return this.todoService.findAll(session.user.id);
|
|
77
97
|
}
|
|
98
|
+
{{else if isBetterAuth}}
|
|
99
|
+
findAll(@Session() session: UserSession | null) {
|
|
100
|
+
return this.todoService.findAll(this.requireUserId(session));
|
|
101
|
+
}
|
|
102
|
+
{{else}}
|
|
103
|
+
{{#if isPrisma8}}
|
|
104
|
+
async findAll(): Promise<Todo[]> {
|
|
105
|
+
return await this.todoService.findAll();
|
|
106
|
+
}
|
|
78
107
|
{{else}}
|
|
79
108
|
findAll() {
|
|
80
109
|
return this.todoService.findAll();
|
|
81
110
|
}
|
|
111
|
+
{{/if}}
|
|
82
112
|
{{/if}}
|
|
83
113
|
|
|
84
114
|
@Post()
|
|
@@ -92,7 +122,7 @@ export class TodoController {
|
|
|
92
122
|
@ApiUnauthorizedResponse({ description: "Unauthorized" })
|
|
93
123
|
{{/if}}
|
|
94
124
|
{{/if}}
|
|
95
|
-
{{#if
|
|
125
|
+
{{#if isPassportAuth}}
|
|
96
126
|
create(
|
|
97
127
|
@Body{{#if (includes addons "nestjs-zod")}}(){{else}}({ schema: CreateTodoDto.schema }){{/if}} input: CreateTodoDto,
|
|
98
128
|
@CurrentUser() user: { id: string }
|
|
@@ -106,10 +136,23 @@ export class TodoController {
|
|
|
106
136
|
) {
|
|
107
137
|
return this.todoService.create(input, session.user.id);
|
|
108
138
|
}
|
|
139
|
+
{{else if isBetterAuth}}
|
|
140
|
+
create(
|
|
141
|
+
@Body{{#if (includes addons "nestjs-zod")}}(){{else}}({ schema: CreateTodoDto.schema }){{/if}} input: CreateTodoDto,
|
|
142
|
+
@Session() session: UserSession | null
|
|
143
|
+
) {
|
|
144
|
+
return this.todoService.create(input, this.requireUserId(session));
|
|
145
|
+
}
|
|
146
|
+
{{else}}
|
|
147
|
+
{{#if isPrisma8}}
|
|
148
|
+
async create(@Body{{#if (includes addons "nestjs-zod")}}(){{else}}({ schema: CreateTodoDto.schema }){{/if}} input: CreateTodoDto): Promise<Todo> {
|
|
149
|
+
return await this.todoService.create(input);
|
|
150
|
+
}
|
|
109
151
|
{{else}}
|
|
110
152
|
create(@Body{{#if (includes addons "nestjs-zod")}}(){{else}}({ schema: CreateTodoDto.schema }){{/if}} input: CreateTodoDto) {
|
|
111
153
|
return this.todoService.create(input);
|
|
112
154
|
}
|
|
155
|
+
{{/if}}
|
|
113
156
|
{{/if}}
|
|
114
157
|
|
|
115
158
|
@Put(":id")
|
|
@@ -123,7 +166,7 @@ export class TodoController {
|
|
|
123
166
|
@ApiUnauthorizedResponse({ description: "Unauthorized" })
|
|
124
167
|
{{/if}}
|
|
125
168
|
{{/if}}
|
|
126
|
-
{{#if
|
|
169
|
+
{{#if isPassportAuth}}
|
|
127
170
|
update(
|
|
128
171
|
@Param("id") id: string,
|
|
129
172
|
@Body{{#if (includes addons "nestjs-zod")}}(){{else}}({ schema: UpdateTodoDto.schema }){{/if}} input: UpdateTodoDto,
|
|
@@ -139,6 +182,22 @@ export class TodoController {
|
|
|
139
182
|
) {
|
|
140
183
|
return this.todoService.update(id, input, session.user.id);
|
|
141
184
|
}
|
|
185
|
+
{{else if isBetterAuth}}
|
|
186
|
+
update(
|
|
187
|
+
@Param("id") id: string,
|
|
188
|
+
@Body{{#if (includes addons "nestjs-zod")}}(){{else}}({ schema: UpdateTodoDto.schema }){{/if}} input: UpdateTodoDto,
|
|
189
|
+
@Session() session: UserSession | null
|
|
190
|
+
) {
|
|
191
|
+
return this.todoService.update(id, input, this.requireUserId(session));
|
|
192
|
+
}
|
|
193
|
+
{{else}}
|
|
194
|
+
{{#if isPrisma8}}
|
|
195
|
+
async update(
|
|
196
|
+
@Param("id") id: string,
|
|
197
|
+
@Body{{#if (includes addons "nestjs-zod")}}(){{else}}({ schema: UpdateTodoDto.schema }){{/if}} input: UpdateTodoDto
|
|
198
|
+
): Promise<Todo> {
|
|
199
|
+
return await this.todoService.update(id, input);
|
|
200
|
+
}
|
|
142
201
|
{{else}}
|
|
143
202
|
update(
|
|
144
203
|
@Param("id") id: string,
|
|
@@ -146,6 +205,7 @@ export class TodoController {
|
|
|
146
205
|
) {
|
|
147
206
|
return this.todoService.update(id, input);
|
|
148
207
|
}
|
|
208
|
+
{{/if}}
|
|
149
209
|
{{/if}}
|
|
150
210
|
|
|
151
211
|
@Delete(":id")
|
|
@@ -159,7 +219,7 @@ export class TodoController {
|
|
|
159
219
|
@ApiUnauthorizedResponse({ description: "Unauthorized" })
|
|
160
220
|
{{/if}}
|
|
161
221
|
{{/if}}
|
|
162
|
-
{{#if
|
|
222
|
+
{{#if isPassportAuth}}
|
|
163
223
|
delete(
|
|
164
224
|
@Param("id") id: string,
|
|
165
225
|
@CurrentUser() user: { id: string }
|
|
@@ -173,9 +233,22 @@ export class TodoController {
|
|
|
173
233
|
) {
|
|
174
234
|
return this.todoService.delete(id, session.user.id);
|
|
175
235
|
}
|
|
236
|
+
{{else if isBetterAuth}}
|
|
237
|
+
delete(
|
|
238
|
+
@Param("id") id: string,
|
|
239
|
+
@Session() session: UserSession | null
|
|
240
|
+
) {
|
|
241
|
+
return this.todoService.delete(id, this.requireUserId(session));
|
|
242
|
+
}
|
|
243
|
+
{{else}}
|
|
244
|
+
{{#if isPrisma8}}
|
|
245
|
+
async delete(@Param("id") id: string): Promise<{ message: string }> {
|
|
246
|
+
return await this.todoService.delete(id);
|
|
247
|
+
}
|
|
176
248
|
{{else}}
|
|
177
249
|
delete(@Param("id") id: string) {
|
|
178
250
|
return this.todoService.delete(id);
|
|
179
251
|
}
|
|
180
252
|
{{/if}}
|
|
253
|
+
{{/if}}
|
|
181
254
|
}
|
|
@@ -14,13 +14,20 @@ function objectId(id: string): ObjectId {
|
|
|
14
14
|
export class TodoService {
|
|
15
15
|
constructor(private readonly db: DbService) {}
|
|
16
16
|
private get todos() { return this.db.raw.mongo.collection<TodoDocument>("todos"); }
|
|
17
|
-
async findAll() {
|
|
17
|
+
async findAll() {
|
|
18
|
+
const todos = await this.todos.find().sort({ _id: 1 }).limit(100).toArray();
|
|
19
|
+
return todos.map(serialize);
|
|
20
|
+
}
|
|
18
21
|
async create(input: CreateTodoDto) {
|
|
19
22
|
const todo = { title: input.title, completed: input.completed ?? false, ...(input.description === undefined ? {} : { description: input.description }) };
|
|
20
23
|
const result = await this.todos.insertOne(todo);
|
|
21
24
|
return { ...todo, id: result.insertedId.toHexString() };
|
|
22
25
|
}
|
|
26
|
+
{{#if isPrisma8}}
|
|
27
|
+
update(id: string, input: UpdateTodoDto) {
|
|
28
|
+
{{else}}
|
|
23
29
|
async update(id: string, input: UpdateTodoDto) {
|
|
30
|
+
{{/if}}
|
|
24
31
|
const todo = await this.todos.findOneAndUpdate({ _id: objectId(id) }, { $set: input }, { returnDocument: "after" });
|
|
25
32
|
if (!todo) throw new NotFoundException("Todo not found");
|
|
26
33
|
return serialize(todo);
|
|
@@ -32,8 +39,8 @@ export class TodoService {
|
|
|
32
39
|
}
|
|
33
40
|
}
|
|
34
41
|
{{else}}
|
|
35
|
-
{{#if (and hasDatabase
|
|
36
|
-
import { Injectable, NotFoundException } from "@nestjs/common";
|
|
42
|
+
{{#if (or hasTodoExample hasAuth isPrisma8 (and hasDatabase useTypeorm) (and hasDatabase useDrizzle hasMysql))}}
|
|
43
|
+
import { Injectable, NotFoundException{{#if (and hasAuth hasPostgres useRawSql)}}, type OnModuleInit{{/if}} } from "@nestjs/common";
|
|
37
44
|
{{else}}
|
|
38
45
|
import { Injectable } from "@nestjs/common";
|
|
39
46
|
{{/if}}
|
|
@@ -54,31 +61,74 @@ import { eq } from "drizzle-orm";
|
|
|
54
61
|
{{#if useTypeorm}}
|
|
55
62
|
import { Todo } from "../../database/entities/todo.entity.js";
|
|
56
63
|
{{/if}}
|
|
64
|
+
{{#if (and hasAuth hasPostgres useRawSql)}}
|
|
65
|
+
import type { Pool } from "pg";
|
|
66
|
+
{{/if}}
|
|
57
67
|
{{/if}}
|
|
58
68
|
import { CreateTodoDto } from "./dto/create-todo.dto.js";
|
|
59
69
|
import { UpdateTodoDto } from "./dto/update-todo.dto.js";
|
|
70
|
+
{{#if isPrisma8}}
|
|
71
|
+
import type { Models } from "../../prisma/contract.js";
|
|
72
|
+
|
|
73
|
+
type Todo = Models.public_Todo;
|
|
74
|
+
{{/if}}
|
|
60
75
|
|
|
61
76
|
@Injectable()
|
|
77
|
+
{{#if (and hasAuth hasPostgres useRawSql)}}
|
|
78
|
+
export class TodoService implements OnModuleInit {
|
|
79
|
+
{{else}}
|
|
62
80
|
export class TodoService {
|
|
81
|
+
{{/if}}
|
|
63
82
|
{{#if hasDatabase}}
|
|
64
83
|
constructor(private readonly db: DbService) {}
|
|
65
84
|
{{else}}
|
|
66
|
-
private todos:
|
|
85
|
+
private todos: {
|
|
86
|
+
{{#if hasAuth}}
|
|
87
|
+
authorId: string;
|
|
88
|
+
{{/if}}
|
|
67
89
|
completed: boolean;
|
|
68
90
|
description?: string;
|
|
69
91
|
id: string;
|
|
70
92
|
title: string;
|
|
71
|
-
}
|
|
93
|
+
}[] = [];
|
|
72
94
|
private nextId = 1;
|
|
73
95
|
{{/if}}
|
|
74
96
|
|
|
97
|
+
{{#if (and hasAuth hasPostgres useRawSql)}}
|
|
98
|
+
async onModuleInit(): Promise<void> {
|
|
99
|
+
const pool = this.db.raw.sql as Pool;
|
|
100
|
+
await pool.query(`CREATE TABLE IF NOT EXISTS todos (
|
|
101
|
+
id SERIAL PRIMARY KEY,
|
|
102
|
+
title TEXT NOT NULL,
|
|
103
|
+
completed BOOLEAN NOT NULL DEFAULT FALSE,
|
|
104
|
+
description TEXT,
|
|
105
|
+
author_id TEXT NOT NULL,
|
|
106
|
+
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
107
|
+
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
108
|
+
)`);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
{{/if}}
|
|
75
112
|
{{#if hasDatabase}}
|
|
76
113
|
{{#if hasAuth}}
|
|
114
|
+
{{#if isPrisma8}}
|
|
115
|
+
async findAll(authorId: string): Promise<Todo[]> {
|
|
116
|
+
{{else if (and hasPostgres useRawSql)}}
|
|
117
|
+
async findAll(authorId: string) {
|
|
118
|
+
{{else}}
|
|
77
119
|
findAll(authorId: string) {
|
|
120
|
+
{{/if}}
|
|
78
121
|
{{#if isPrisma8}}
|
|
79
|
-
return
|
|
122
|
+
return await this.db.prisma.orm.public.Todo.where({ authorId }).all();
|
|
80
123
|
{{else if usePrisma}}
|
|
81
124
|
return this.db.prisma.todo.findMany({ where: { authorId } });
|
|
125
|
+
{{else if (and hasPostgres useRawSql)}}
|
|
126
|
+
const pool = this.db.raw.sql as Pool;
|
|
127
|
+
const result = await pool.query(
|
|
128
|
+
'SELECT id, title, completed, description, author_id AS "authorId" FROM todos WHERE author_id = $1 ORDER BY id',
|
|
129
|
+
[authorId]
|
|
130
|
+
);
|
|
131
|
+
return result.rows;
|
|
82
132
|
{{else if useTypeorm}}
|
|
83
133
|
return this.db.typeorm.getRepository(Todo).find({ where: { authorId } });
|
|
84
134
|
{{else if useDrizzle}}
|
|
@@ -86,16 +136,32 @@ export class TodoService {
|
|
|
86
136
|
{{/if}}
|
|
87
137
|
}
|
|
88
138
|
|
|
139
|
+
{{#if isPrisma8}}
|
|
140
|
+
async create(input: CreateTodoDto, authorId: string): Promise<Todo> {
|
|
141
|
+
{{else}}
|
|
89
142
|
async create(input: CreateTodoDto, authorId: string) {
|
|
143
|
+
{{/if}}
|
|
90
144
|
{{#if isPrisma8}}
|
|
91
|
-
return
|
|
145
|
+
return await this.db.prisma.orm.public.Todo.create({ ...input, authorId });
|
|
92
146
|
{{else if usePrisma}}
|
|
93
147
|
const todo = await this.db.prisma.todo.create({ data: { ...input, authorId } });
|
|
94
148
|
return todo;
|
|
149
|
+
{{else if (and hasPostgres useRawSql)}}
|
|
150
|
+
const pool = this.db.raw.sql as Pool;
|
|
151
|
+
const result = await pool.query(
|
|
152
|
+
'INSERT INTO todos (title, completed, description, author_id) VALUES ($1, $2, $3, $4) RETURNING id, title, completed, description, author_id AS "authorId"',
|
|
153
|
+
[input.title, input.completed ?? false, input.description ?? null, authorId]
|
|
154
|
+
);
|
|
155
|
+
const [todo] = result.rows;
|
|
156
|
+
return todo;
|
|
95
157
|
{{else if useTypeorm}}
|
|
96
158
|
const todo = await this.db.typeorm.getRepository(Todo).save(this.db.typeorm.getRepository(Todo).create({ ...input, authorId }));
|
|
97
159
|
return todo;
|
|
98
160
|
{{else if useDrizzle}}
|
|
161
|
+
{{#if hasSqlserver}}
|
|
162
|
+
const [todo] = await this.db.drizzle.insert(schema.todos).output().values({ ...input, authorId });
|
|
163
|
+
return todo;
|
|
164
|
+
{{else}}
|
|
99
165
|
{{#if hasMysql}}
|
|
100
166
|
const [inserted] = await this.db.drizzle.insert(schema.todos).values({ ...input, authorId }).$returningId();
|
|
101
167
|
if (!inserted) {
|
|
@@ -107,12 +173,13 @@ export class TodoService {
|
|
|
107
173
|
const [todo] = await this.db.drizzle.insert(schema.todos).values({ ...input, authorId }).returning();
|
|
108
174
|
return todo;
|
|
109
175
|
{{/if}}
|
|
176
|
+
{{/if}}
|
|
110
177
|
{{/if}}
|
|
111
178
|
}
|
|
112
179
|
|
|
113
180
|
async update(id: string, input: UpdateTodoDto, authorId: string) {
|
|
114
181
|
{{#if isPrisma8}}
|
|
115
|
-
const result = await
|
|
182
|
+
const result = await this.db.prisma.orm.public.Todo.where({ authorId, id: Number(id) }).update(input);
|
|
116
183
|
if (!result) {
|
|
117
184
|
throw new NotFoundException("Todo not found");
|
|
118
185
|
}
|
|
@@ -123,6 +190,17 @@ export class TodoService {
|
|
|
123
190
|
throw new NotFoundException("Todo not found");
|
|
124
191
|
}
|
|
125
192
|
return this.db.prisma.todo.update({ data: input, where: { id } });
|
|
193
|
+
{{else if (and hasPostgres useRawSql)}}
|
|
194
|
+
const pool = this.db.raw.sql as Pool;
|
|
195
|
+
const result = await pool.query(
|
|
196
|
+
'UPDATE todos SET title = COALESCE($1, title), completed = COALESCE($2, completed), description = COALESCE($3, description), updated_at = NOW() WHERE id = $4 AND author_id = $5 RETURNING id, title, completed, description, author_id AS "authorId"',
|
|
197
|
+
[input.title ?? null, input.completed ?? null, input.description ?? null, id, authorId]
|
|
198
|
+
);
|
|
199
|
+
const [todo] = result.rows;
|
|
200
|
+
if (!todo) {
|
|
201
|
+
throw new NotFoundException("Todo not found");
|
|
202
|
+
}
|
|
203
|
+
return todo;
|
|
126
204
|
{{else if useTypeorm}}
|
|
127
205
|
const repository = this.db.typeorm.getRepository(Todo);
|
|
128
206
|
const result = await repository.update({ authorId, id }, input);
|
|
@@ -138,6 +216,8 @@ export class TodoService {
|
|
|
138
216
|
{{#if hasMysql}}
|
|
139
217
|
await this.db.drizzle.update(schema.todos).set(input).where(and(eq(schema.todos.authorId, authorId), eq(schema.todos.id, Number(id))));
|
|
140
218
|
const [todo] = await this.db.drizzle.select().from(schema.todos).where(and(eq(schema.todos.authorId, authorId), eq(schema.todos.id, Number(id))));
|
|
219
|
+
{{else if hasSqlserver}}
|
|
220
|
+
const [todo] = await this.db.drizzle.update(schema.todos).set(input).output().where(and(eq(schema.todos.authorId, authorId), eq(schema.todos.id, Number(id))));
|
|
141
221
|
{{else}}
|
|
142
222
|
const [todo] = await this.db.drizzle.update(schema.todos).set(input).where(and(eq(schema.todos.authorId, authorId), eq(schema.todos.id, Number(id)))).returning();
|
|
143
223
|
{{/if}}
|
|
@@ -150,7 +230,7 @@ export class TodoService {
|
|
|
150
230
|
|
|
151
231
|
async delete(id: string, authorId: string) {
|
|
152
232
|
{{#if isPrisma8}}
|
|
153
|
-
const count = await
|
|
233
|
+
const count = await this.db.prisma.orm.public.Todo.where({ authorId, id }).delete();
|
|
154
234
|
if (count === 0) {
|
|
155
235
|
throw new NotFoundException("Todo not found");
|
|
156
236
|
}
|
|
@@ -159,6 +239,16 @@ export class TodoService {
|
|
|
159
239
|
if (result.count === 0) {
|
|
160
240
|
throw new NotFoundException("Todo not found");
|
|
161
241
|
}
|
|
242
|
+
{{else if (and hasPostgres useRawSql)}}
|
|
243
|
+
const pool = this.db.raw.sql as Pool;
|
|
244
|
+
const result = await pool.query(
|
|
245
|
+
'DELETE FROM todos WHERE id = $1 AND author_id = $2 RETURNING id',
|
|
246
|
+
[id, authorId]
|
|
247
|
+
);
|
|
248
|
+
const [todo] = result.rows;
|
|
249
|
+
if (!todo) {
|
|
250
|
+
throw new NotFoundException("Todo not found");
|
|
251
|
+
}
|
|
162
252
|
{{else if useTypeorm}}
|
|
163
253
|
const result = await this.db.typeorm.getRepository(Todo).delete({ authorId, id });
|
|
164
254
|
if (result.affected === 0) {
|
|
@@ -171,6 +261,11 @@ export class TodoService {
|
|
|
171
261
|
throw new NotFoundException("Todo not found");
|
|
172
262
|
}
|
|
173
263
|
await this.db.drizzle.delete(schema.todos).where(and(eq(schema.todos.authorId, authorId), eq(schema.todos.id, Number(id))));
|
|
264
|
+
{{else if hasSqlserver}}
|
|
265
|
+
const [todo] = await this.db.drizzle.delete(schema.todos).output().where(and(eq(schema.todos.authorId, authorId), eq(schema.todos.id, Number(id))));
|
|
266
|
+
if (!todo) {
|
|
267
|
+
throw new NotFoundException("Todo not found");
|
|
268
|
+
}
|
|
174
269
|
{{else}}
|
|
175
270
|
const [todo] = await this.db.drizzle.delete(schema.todos).where(and(eq(schema.todos.authorId, authorId), eq(schema.todos.id, Number(id)))).returning();
|
|
176
271
|
if (!todo) {
|
|
@@ -180,10 +275,14 @@ export class TodoService {
|
|
|
180
275
|
{{/if}}
|
|
181
276
|
return { message: "Todo deleted successfully" };
|
|
182
277
|
}
|
|
278
|
+
{{else}}
|
|
279
|
+
{{#if isPrisma8}}
|
|
280
|
+
async findAll(): Promise<Todo[]> {
|
|
183
281
|
{{else}}
|
|
184
282
|
findAll() {
|
|
283
|
+
{{/if}}
|
|
185
284
|
{{#if isPrisma8}}
|
|
186
|
-
return
|
|
285
|
+
return await this.db.prisma.orm.public.Todo.all();
|
|
187
286
|
{{else if usePrisma}}
|
|
188
287
|
return this.db.prisma.todo.findMany();
|
|
189
288
|
{{else if useTypeorm}}
|
|
@@ -193,9 +292,13 @@ export class TodoService {
|
|
|
193
292
|
{{/if}}
|
|
194
293
|
}
|
|
195
294
|
|
|
295
|
+
{{#if isPrisma8}}
|
|
296
|
+
async create(input: CreateTodoDto): Promise<Todo> {
|
|
297
|
+
{{else}}
|
|
196
298
|
async create(input: CreateTodoDto) {
|
|
299
|
+
{{/if}}
|
|
197
300
|
{{#if isPrisma8}}
|
|
198
|
-
return
|
|
301
|
+
return await this.db.prisma.orm.public.Todo.create(input);
|
|
199
302
|
{{else if usePrisma}}
|
|
200
303
|
const todo = await this.db.prisma.todo.create({ data: input });
|
|
201
304
|
return todo;
|
|
@@ -203,64 +306,117 @@ export class TodoService {
|
|
|
203
306
|
const todo = await this.db.typeorm.getRepository(Todo).save(this.db.typeorm.getRepository(Todo).create(input));
|
|
204
307
|
return todo;
|
|
205
308
|
{{else if useDrizzle}}
|
|
309
|
+
{{#if hasSqlserver}}
|
|
310
|
+
const [todo] = await this.db.drizzle.insert(schema.todos).output().values(input);
|
|
311
|
+
{{else if hasMysql}}
|
|
312
|
+
const [inserted] = await this.db.drizzle.insert(schema.todos).values(input).$returningId();
|
|
313
|
+
if (!inserted) {
|
|
314
|
+
throw new NotFoundException("Todo not found");
|
|
315
|
+
}
|
|
316
|
+
const [todo] = await this.db.drizzle.select().from(schema.todos).where(eq(schema.todos.id, inserted.id));
|
|
317
|
+
{{else}}
|
|
206
318
|
const [todo] = await this.db.drizzle.insert(schema.todos).values(input).returning();
|
|
319
|
+
{{/if}}
|
|
207
320
|
return todo;
|
|
208
321
|
{{/if}}
|
|
209
322
|
}
|
|
210
323
|
|
|
324
|
+
{{#if isPrisma8}}
|
|
325
|
+
async update(id: string, input: UpdateTodoDto): Promise<Todo> {
|
|
326
|
+
{{else}}
|
|
211
327
|
async update(id: string, input: UpdateTodoDto) {
|
|
328
|
+
{{/if}}
|
|
212
329
|
{{#if isPrisma8}}
|
|
213
|
-
|
|
330
|
+
const todo = await this.db.prisma.orm.public.Todo.where({ id: Number(id) }).update(input);
|
|
331
|
+
if (!todo) {
|
|
332
|
+
throw new NotFoundException("Todo not found");
|
|
333
|
+
}
|
|
334
|
+
return todo;
|
|
214
335
|
{{else if usePrisma}}
|
|
215
336
|
const result = await this.db.prisma.todo.update({ data: input, where: { id } });
|
|
216
337
|
return result;
|
|
217
338
|
{{else if useTypeorm}}
|
|
218
|
-
const
|
|
219
|
-
|
|
339
|
+
const repository = this.db.typeorm.getRepository(Todo);
|
|
340
|
+
const result = await repository.update(id, input);
|
|
341
|
+
if (result.affected === 0) {
|
|
342
|
+
throw new NotFoundException("Todo not found");
|
|
343
|
+
}
|
|
344
|
+
const todo = await repository.findOneBy({ id });
|
|
345
|
+
if (!todo) {
|
|
346
|
+
throw new NotFoundException("Todo not found");
|
|
347
|
+
}
|
|
348
|
+
return todo;
|
|
220
349
|
{{else if useDrizzle}}
|
|
350
|
+
{{#if hasSqlserver}}
|
|
351
|
+
const [todo] = await this.db.drizzle.update(schema.todos).set(input).output().where(eq(schema.todos.id, Number(id)));
|
|
352
|
+
if (!todo) {
|
|
353
|
+
throw new NotFoundException("Todo not found");
|
|
354
|
+
}
|
|
355
|
+
{{else if hasMysql}}
|
|
356
|
+
await this.db.drizzle.update(schema.todos).set(input).where(eq(schema.todos.id, Number(id)));
|
|
357
|
+
const [todo] = await this.db.drizzle.select().from(schema.todos).where(eq(schema.todos.id, Number(id)));
|
|
358
|
+
if (!todo) {
|
|
359
|
+
throw new NotFoundException("Todo not found");
|
|
360
|
+
}
|
|
361
|
+
{{else}}
|
|
221
362
|
const [todo] = await this.db.drizzle.update(schema.todos).set(input).where(eq(schema.todos.id, Number(id))).returning();
|
|
363
|
+
{{/if}}
|
|
222
364
|
return todo;
|
|
223
365
|
{{/if}}
|
|
224
366
|
}
|
|
225
367
|
|
|
226
368
|
async delete(id: string) {
|
|
227
369
|
{{#if isPrisma8}}
|
|
228
|
-
await
|
|
370
|
+
await this.db.prisma.orm.public.Todo.where({ id: Number(id) }).delete();
|
|
229
371
|
{{else if usePrisma}}
|
|
230
372
|
await this.db.prisma.todo.delete({ where: { id } });
|
|
231
373
|
{{else if useTypeorm}}
|
|
232
374
|
await this.db.typeorm.getRepository(Todo).delete(id);
|
|
233
375
|
{{else if useDrizzle}}
|
|
376
|
+
{{#if hasSqlserver}}
|
|
377
|
+
const [todo] = await this.db.drizzle.delete(schema.todos).output().where(eq(schema.todos.id, Number(id)));
|
|
378
|
+
if (!todo) {
|
|
379
|
+
throw new NotFoundException("Todo not found");
|
|
380
|
+
}
|
|
381
|
+
{{else}}
|
|
234
382
|
await this.db.drizzle.delete(schema.todos).where(eq(schema.todos.id, Number(id)));
|
|
383
|
+
{{/if}}
|
|
235
384
|
{{/if}}
|
|
236
385
|
return { message: "Todo deleted successfully" };
|
|
237
386
|
}
|
|
238
387
|
{{/if}}
|
|
239
388
|
{{else}}
|
|
240
|
-
findAll() {
|
|
389
|
+
findAll({{#if hasAuth}}authorId: string{{/if}}) {
|
|
390
|
+
{{#if hasAuth}}
|
|
391
|
+
return this.todos.filter((todo) => todo.authorId === authorId);
|
|
392
|
+
{{else}}
|
|
241
393
|
return [...this.todos];
|
|
394
|
+
{{/if}}
|
|
242
395
|
}
|
|
243
396
|
|
|
244
|
-
create(input: CreateTodoDto) {
|
|
245
|
-
const
|
|
397
|
+
create(input: CreateTodoDto{{#if hasAuth}}, authorId: string{{/if}}) {
|
|
398
|
+
const id = String(this.nextId);
|
|
399
|
+
this.nextId += 1;
|
|
400
|
+
const todo = { id, ...input{{#if hasAuth}}, authorId{{/if}} };
|
|
246
401
|
this.todos.push(todo);
|
|
247
402
|
return todo;
|
|
248
403
|
}
|
|
249
404
|
|
|
250
|
-
update(id: string, input: UpdateTodoDto) {
|
|
251
|
-
const todo = this.todos.find((
|
|
405
|
+
update(id: string, input: UpdateTodoDto{{#if hasAuth}}, authorId: string{{/if}}) {
|
|
406
|
+
const todo = this.todos.find((item) => item.id === id{{#if hasAuth}} && item.authorId === authorId{{/if}});
|
|
252
407
|
if (!todo) {
|
|
253
|
-
|
|
408
|
+
throw new NotFoundException("Todo not found");
|
|
254
409
|
}
|
|
255
410
|
Object.assign(todo, input);
|
|
256
411
|
return todo;
|
|
257
412
|
}
|
|
258
413
|
|
|
259
|
-
|
|
260
|
-
const index = this.todos.findIndex((
|
|
261
|
-
if (index
|
|
262
|
-
|
|
414
|
+
delete(id: string{{#if hasAuth}}, authorId: string{{/if}}) {
|
|
415
|
+
const index = this.todos.findIndex((item) => item.id === id{{#if hasAuth}} && item.authorId === authorId{{/if}});
|
|
416
|
+
if (index === -1) {
|
|
417
|
+
throw new NotFoundException("Todo not found");
|
|
263
418
|
}
|
|
419
|
+
this.todos.splice(index, 1);
|
|
264
420
|
return { message: "Todo deleted successfully" };
|
|
265
421
|
}
|
|
266
422
|
{{/if}}
|