@trinacria/cli 0.1.1-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of @trinacria/cli might be problematic. Click here for more details.
- package/README.md +109 -0
- package/README.npm.md +109 -0
- package/dist/cli.d.ts +19 -0
- package/dist/cli.js +88 -0
- package/dist/commands/build.d.ts +2 -0
- package/dist/commands/build.js +102 -0
- package/dist/commands/dev.d.ts +28 -0
- package/dist/commands/dev.js +186 -0
- package/dist/commands/new.d.ts +41 -0
- package/dist/commands/new.js +280 -0
- package/dist/commands/start.d.ts +13 -0
- package/dist/commands/start.js +110 -0
- package/dist/config/config.contract.d.ts +9 -0
- package/dist/config/config.contract.js +2 -0
- package/dist/config/default-config.d.ts +2 -0
- package/dist/config/default-config.js +11 -0
- package/dist/config/load-config.d.ts +2 -0
- package/dist/config/load-config.js +113 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +5 -0
- package/dist/templates/api-events-rabbitmq/.env.example +23 -0
- package/dist/templates/api-events-rabbitmq/Dockerfile +18 -0
- package/dist/templates/api-events-rabbitmq/README.md +97 -0
- package/dist/templates/api-events-rabbitmq/docker-compose.yml +30 -0
- package/dist/templates/api-events-rabbitmq/package.json +21 -0
- package/dist/templates/api-events-rabbitmq/src/global/config.service.ts +95 -0
- package/dist/templates/api-events-rabbitmq/src/global/controllers/swagger/docs.html +28 -0
- package/dist/templates/api-events-rabbitmq/src/global/controllers/swagger/swagger-docs.controller.ts +79 -0
- package/dist/templates/api-events-rabbitmq/src/global/rabbitmq.service.ts +86 -0
- package/dist/templates/api-events-rabbitmq/src/global/register-global-controllers.ts +20 -0
- package/dist/templates/api-events-rabbitmq/src/main.ts +89 -0
- package/dist/templates/api-events-rabbitmq/src/modules/events/dto/event.dto.ts +24 -0
- package/dist/templates/api-events-rabbitmq/src/modules/events/dto/index.ts +2 -0
- package/dist/templates/api-events-rabbitmq/src/modules/events/dto/publish-event.dto.ts +12 -0
- package/dist/templates/api-events-rabbitmq/src/modules/events/events.controller.ts +76 -0
- package/dist/templates/api-events-rabbitmq/src/modules/events/events.module.ts +27 -0
- package/dist/templates/api-events-rabbitmq/src/modules/events/events.provider.ts +28 -0
- package/dist/templates/api-events-rabbitmq/src/modules/events/events.service.ts +19 -0
- package/dist/templates/api-events-rabbitmq/src/modules/events/events.store.ts +42 -0
- package/dist/templates/api-events-rabbitmq/src/modules/events/events.tokens.ts +11 -0
- package/dist/templates/api-events-rabbitmq/trinacria.config.mjs +12 -0
- package/dist/templates/api-events-rabbitmq/tsconfig.json +14 -0
- package/dist/templates/api-events-redis/.env.example +18 -0
- package/dist/templates/api-events-redis/Dockerfile +18 -0
- package/dist/templates/api-events-redis/README.md +97 -0
- package/dist/templates/api-events-redis/docker-compose.yml +33 -0
- package/dist/templates/api-events-redis/package.json +18 -0
- package/dist/templates/api-events-redis/src/global/config.service.ts +93 -0
- package/dist/templates/api-events-redis/src/global/controllers/swagger/docs.html +28 -0
- package/dist/templates/api-events-redis/src/global/controllers/swagger/swagger-docs.controller.ts +79 -0
- package/dist/templates/api-events-redis/src/global/redis.service.ts +50 -0
- package/dist/templates/api-events-redis/src/global/register-global-controllers.ts +20 -0
- package/dist/templates/api-events-redis/src/main.ts +88 -0
- package/dist/templates/api-events-redis/src/modules/events/dto/event.dto.ts +24 -0
- package/dist/templates/api-events-redis/src/modules/events/dto/index.ts +2 -0
- package/dist/templates/api-events-redis/src/modules/events/dto/publish-event.dto.ts +12 -0
- package/dist/templates/api-events-redis/src/modules/events/events.controller.ts +76 -0
- package/dist/templates/api-events-redis/src/modules/events/events.module.ts +27 -0
- package/dist/templates/api-events-redis/src/modules/events/events.provider.ts +28 -0
- package/dist/templates/api-events-redis/src/modules/events/events.service.ts +19 -0
- package/dist/templates/api-events-redis/src/modules/events/events.store.ts +42 -0
- package/dist/templates/api-events-redis/src/modules/events/events.tokens.ts +11 -0
- package/dist/templates/api-events-redis/trinacria.config.mjs +12 -0
- package/dist/templates/api-events-redis/tsconfig.json +14 -0
- package/dist/templates/api-mongoose-mongodb/.env.example +17 -0
- package/dist/templates/api-mongoose-mongodb/Dockerfile +18 -0
- package/dist/templates/api-mongoose-mongodb/README.md +98 -0
- package/dist/templates/api-mongoose-mongodb/docker-compose.yml +34 -0
- package/dist/templates/api-mongoose-mongodb/package.json +17 -0
- package/dist/templates/api-mongoose-mongodb/src/global/config.service.ts +92 -0
- package/dist/templates/api-mongoose-mongodb/src/global/controllers/swagger/docs.html +28 -0
- package/dist/templates/api-mongoose-mongodb/src/global/controllers/swagger/swagger-docs.controller.ts +79 -0
- package/dist/templates/api-mongoose-mongodb/src/global/mongoose-schema.provider.ts +18 -0
- package/dist/templates/api-mongoose-mongodb/src/global/mongoose.service.ts +36 -0
- package/dist/templates/api-mongoose-mongodb/src/global/register-global-controllers.ts +20 -0
- package/dist/templates/api-mongoose-mongodb/src/main.ts +70 -0
- package/dist/templates/api-mongoose-mongodb/src/modules/users/dto/create-user.dto.ts +14 -0
- package/dist/templates/api-mongoose-mongodb/src/modules/users/dto/index.ts +2 -0
- package/dist/templates/api-mongoose-mongodb/src/modules/users/dto/public-user.dto.ts +22 -0
- package/dist/templates/api-mongoose-mongodb/src/modules/users/users.controller.ts +89 -0
- package/dist/templates/api-mongoose-mongodb/src/modules/users/users.module.ts +17 -0
- package/dist/templates/api-mongoose-mongodb/src/modules/users/users.schema.ts +35 -0
- package/dist/templates/api-mongoose-mongodb/src/modules/users/users.service.ts +35 -0
- package/dist/templates/api-mongoose-mongodb/src/modules/users/users.tokens.ts +9 -0
- package/dist/templates/api-mongoose-mongodb/trinacria.config.mjs +12 -0
- package/dist/templates/api-mongoose-mongodb/tsconfig.json +14 -0
- package/dist/templates/api-prisma-postgresql/.env.example +19 -0
- package/dist/templates/api-prisma-postgresql/Dockerfile +24 -0
- package/dist/templates/api-prisma-postgresql/README.md +107 -0
- package/dist/templates/api-prisma-postgresql/docker-compose.yml +38 -0
- package/dist/templates/api-prisma-postgresql/package.json +26 -0
- package/dist/templates/api-prisma-postgresql/prisma/schema.prisma +15 -0
- package/dist/templates/api-prisma-postgresql/prisma.config.ts +9 -0
- package/dist/templates/api-prisma-postgresql/src/global/config.service.ts +92 -0
- package/dist/templates/api-prisma-postgresql/src/global/controllers/swagger/docs.html +28 -0
- package/dist/templates/api-prisma-postgresql/src/global/controllers/swagger/swagger-docs.controller.ts +79 -0
- package/dist/templates/api-prisma-postgresql/src/global/prisma.service.ts +20 -0
- package/dist/templates/api-prisma-postgresql/src/global/register-global-controllers.ts +20 -0
- package/dist/templates/api-prisma-postgresql/src/main.ts +70 -0
- package/dist/templates/api-prisma-postgresql/src/modules/users/dto/create-user.dto.ts +14 -0
- package/dist/templates/api-prisma-postgresql/src/modules/users/dto/index.ts +2 -0
- package/dist/templates/api-prisma-postgresql/src/modules/users/dto/public-user.dto.ts +22 -0
- package/dist/templates/api-prisma-postgresql/src/modules/users/users.controller.ts +89 -0
- package/dist/templates/api-prisma-postgresql/src/modules/users/users.module.ts +15 -0
- package/dist/templates/api-prisma-postgresql/src/modules/users/users.service.ts +36 -0
- package/dist/templates/api-prisma-postgresql/src/modules/users/users.tokens.ts +7 -0
- package/dist/templates/api-prisma-postgresql/trinacria.config.mjs +12 -0
- package/dist/templates/api-prisma-postgresql/tsconfig.json +14 -0
- package/dist/templates/app-starter/.env.example +1 -0
- package/dist/templates/app-starter/Dockerfile +13 -0
- package/dist/templates/app-starter/README.md +32 -0
- package/dist/templates/app-starter/docker-compose.yml +8 -0
- package/dist/templates/app-starter/package.json +14 -0
- package/dist/templates/app-starter/src/main.ts +14 -0
- package/dist/templates/app-starter/trinacria.config.mjs +12 -0
- package/dist/templates/app-starter/tsconfig.json +14 -0
- package/dist/templates/cron-example/.env.example +11 -0
- package/dist/templates/cron-example/Dockerfile +13 -0
- package/dist/templates/cron-example/README.md +68 -0
- package/dist/templates/cron-example/docker-compose.yml +11 -0
- package/dist/templates/cron-example/package.json +15 -0
- package/dist/templates/cron-example/src/config.service.ts +46 -0
- package/dist/templates/cron-example/src/main.ts +41 -0
- package/dist/templates/cron-example/src/modules/cron/cron.module.ts +15 -0
- package/dist/templates/cron-example/src/modules/cron/cron.tokens.ts +6 -0
- package/dist/templates/cron-example/src/modules/cron/example-cron-jobs.provider.ts +48 -0
- package/dist/templates/cron-example/trinacria.config.mjs +12 -0
- package/dist/templates/cron-example/tsconfig.json +14 -0
- package/package.json +32 -0
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import mongoose, { type Connection } from "mongoose";
|
|
2
|
+
import { createToken } from "@trinacria/core";
|
|
3
|
+
import type { ConfigService } from "./config.service";
|
|
4
|
+
|
|
5
|
+
export const MONGOOSE_SERVICE =
|
|
6
|
+
createToken<MongooseService>("MONGOOSE_SERVICE");
|
|
7
|
+
|
|
8
|
+
export class MongooseService {
|
|
9
|
+
private connection?: Connection;
|
|
10
|
+
|
|
11
|
+
constructor(private readonly config: ConfigService) {}
|
|
12
|
+
|
|
13
|
+
async onInit(): Promise<void> {
|
|
14
|
+
if (this.connection?.readyState === 1) {
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
await mongoose.connect(this.config.get("DATABASE_URL"));
|
|
19
|
+
this.connection = mongoose.connection;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
getConnection(): Connection {
|
|
23
|
+
if (!this.connection || this.connection.readyState !== 1) {
|
|
24
|
+
throw new Error("Mongoose connection is not ready");
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return this.connection;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
async onDestroy(): Promise<void> {
|
|
31
|
+
if (this.connection) {
|
|
32
|
+
await mongoose.disconnect();
|
|
33
|
+
this.connection = undefined;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { TrinacriaApp } from "@trinacria/core";
|
|
2
|
+
import { httpProvider } from "@trinacria/http";
|
|
3
|
+
import { ConfigService, CONFIG_SERVICE } from "./config.service";
|
|
4
|
+
import {
|
|
5
|
+
SWAGGER_DOCS_CONTROLLER,
|
|
6
|
+
SwaggerDocsController,
|
|
7
|
+
} from "./controllers/swagger/swagger-docs.controller";
|
|
8
|
+
|
|
9
|
+
export function registerGlobalControllers(
|
|
10
|
+
app: TrinacriaApp,
|
|
11
|
+
config: ConfigService,
|
|
12
|
+
): void {
|
|
13
|
+
if (config.get("OPENAPI_ENABLED")) {
|
|
14
|
+
app.registerGlobalProvider(
|
|
15
|
+
httpProvider(SWAGGER_DOCS_CONTROLLER, SwaggerDocsController, [
|
|
16
|
+
CONFIG_SERVICE,
|
|
17
|
+
]),
|
|
18
|
+
);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { TrinacriaApp, classProvider, valueProvider } from "@trinacria/core";
|
|
2
|
+
import {
|
|
3
|
+
cors,
|
|
4
|
+
createHttpPlugin,
|
|
5
|
+
createSecurityHeadersBuilder,
|
|
6
|
+
rateLimit,
|
|
7
|
+
requestId,
|
|
8
|
+
requestLogger,
|
|
9
|
+
requestTimeout,
|
|
10
|
+
} from "@trinacria/http";
|
|
11
|
+
import { CONFIG_SERVICE, ConfigService } from "./global/config.service";
|
|
12
|
+
import { MONGOOSE_SERVICE, MongooseService } from "./global/mongoose.service";
|
|
13
|
+
import { registerGlobalControllers } from "./global/register-global-controllers";
|
|
14
|
+
import { UsersModule } from "./modules/users/users.module";
|
|
15
|
+
|
|
16
|
+
async function bootstrap() {
|
|
17
|
+
const app = new TrinacriaApp();
|
|
18
|
+
const configService = new ConfigService();
|
|
19
|
+
const config = configService.getAll();
|
|
20
|
+
const isProduction = config.ENV === "production";
|
|
21
|
+
const corsOrigins = config.CORS_ALLOWED_ORIGINS;
|
|
22
|
+
const securityHeadersMiddleware = createSecurityHeadersBuilder()
|
|
23
|
+
.preset(config.ENV)
|
|
24
|
+
.trustProxy(false)
|
|
25
|
+
.build();
|
|
26
|
+
|
|
27
|
+
app.registerGlobalProvider(valueProvider(CONFIG_SERVICE, configService));
|
|
28
|
+
app.registerGlobalProvider(
|
|
29
|
+
classProvider(MONGOOSE_SERVICE, MongooseService, [CONFIG_SERVICE]),
|
|
30
|
+
);
|
|
31
|
+
registerGlobalControllers(app, configService);
|
|
32
|
+
|
|
33
|
+
app.use(
|
|
34
|
+
createHttpPlugin({
|
|
35
|
+
host: config.HOST,
|
|
36
|
+
port: config.PORT,
|
|
37
|
+
middlewares: [
|
|
38
|
+
requestId(),
|
|
39
|
+
requestLogger({ includeUserAgent: !isProduction }),
|
|
40
|
+
cors({
|
|
41
|
+
origin: corsOrigins.length > 0 ? corsOrigins : "*",
|
|
42
|
+
credentials: true,
|
|
43
|
+
methods: ["GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS", "HEAD"],
|
|
44
|
+
}),
|
|
45
|
+
rateLimit({
|
|
46
|
+
windowMs: 60_000,
|
|
47
|
+
max: isProduction ? 240 : 2_000,
|
|
48
|
+
trustProxy: false,
|
|
49
|
+
}),
|
|
50
|
+
requestTimeout({ timeoutMs: 15_000 }),
|
|
51
|
+
securityHeadersMiddleware,
|
|
52
|
+
],
|
|
53
|
+
openApi: config.OPENAPI_ENABLED
|
|
54
|
+
? {
|
|
55
|
+
enabled: true,
|
|
56
|
+
title: "API Mongoose MongoDB Example",
|
|
57
|
+
version: "1.0.0",
|
|
58
|
+
}
|
|
59
|
+
: undefined,
|
|
60
|
+
}),
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
await app.registerModule(UsersModule);
|
|
64
|
+
await app.start();
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
bootstrap().catch((error) => {
|
|
68
|
+
console.error(error);
|
|
69
|
+
process.exit(1);
|
|
70
|
+
});
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { s } from "@trinacria/schema";
|
|
2
|
+
|
|
3
|
+
export interface CreateUserDto {
|
|
4
|
+
name: string;
|
|
5
|
+
email: string;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export const CreateUserDtoSchema = s.objectOf<CreateUserDto>()(
|
|
9
|
+
{
|
|
10
|
+
name: s.string({ trim: true, minLength: 1 }),
|
|
11
|
+
email: s.string({ trim: true, toLowerCase: true, email: true }),
|
|
12
|
+
},
|
|
13
|
+
{ strict: true },
|
|
14
|
+
);
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { s } from "@trinacria/schema";
|
|
2
|
+
|
|
3
|
+
export interface PublicUserDto {
|
|
4
|
+
id: string;
|
|
5
|
+
name: string;
|
|
6
|
+
email: string;
|
|
7
|
+
createdAt: Date;
|
|
8
|
+
updatedAt: Date;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export const PublicUserDtoSchema = s.objectOf<PublicUserDto>()(
|
|
12
|
+
{
|
|
13
|
+
id: s.string({ trim: true, minLength: 1 }),
|
|
14
|
+
name: s.string({ trim: true, minLength: 1 }),
|
|
15
|
+
email: s.string({ trim: true, toLowerCase: true, email: true }),
|
|
16
|
+
createdAt: s.date(),
|
|
17
|
+
updatedAt: s.date(),
|
|
18
|
+
},
|
|
19
|
+
{ strict: true },
|
|
20
|
+
);
|
|
21
|
+
|
|
22
|
+
export const PublicUserListDtoSchema = s.array(PublicUserDtoSchema);
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ConflictException,
|
|
3
|
+
HttpController,
|
|
4
|
+
HttpContext,
|
|
5
|
+
response,
|
|
6
|
+
} from "@trinacria/http";
|
|
7
|
+
import { UsersService } from "./users.service";
|
|
8
|
+
import {
|
|
9
|
+
CreateUserDtoSchema,
|
|
10
|
+
PublicUserDtoSchema,
|
|
11
|
+
PublicUserListDtoSchema,
|
|
12
|
+
} from "./dto";
|
|
13
|
+
|
|
14
|
+
export class UsersController extends HttpController {
|
|
15
|
+
constructor(private readonly users: UsersService) {
|
|
16
|
+
super();
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
routes() {
|
|
20
|
+
return this.router()
|
|
21
|
+
.get("/health", () => ({ status: "ok" }), {
|
|
22
|
+
docs: {
|
|
23
|
+
tags: ["Health"],
|
|
24
|
+
summary: "Health check",
|
|
25
|
+
},
|
|
26
|
+
})
|
|
27
|
+
.get("/users", this.listUsers, {
|
|
28
|
+
docs: {
|
|
29
|
+
tags: ["Users"],
|
|
30
|
+
summary: "List users",
|
|
31
|
+
responses: {
|
|
32
|
+
200: {
|
|
33
|
+
description: "Users list",
|
|
34
|
+
schema: PublicUserListDtoSchema.toOpenApi(),
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
})
|
|
39
|
+
.post("/users", this.createUser, {
|
|
40
|
+
docs: {
|
|
41
|
+
tags: ["Users"],
|
|
42
|
+
summary: "Create user",
|
|
43
|
+
requestBody: {
|
|
44
|
+
required: true,
|
|
45
|
+
schema: CreateUserDtoSchema.toOpenApi(),
|
|
46
|
+
},
|
|
47
|
+
responses: {
|
|
48
|
+
201: {
|
|
49
|
+
description: "Created user",
|
|
50
|
+
schema: PublicUserDtoSchema.toOpenApi(),
|
|
51
|
+
},
|
|
52
|
+
409: {
|
|
53
|
+
description: "Email already exists",
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
})
|
|
58
|
+
.build();
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
async listUsers() {
|
|
62
|
+
return this.users.list();
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
async createUser(ctx: HttpContext) {
|
|
66
|
+
const payload = CreateUserDtoSchema.parse(ctx.body);
|
|
67
|
+
|
|
68
|
+
try {
|
|
69
|
+
const created = await this.users.create(payload);
|
|
70
|
+
return response(created, {
|
|
71
|
+
status: 201,
|
|
72
|
+
headers: {
|
|
73
|
+
location: `/users/${created.id}`,
|
|
74
|
+
},
|
|
75
|
+
});
|
|
76
|
+
} catch (error) {
|
|
77
|
+
if (
|
|
78
|
+
error &&
|
|
79
|
+
typeof error === "object" &&
|
|
80
|
+
"code" in error &&
|
|
81
|
+
error.code === 11000
|
|
82
|
+
) {
|
|
83
|
+
throw new ConflictException("Email already exists");
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
throw error;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { classProvider, defineModule } from "@trinacria/core";
|
|
2
|
+
import { httpProvider } from "@trinacria/http";
|
|
3
|
+
import { createMongooseSchemaProvider } from "../../global/mongoose-schema.provider";
|
|
4
|
+
import { USERS_CONTROLLER, USERS_SERVICE, USER_SCHEMA } from "./users.tokens";
|
|
5
|
+
import { UsersController } from "./users.controller";
|
|
6
|
+
import { userSchema } from "./users.schema";
|
|
7
|
+
import { UsersService } from "./users.service";
|
|
8
|
+
|
|
9
|
+
export const UsersModule = defineModule({
|
|
10
|
+
name: "UsersModule",
|
|
11
|
+
providers: [
|
|
12
|
+
createMongooseSchemaProvider(USER_SCHEMA, "User", userSchema),
|
|
13
|
+
classProvider(USERS_SERVICE, UsersService, [USER_SCHEMA]),
|
|
14
|
+
httpProvider(USERS_CONTROLLER, UsersController, [USERS_SERVICE]),
|
|
15
|
+
],
|
|
16
|
+
exports: [USERS_SERVICE, USERS_CONTROLLER],
|
|
17
|
+
});
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { type HydratedDocument, type Model, Schema } from "mongoose";
|
|
2
|
+
|
|
3
|
+
export interface UserEntity {
|
|
4
|
+
name: string;
|
|
5
|
+
email: string;
|
|
6
|
+
createdAt: Date;
|
|
7
|
+
updatedAt: Date;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export type UserDocument = HydratedDocument<UserEntity>;
|
|
11
|
+
export type UserSchemaModel = Model<UserEntity>;
|
|
12
|
+
|
|
13
|
+
const userSchema = new Schema<UserEntity>(
|
|
14
|
+
{
|
|
15
|
+
name: {
|
|
16
|
+
type: String,
|
|
17
|
+
required: true,
|
|
18
|
+
trim: true,
|
|
19
|
+
},
|
|
20
|
+
email: {
|
|
21
|
+
type: String,
|
|
22
|
+
required: true,
|
|
23
|
+
unique: true,
|
|
24
|
+
lowercase: true,
|
|
25
|
+
trim: true,
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
timestamps: true,
|
|
30
|
+
versionKey: false,
|
|
31
|
+
},
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
userSchema.index({ email: 1 }, { unique: true });
|
|
35
|
+
export { userSchema };
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { CreateUserDto, PublicUserDto } from "./dto";
|
|
2
|
+
import type { UserDocument, UserSchemaModel } from "./users.schema";
|
|
3
|
+
|
|
4
|
+
export class UsersService {
|
|
5
|
+
constructor(private readonly userSchema: UserSchemaModel) {}
|
|
6
|
+
|
|
7
|
+
async list(): Promise<PublicUserDto[]> {
|
|
8
|
+
const users: UserDocument[] = await this.userSchema
|
|
9
|
+
.find({}, undefined, { sort: { createdAt: -1 } })
|
|
10
|
+
.exec();
|
|
11
|
+
|
|
12
|
+
return users.map((user: UserDocument) => ({
|
|
13
|
+
id: String(user._id),
|
|
14
|
+
name: user.name,
|
|
15
|
+
email: user.email,
|
|
16
|
+
createdAt: user.createdAt,
|
|
17
|
+
updatedAt: user.updatedAt,
|
|
18
|
+
}));
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
async create(input: CreateUserDto): Promise<PublicUserDto> {
|
|
22
|
+
const created = await this.userSchema.create({
|
|
23
|
+
name: input.name,
|
|
24
|
+
email: input.email.toLowerCase(),
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
return {
|
|
28
|
+
id: String(created._id),
|
|
29
|
+
name: created.name,
|
|
30
|
+
email: created.email,
|
|
31
|
+
createdAt: created.createdAt,
|
|
32
|
+
updatedAt: created.updatedAt,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { createToken } from "@trinacria/core";
|
|
2
|
+
import type { UserSchemaModel } from "./users.schema";
|
|
3
|
+
import { UsersController } from "./users.controller";
|
|
4
|
+
import { UsersService } from "./users.service";
|
|
5
|
+
|
|
6
|
+
export const USER_SCHEMA = createToken<UserSchemaModel>("USER_SCHEMA");
|
|
7
|
+
export const USERS_SERVICE = createToken<UsersService>("USERS_SERVICE");
|
|
8
|
+
export const USERS_CONTROLLER =
|
|
9
|
+
createToken<UsersController>("USERS_CONTROLLER");
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/** @type {import('@trinacria/cli').TrinacriaConfig} */
|
|
2
|
+
|
|
3
|
+
const config = {
|
|
4
|
+
entry: "src/main.ts",
|
|
5
|
+
outDir: "dist",
|
|
6
|
+
watchDir: "src",
|
|
7
|
+
env: "development",
|
|
8
|
+
crashLoopWindowMs: 15_000,
|
|
9
|
+
maxConsecutiveCrashRestarts: 3,
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export default config;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"types": ["node"],
|
|
4
|
+
"target": "ES2022",
|
|
5
|
+
"module": "CommonJS",
|
|
6
|
+
"strict": true,
|
|
7
|
+
"declaration": true,
|
|
8
|
+
"esModuleInterop": true,
|
|
9
|
+
"skipLibCheck": true,
|
|
10
|
+
"rootDir": "src",
|
|
11
|
+
"outDir": "dist"
|
|
12
|
+
},
|
|
13
|
+
"include": ["src"]
|
|
14
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# ------------------------------------------------------------
|
|
2
|
+
# Local app runtime (used by `npm run dev -w api-prisma-postgresql`)
|
|
3
|
+
# ------------------------------------------------------------
|
|
4
|
+
ENV=development
|
|
5
|
+
HOST=127.0.0.1
|
|
6
|
+
PORT=4001
|
|
7
|
+
OPENAPI_ENABLED=true
|
|
8
|
+
CORS_ALLOWED_ORIGINS=
|
|
9
|
+
DATABASE_URL="postgresql://postgres:postgres@localhost:5432/trinacria_example?schema=public"
|
|
10
|
+
|
|
11
|
+
# ------------------------------------------------------------
|
|
12
|
+
# Docker runtime (used by `docker compose ... --env-file .../.env`)
|
|
13
|
+
# ------------------------------------------------------------
|
|
14
|
+
DOCKER_POSTGRES_DB=trinacria_example
|
|
15
|
+
DOCKER_POSTGRES_USER=postgres
|
|
16
|
+
DOCKER_POSTGRES_PASSWORD=postgres
|
|
17
|
+
DOCKER_POSTGRES_PORT=5432
|
|
18
|
+
DOCKER_API_PORT=4001
|
|
19
|
+
DOCKER_DATABASE_URL="postgresql://postgres:postgres@postgres:5432/trinacria_example?schema=public"
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
FROM node:24-bookworm-slim
|
|
2
|
+
|
|
3
|
+
WORKDIR /app
|
|
4
|
+
|
|
5
|
+
RUN apt-get update -y && apt-get install -y --no-install-recommends openssl \
|
|
6
|
+
&& rm -rf /var/lib/apt/lists/*
|
|
7
|
+
|
|
8
|
+
ENV DATABASE_URL=postgresql://postgres:postgres@localhost:5432/trinacria_example?schema=public
|
|
9
|
+
|
|
10
|
+
COPY package.json package-lock.json tsconfig.base.json ./
|
|
11
|
+
COPY packages ./packages
|
|
12
|
+
COPY apps/api-prisma-postgresql ./apps/api-prisma-postgresql
|
|
13
|
+
|
|
14
|
+
RUN npm ci
|
|
15
|
+
RUN npm run build:packages
|
|
16
|
+
RUN npm run prisma:generate -w api-prisma-postgresql
|
|
17
|
+
RUN npm run build -w api-prisma-postgresql
|
|
18
|
+
|
|
19
|
+
ENV HOST=0.0.0.0
|
|
20
|
+
ENV PORT=4001
|
|
21
|
+
|
|
22
|
+
EXPOSE 4001
|
|
23
|
+
|
|
24
|
+
CMD ["sh", "-c", "npm run prisma:push -w api-prisma-postgresql && npm run start -w api-prisma-postgresql"]
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
# API Example: Prisma + PostgreSQL
|
|
2
|
+
|
|
3
|
+
A minimal API-only example using Trinacria, Prisma, and PostgreSQL.
|
|
4
|
+
|
|
5
|
+
## Scope
|
|
6
|
+
|
|
7
|
+
This app is intentionally a base template:
|
|
8
|
+
|
|
9
|
+
- no authentication (no JWT/session/password flow)
|
|
10
|
+
- no authorization roles/policies
|
|
11
|
+
- no background jobs
|
|
12
|
+
|
|
13
|
+
Use this as a starting point for integration examples.
|
|
14
|
+
|
|
15
|
+
## Endpoints
|
|
16
|
+
|
|
17
|
+
- `GET /health`
|
|
18
|
+
- `GET /users`
|
|
19
|
+
- `POST /users`
|
|
20
|
+
- `GET /openapi.json`
|
|
21
|
+
- `GET /docs` (Swagger UI)
|
|
22
|
+
|
|
23
|
+
## Built-in middleware enabled
|
|
24
|
+
|
|
25
|
+
- `requestId`
|
|
26
|
+
- `requestLogger`
|
|
27
|
+
- `cors`
|
|
28
|
+
- `rateLimit`
|
|
29
|
+
- `requestTimeout`
|
|
30
|
+
- `securityHeaders`
|
|
31
|
+
|
|
32
|
+
## First steps (local)
|
|
33
|
+
|
|
34
|
+
1. Install dependencies from the monorepo root:
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
npm install
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
2. Copy the environment file:
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
cp apps/api-prisma-postgresql/.env.example apps/api-prisma-postgresql/.env.development
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
3. Make sure PostgreSQL is running locally and matches `DATABASE_URL`.
|
|
47
|
+
|
|
48
|
+
4. Push schema and generate Prisma client:
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
npm run prisma:push -w api-prisma-postgresql
|
|
52
|
+
npm run prisma:generate -w api-prisma-postgresql
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
5. Start the API:
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
npm run dev -w api-prisma-postgresql
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
6. Verify the app is running:
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
curl http://127.0.0.1:4001/health
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Create your first user
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
curl -X POST http://127.0.0.1:4001/users \
|
|
71
|
+
-H 'content-type: application/json' \
|
|
72
|
+
-d '{"name":"Mario Rossi","email":"mario@example.com"}'
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Then list users:
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
curl http://127.0.0.1:4001/users
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## Docker quick start
|
|
82
|
+
|
|
83
|
+
1. Create the Docker env file from the same template:
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
cp apps/api-prisma-postgresql/.env.example apps/api-prisma-postgresql/.env
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
2. Edit the Docker section in `apps/api-prisma-postgresql/.env` if you want custom credentials/ports.
|
|
90
|
+
|
|
91
|
+
3. Start PostgreSQL + API using that `.env` file:
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
docker compose \
|
|
95
|
+
--env-file apps/api-prisma-postgresql/.env \
|
|
96
|
+
-f apps/api-prisma-postgresql/docker-compose.yml \
|
|
97
|
+
up --build
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
The API will be available at `http://127.0.0.1:${DOCKER_API_PORT}` (default `4001`).
|
|
101
|
+
|
|
102
|
+
Compose services:
|
|
103
|
+
|
|
104
|
+
- `postgres`: PostgreSQL 16
|
|
105
|
+
- `api`: this Trinacria API (built from this monorepo)
|
|
106
|
+
|
|
107
|
+
The container startup command automatically runs `prisma db push` before starting the API.
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
services:
|
|
2
|
+
postgres:
|
|
3
|
+
image: postgres:16-alpine
|
|
4
|
+
environment:
|
|
5
|
+
POSTGRES_DB: ${DOCKER_POSTGRES_DB}
|
|
6
|
+
POSTGRES_USER: ${DOCKER_POSTGRES_USER}
|
|
7
|
+
POSTGRES_PASSWORD: ${DOCKER_POSTGRES_PASSWORD}
|
|
8
|
+
ports:
|
|
9
|
+
- "${DOCKER_POSTGRES_PORT}:5432"
|
|
10
|
+
volumes:
|
|
11
|
+
- trinacria_api_pg_data:/var/lib/postgresql/data
|
|
12
|
+
healthcheck:
|
|
13
|
+
test:
|
|
14
|
+
[
|
|
15
|
+
"CMD-SHELL",
|
|
16
|
+
"pg_isready -U ${DOCKER_POSTGRES_USER} -d ${DOCKER_POSTGRES_DB}",
|
|
17
|
+
]
|
|
18
|
+
interval: 5s
|
|
19
|
+
timeout: 5s
|
|
20
|
+
retries: 20
|
|
21
|
+
|
|
22
|
+
api:
|
|
23
|
+
build:
|
|
24
|
+
context: ../..
|
|
25
|
+
dockerfile: apps/api-prisma-postgresql/Dockerfile
|
|
26
|
+
environment:
|
|
27
|
+
ENV: development
|
|
28
|
+
HOST: 0.0.0.0
|
|
29
|
+
PORT: 4001
|
|
30
|
+
DATABASE_URL: ${DOCKER_DATABASE_URL}
|
|
31
|
+
ports:
|
|
32
|
+
- "${DOCKER_API_PORT}:4001"
|
|
33
|
+
depends_on:
|
|
34
|
+
postgres:
|
|
35
|
+
condition: service_healthy
|
|
36
|
+
|
|
37
|
+
volumes:
|
|
38
|
+
trinacria_api_pg_data:
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "api-prisma-postgresql",
|
|
3
|
+
"private": true,
|
|
4
|
+
"type": "commonjs",
|
|
5
|
+
"scripts": {
|
|
6
|
+
"start": "node ../../packages/cli/dist/index.js start",
|
|
7
|
+
"build": "node ../../packages/cli/dist/index.js build",
|
|
8
|
+
"dev": "node ../../packages/cli/dist/index.js dev",
|
|
9
|
+
"prisma:generate": "prisma generate --schema prisma/schema.prisma",
|
|
10
|
+
"prisma:migrate:dev": "prisma migrate dev --schema prisma/schema.prisma",
|
|
11
|
+
"prisma:push": "prisma db push --schema prisma/schema.prisma",
|
|
12
|
+
"prisma:studio": "prisma studio --schema prisma/schema.prisma"
|
|
13
|
+
},
|
|
14
|
+
"dependencies": {
|
|
15
|
+
"@prisma/adapter-pg": "^7.4.1",
|
|
16
|
+
"@prisma/client": "^7.4.1",
|
|
17
|
+
"@trinacria/cli": "*",
|
|
18
|
+
"@trinacria/core": "*",
|
|
19
|
+
"@trinacria/http": "*",
|
|
20
|
+
"@trinacria/schema": "*",
|
|
21
|
+
"pg": "^8.16.3"
|
|
22
|
+
},
|
|
23
|
+
"devDependencies": {
|
|
24
|
+
"prisma": "^7.4.1"
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
generator client {
|
|
2
|
+
provider = "prisma-client-js"
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
datasource db {
|
|
6
|
+
provider = "postgresql"
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
model User {
|
|
10
|
+
id String @id @default(cuid())
|
|
11
|
+
name String
|
|
12
|
+
email String @unique
|
|
13
|
+
createdAt DateTime @default(now())
|
|
14
|
+
updatedAt DateTime @updatedAt
|
|
15
|
+
}
|