From efdbad4ed709d2829127f378ef7b3976ba1cc1b5 Mon Sep 17 00:00:00 2001 From: Evert Prants Date: Thu, 29 Jun 2023 20:41:36 +0300 Subject: [PATCH] dev docker setup, auth service start --- .dockerignore | 4 + .gitignore | 4 +- Dockerfile.dev | 18 ++ apps/auth/src/auth.controller.spec.ts | 2 +- apps/auth/src/auth.controller.ts | 2 +- apps/auth/src/auth.module.ts | 37 ++- apps/auth/src/config/security.config.ts | 8 + .../migrations/20230629153343_user.ts | 26 ++ apps/auth/src/database/seeds/initial-users.ts | 21 ++ apps/auth/src/knexfile.ts | 5 + apps/auth/src/main.ts | 2 +- apps/auth/src/providers/keys.providers.ts | 32 +++ apps/auth/src/{ => services}/auth.service.ts | 2 +- apps/auth/src/services/jwt.service.ts | 36 +++ apps/auth/tsconfig.app.json | 11 +- apps/catalog/src/catalog.module.ts | 13 +- apps/catalog/src/main.ts | 2 +- apps/game/src/game.module.ts | 13 +- apps/game/src/main.ts | 2 +- apps/player/src/main.ts | 2 +- apps/player/src/player.module.ts | 17 +- apps/server/src/main.ts | 2 +- apps/server/src/server.module.ts | 13 +- apps/session/src/main.ts | 2 +- apps/session/src/session.module.ts | 13 +- docker-compose.yml | 147 +++++++++- docker/postgres/databases.sh | 23 ++ libs/shared/src/database/make-knex.ts | 35 +++ libs/shared/src/database/make-typeorm.ts | 17 ++ libs/shared/src/index.ts | 2 + libs/shared/src/utils/nats-client.ts | 2 +- package.json | 7 +- pnpm-lock.yaml | 269 ++++++++++++++++-- webpack.config.js | 34 +++ 34 files changed, 768 insertions(+), 57 deletions(-) create mode 100644 .dockerignore create mode 100644 Dockerfile.dev create mode 100644 apps/auth/src/config/security.config.ts create mode 100644 apps/auth/src/database/migrations/20230629153343_user.ts create mode 100644 apps/auth/src/database/seeds/initial-users.ts create mode 100644 apps/auth/src/knexfile.ts create mode 100644 apps/auth/src/providers/keys.providers.ts rename apps/auth/src/{ => services}/auth.service.ts (72%) create mode 100644 apps/auth/src/services/jwt.service.ts create mode 100755 docker/postgres/databases.sh create mode 100644 libs/shared/src/database/make-knex.ts create mode 100644 libs/shared/src/database/make-typeorm.ts create mode 100644 webpack.config.js diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..20cbe5e --- /dev/null +++ b/.dockerignore @@ -0,0 +1,4 @@ +/database +/dist +/node_modules +/.env diff --git a/.gitignore b/.gitignore index caf4bc9..e964fee 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ /dist /node_modules /database +/private # Logs logs @@ -18,6 +19,7 @@ lerna-debug.log* # Tests /coverage /.nyc_output +.env* # IDEs and editors /.idea @@ -34,5 +36,3 @@ lerna-debug.log* !.vscode/tasks.json !.vscode/launch.json !.vscode/extensions.json - -.env* diff --git a/Dockerfile.dev b/Dockerfile.dev new file mode 100644 index 0000000..61d04c8 --- /dev/null +++ b/Dockerfile.dev @@ -0,0 +1,18 @@ +FROM node:18-alpine + +WORKDIR /usr/src/app + +ARG SERVICE + +RUN npm i -g pnpm + +COPY package.json ./ +COPY pnpm-lock.yaml ./ + +RUN pnpm install + +COPY . ./ + +ENV APP_NAME=${SERVICE} + +CMD [ "/bin/sh", "-c", "pnpm start:dev $APP_NAME" ] diff --git a/apps/auth/src/auth.controller.spec.ts b/apps/auth/src/auth.controller.spec.ts index d59df3e..ef6633c 100644 --- a/apps/auth/src/auth.controller.spec.ts +++ b/apps/auth/src/auth.controller.spec.ts @@ -1,6 +1,6 @@ import { Test, TestingModule } from '@nestjs/testing'; import { AuthController } from './auth.controller'; -import { AuthService } from './auth.service'; +import { AuthService } from './services/auth.service'; describe('AuthController', () => { let authController: AuthController; diff --git a/apps/auth/src/auth.controller.ts b/apps/auth/src/auth.controller.ts index ca10a39..aaaf71c 100644 --- a/apps/auth/src/auth.controller.ts +++ b/apps/auth/src/auth.controller.ts @@ -1,5 +1,5 @@ import { Controller } from '@nestjs/common'; -import { AuthService } from './auth.service'; +import { AuthService } from './services/auth.service'; import { MessagePattern } from '@nestjs/microservices'; import { LoginRequest } from './interfaces/auth.interface'; diff --git a/apps/auth/src/auth.module.ts b/apps/auth/src/auth.module.ts index c2e3178..b6b7a2f 100644 --- a/apps/auth/src/auth.module.ts +++ b/apps/auth/src/auth.module.ts @@ -1,12 +1,37 @@ -import { Module } from '@nestjs/common'; +import { Module, OnModuleInit } from '@nestjs/common'; import { AuthController } from './auth.controller'; -import { AuthService } from './auth.service'; +import { AuthService } from './services/auth.service'; import { ClientsModule } from '@nestjs/microservices'; -import { natsClient } from '@freeblox/shared'; +import { makeKnex, makeTypeOrm, natsClient } from '@freeblox/shared'; +import { ConfigModule, ConfigService } from '@nestjs/config'; +import { TypeOrmModule } from '@nestjs/typeorm'; +import knex from 'knex'; +import { security } from './config/security.config'; +import { keysProviders } from './providers/keys.providers'; +import { JWTService } from './services/jwt.service'; @Module({ - imports: [ClientsModule.register([natsClient('auth')])], + imports: [ + ConfigModule.forRoot({ + ignoreEnvFile: process.env.NODE_ENV === 'development', + load: [makeKnex('auth', __dirname), makeTypeOrm('auth'), security], + }), + TypeOrmModule.forRootAsync({ + imports: [ConfigModule], + inject: [ConfigService], + useFactory: (config: ConfigService) => config.get('typeorm'), + }), + ClientsModule.register([natsClient('auth')]), + ], controllers: [AuthController], - providers: [AuthService], + providers: [AuthService, ...keysProviders, JWTService], }) -export class AuthModule {} +export class AuthModule implements OnModuleInit { + constructor(private readonly config: ConfigService) {} + + async onModuleInit() { + const knexInstance = knex(this.config.get('knex')); + await knexInstance.migrate.latest(); + await knexInstance.seed.run(); + } +} diff --git a/apps/auth/src/config/security.config.ts b/apps/auth/src/config/security.config.ts new file mode 100644 index 0000000..02100f5 --- /dev/null +++ b/apps/auth/src/config/security.config.ts @@ -0,0 +1,8 @@ +import { registerAs } from '@nestjs/config'; +import { resolve } from 'path'; + +export const security = registerAs('security', () => ({ + algorithm: String(process.env.JWT_ALGORITHM || 'RS512'), + privateKeyPath: resolve(String(process.env.PRIVATE_KEY_FILE)), + publicKeyPath: resolve(String(process.env.PUBLIC_KEY_FILE)), +})); diff --git a/apps/auth/src/database/migrations/20230629153343_user.ts b/apps/auth/src/database/migrations/20230629153343_user.ts new file mode 100644 index 0000000..f7f8039 --- /dev/null +++ b/apps/auth/src/database/migrations/20230629153343_user.ts @@ -0,0 +1,26 @@ +import { Knex } from 'knex'; + +export async function up(knex: Knex): Promise { + return knex.schema.createTable('users', (table) => { + table.uuid('id').primary().defaultTo(knex.raw('uuid_generate_v4()')); + + table.string('username', 255).notNullable(); + table.string('email', 255).notNullable(); + table.string('phone', 255).nullable(); + table.string('country', 2).nullable(); + table.string('language', 2).notNullable().defaultTo('en'); + + table.text('password').notNullable(); + table.text('display_name').nullable(); + + table.boolean('verified').defaultTo(false); + table.boolean('activated').defaultTo(true); + + table.timestamps(true, true); + table.timestamp('login_at'); + }); +} + +export async function down(knex: Knex): Promise { + return knex.schema.dropTable('users'); +} diff --git a/apps/auth/src/database/seeds/initial-users.ts b/apps/auth/src/database/seeds/initial-users.ts new file mode 100644 index 0000000..7112129 --- /dev/null +++ b/apps/auth/src/database/seeds/initial-users.ts @@ -0,0 +1,21 @@ +import { hash } from 'bcrypt'; +import { Knex } from 'knex'; + +export async function seed(knex: Knex): Promise { + const userExists = await knex('users').where({ username: 'freeblox' }); + if (userExists?.length) return; + + await knex('users').insert([ + { + username: 'freeblox', + email: 'freeblox@icynet.eu', + phone: null, + country: 'ee', + language: 'en', + password: await hash('FBLXAdmin123', 12), + display_name: 'Freeblox', + verified: true, + activated: true, + }, + ]); +} diff --git a/apps/auth/src/knexfile.ts b/apps/auth/src/knexfile.ts new file mode 100644 index 0000000..f8e3f24 --- /dev/null +++ b/apps/auth/src/knexfile.ts @@ -0,0 +1,5 @@ +import { getKnex } from '../../../libs/shared/src/'; + +module.exports = { + development: getKnex('auth', __dirname, ['.ts', '.js']), +}; diff --git a/apps/auth/src/main.ts b/apps/auth/src/main.ts index 7402453..208290a 100644 --- a/apps/auth/src/main.ts +++ b/apps/auth/src/main.ts @@ -8,7 +8,7 @@ async function bootstrap() { { transport: Transport.NATS, options: { - servers: ['nats://localhost:4222'], + servers: [String(process.env.NATS_ENTRYPOINT)], }, }, ); diff --git a/apps/auth/src/providers/keys.providers.ts b/apps/auth/src/providers/keys.providers.ts new file mode 100644 index 0000000..abb091f --- /dev/null +++ b/apps/auth/src/providers/keys.providers.ts @@ -0,0 +1,32 @@ +import { FactoryProvider } from '@nestjs/common'; +import { ConfigService } from '@nestjs/config'; +import { promises as fs } from 'fs'; +import * as jose from 'jose'; + +/** + * ssh-keygen -t rsa -b 4096 -m PKCS8 -f jwt.private.pem + * openssl rsa -in jwt.private.pem -pubout -outform PEM -out jwt.public.pem + */ +export const keysProviders = [ + { + provide: 'APP_PRIVATE_KEY', + inject: [ConfigService], + useFactory: async (config: ConfigService) => + fs + .readFile(config.get('security.privateKeyPath'), 'utf-8') + .then((key) => jose.importPKCS8(key, 'RS512')), + }, + { + provide: 'APP_PUBLIC_KEY', + inject: [ConfigService], + useFactory: async (config: ConfigService) => + fs + .readFile(config.get('security.publicKeyPath'), 'utf-8') + .then((key) => jose.importSPKI(key, 'RS512')), + }, + { + provide: 'APP_PUBLIC_KEY_JWK', + inject: ['APP_PUBLIC_KEY'], + useFactory: async (key: jose.KeyLike) => jose.exportJWK(key), + }, +]; diff --git a/apps/auth/src/auth.service.ts b/apps/auth/src/services/auth.service.ts similarity index 72% rename from apps/auth/src/auth.service.ts rename to apps/auth/src/services/auth.service.ts index 7d7f9be..db9e049 100644 --- a/apps/auth/src/auth.service.ts +++ b/apps/auth/src/services/auth.service.ts @@ -1,5 +1,5 @@ import { Injectable } from '@nestjs/common'; -import { LoginRequest } from './interfaces/auth.interface'; +import { LoginRequest } from '../interfaces/auth.interface'; @Injectable() export class AuthService { diff --git a/apps/auth/src/services/jwt.service.ts b/apps/auth/src/services/jwt.service.ts new file mode 100644 index 0000000..c487fd5 --- /dev/null +++ b/apps/auth/src/services/jwt.service.ts @@ -0,0 +1,36 @@ +import { ForbiddenException, Inject, Injectable } from '@nestjs/common'; +import { ConfigService } from '@nestjs/config'; +import { JWK, KeyLike, SignJWT, jwtVerify } from 'jose'; + +@Injectable() +export class JWTService { + constructor( + @Inject('APP_PRIVATE_KEY') private readonly privateKey: KeyLike, + @Inject('APP_PUBLIC_KEY') private readonly publicKey: KeyLike, + @Inject('APP_PUBLIC_KEY_JWK') private readonly publicKeyJWK: JWK, + private readonly config: ConfigService, + ) {} + + async sign(data: Record, audience = 'urn:freeblox:service') { + const alg = this.config.get('security.algorithm'); + const jwt = await new SignJWT(data) + .setProtectedHeader({ alg }) + .setIssuedAt() + .setIssuer('urn:freeblox:auth') + .setAudience(audience) + .setExpirationTime('8d') + .sign(this.privateKey); + return jwt; + } + + async verify(jwt: string, audience = 'urn:freeblox:service') { + const alg = this.config.get('security.algorithm'); + const { payload, protectedHeader } = await jwtVerify(jwt, this.publicKey, { + issuer: 'urn:freeblox:auth', + audience, + }); + if (protectedHeader.alg !== alg) + throw new ForbiddenException('Provided JWT contains invalid headers.'); + return payload; + } +} diff --git a/apps/auth/tsconfig.app.json b/apps/auth/tsconfig.app.json index 01d9c9a..2fd6709 100644 --- a/apps/auth/tsconfig.app.json +++ b/apps/auth/tsconfig.app.json @@ -4,6 +4,13 @@ "declaration": false, "outDir": "../../dist/apps/auth" }, - "include": ["src/**/*"], - "exclude": ["node_modules", "dist", "test", "**/*spec.ts"] + "include": [ + "src/**/*" + ], + "exclude": [ + "node_modules", + "dist", + "test", + "**/*spec.ts" + ] } diff --git a/apps/catalog/src/catalog.module.ts b/apps/catalog/src/catalog.module.ts index 8a1dd0a..32b8554 100644 --- a/apps/catalog/src/catalog.module.ts +++ b/apps/catalog/src/catalog.module.ts @@ -2,10 +2,21 @@ import { Module } from '@nestjs/common'; import { CatalogController } from './catalog.controller'; import { CatalogService } from './catalog.service'; import { ClientsModule } from '@nestjs/microservices'; -import { natsClient } from '@freeblox/shared'; +import { makeKnex, makeTypeOrm, natsClient } from '@freeblox/shared'; +import { ConfigModule, ConfigService } from '@nestjs/config'; +import { TypeOrmModule } from '@nestjs/typeorm'; @Module({ imports: [ + ConfigModule.forRoot({ + ignoreEnvFile: process.env.NODE_ENV === 'development', + load: [makeKnex('catalog', __dirname), makeTypeOrm('catalog')], + }), + TypeOrmModule.forRootAsync({ + imports: [ConfigModule], + inject: [ConfigService], + useFactory: (config: ConfigService) => config.get('typeorm'), + }), ClientsModule.register([ natsClient('catalog'), natsClient('auth'), diff --git a/apps/catalog/src/main.ts b/apps/catalog/src/main.ts index bb04a26..2c77549 100644 --- a/apps/catalog/src/main.ts +++ b/apps/catalog/src/main.ts @@ -8,7 +8,7 @@ async function bootstrap() { { transport: Transport.NATS, options: { - servers: ['nats://localhost:4222'], + servers: [String(process.env.NATS_ENTRYPOINT)], }, }, ); diff --git a/apps/game/src/game.module.ts b/apps/game/src/game.module.ts index 5331037..2feff68 100644 --- a/apps/game/src/game.module.ts +++ b/apps/game/src/game.module.ts @@ -2,10 +2,21 @@ import { Module } from '@nestjs/common'; import { GameController } from './game.controller'; import { GameService } from './game.service'; import { ClientsModule } from '@nestjs/microservices'; -import { natsClient } from '@freeblox/shared'; +import { makeKnex, makeTypeOrm, natsClient } from '@freeblox/shared'; +import { ConfigModule, ConfigService } from '@nestjs/config'; +import { TypeOrmModule } from '@nestjs/typeorm'; @Module({ imports: [ + ConfigModule.forRoot({ + ignoreEnvFile: process.env.NODE_ENV === 'development', + load: [makeKnex('game', __dirname), makeTypeOrm('game')], + }), + TypeOrmModule.forRootAsync({ + imports: [ConfigModule], + inject: [ConfigService], + useFactory: (config: ConfigService) => config.get('typeorm'), + }), ClientsModule.register([ natsClient('game'), natsClient('auth'), diff --git a/apps/game/src/main.ts b/apps/game/src/main.ts index 4225282..dda29a9 100644 --- a/apps/game/src/main.ts +++ b/apps/game/src/main.ts @@ -8,7 +8,7 @@ async function bootstrap() { { transport: Transport.NATS, options: { - servers: ['nats://localhost:4222'], + servers: [String(process.env.NATS_ENTRYPOINT)], }, }, ); diff --git a/apps/player/src/main.ts b/apps/player/src/main.ts index a58a4ac..c2c3871 100644 --- a/apps/player/src/main.ts +++ b/apps/player/src/main.ts @@ -8,7 +8,7 @@ async function bootstrap() { { transport: Transport.NATS, options: { - servers: ['nats://localhost:4222'], + servers: [String(process.env.NATS_ENTRYPOINT)], }, }, ); diff --git a/apps/player/src/player.module.ts b/apps/player/src/player.module.ts index 7a9db89..f1ec611 100644 --- a/apps/player/src/player.module.ts +++ b/apps/player/src/player.module.ts @@ -2,10 +2,23 @@ import { Module } from '@nestjs/common'; import { PlayerController } from './player.controller'; import { PlayerService } from './player.service'; import { ClientsModule } from '@nestjs/microservices'; -import { natsClient } from '@freeblox/shared'; +import { makeKnex, makeTypeOrm, natsClient } from '@freeblox/shared'; +import { ConfigModule, ConfigService } from '@nestjs/config'; +import { TypeOrmModule } from '@nestjs/typeorm'; @Module({ - imports: [ClientsModule.register([natsClient('player'), natsClient('auth')])], + imports: [ + ConfigModule.forRoot({ + ignoreEnvFile: process.env.NODE_ENV === 'development', + load: [makeKnex('player', __dirname), makeTypeOrm('player')], + }), + TypeOrmModule.forRootAsync({ + imports: [ConfigModule], + inject: [ConfigService], + useFactory: (config: ConfigService) => config.get('typeorm'), + }), + ClientsModule.register([natsClient('player'), natsClient('auth')]), + ], controllers: [PlayerController], providers: [PlayerService], }) diff --git a/apps/server/src/main.ts b/apps/server/src/main.ts index a9d0ae2..30b833f 100644 --- a/apps/server/src/main.ts +++ b/apps/server/src/main.ts @@ -8,7 +8,7 @@ async function bootstrap() { { transport: Transport.NATS, options: { - servers: ['nats://localhost:4222'], + servers: [String(process.env.NATS_ENTRYPOINT)], }, }, ); diff --git a/apps/server/src/server.module.ts b/apps/server/src/server.module.ts index eae6b7e..f79a0b2 100644 --- a/apps/server/src/server.module.ts +++ b/apps/server/src/server.module.ts @@ -1,11 +1,22 @@ import { Module } from '@nestjs/common'; import { ServerController } from './server.controller'; import { ServerService } from './server.service'; -import { natsClient } from '@freeblox/shared'; +import { makeKnex, makeTypeOrm, natsClient } from '@freeblox/shared'; import { ClientsModule } from '@nestjs/microservices'; +import { ConfigModule, ConfigService } from '@nestjs/config'; +import { TypeOrmModule } from '@nestjs/typeorm'; @Module({ imports: [ + ConfigModule.forRoot({ + ignoreEnvFile: process.env.NODE_ENV === 'development', + load: [makeKnex('server', __dirname), makeTypeOrm('server')], + }), + TypeOrmModule.forRootAsync({ + imports: [ConfigModule], + inject: [ConfigService], + useFactory: (config: ConfigService) => config.get('typeorm'), + }), ClientsModule.register([ natsClient('server'), natsClient('auth'), diff --git a/apps/session/src/main.ts b/apps/session/src/main.ts index 978422b..6aa5166 100644 --- a/apps/session/src/main.ts +++ b/apps/session/src/main.ts @@ -8,7 +8,7 @@ async function bootstrap() { { transport: Transport.NATS, options: { - servers: ['nats://localhost:4222'], + servers: [String(process.env.NATS_ENTRYPOINT)], }, }, ); diff --git a/apps/session/src/session.module.ts b/apps/session/src/session.module.ts index 5b4c917..dc78447 100644 --- a/apps/session/src/session.module.ts +++ b/apps/session/src/session.module.ts @@ -2,10 +2,21 @@ import { Module } from '@nestjs/common'; import { SessionController } from './session.controller'; import { SessionService } from './session.service'; import { ClientsModule } from '@nestjs/microservices'; -import { natsClient } from '@freeblox/shared'; +import { makeKnex, makeTypeOrm, natsClient } from '@freeblox/shared'; +import { ConfigModule, ConfigService } from '@nestjs/config'; +import { TypeOrmModule } from '@nestjs/typeorm'; @Module({ imports: [ + ConfigModule.forRoot({ + ignoreEnvFile: process.env.NODE_ENV === 'development', + load: [makeKnex('session', __dirname), makeTypeOrm('server')], + }), + TypeOrmModule.forRootAsync({ + imports: [ConfigModule], + inject: [ConfigService], + useFactory: (config: ConfigService) => config.get('typeorm'), + }), ClientsModule.register([ natsClient('session'), natsClient('server'), diff --git a/docker-compose.yml b/docker-compose.yml index 0c0006e..ad0a860 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -7,13 +7,154 @@ services: - 4222:4222 - 6222:6222 - 8222:8222 + networks: + - fblx postgres: - container_name: fblx-pg + container_name: fblx-postgres image: postgres:15-alpine - ports: - - 5432:5432 environment: + - POSTGRES_MULTIPLE_DATABASES=auth,catalog,game,player,server,session - POSTGRES_USER=freeblox - POSTGRES_PASSWORD=FREEBLOXDataBaseDEV@123 volumes: - ./database:/var/lib/postgresql/data + - ./docker/postgres:/docker-entrypoint-initdb.d + networks: + - fblx + auth: + container_name: fblx-auth + build: + context: . + dockerfile: Dockerfile.dev + args: + - SERVICE=auth + networks: + - fblx + environment: + - NATS_ENTRYPOINT=nats://nats:4222 + - POSTGRES_HOST=postgres + - POSTGRES_USER=freeblox + - POSTGRES_PASSWORD=FREEBLOXDataBaseDEV@123 + - PRIVATE_KEY_FILE=private/jwt.private.pem + - PUBLIC_KEY_FILE=private/jwt.public.pem + volumes: + - ./apps:/usr/src/app/apps + - ./libs:/usr/src/app/libs + - ./private:/usr/src/app/private + catalog: + container_name: fblx-catalog + build: + context: . + dockerfile: Dockerfile.dev + args: + - SERVICE=catalog + networks: + - fblx + environment: + - NATS_ENTRYPOINT=nats://nats:4222 + - POSTGRES_HOST=postgres + - POSTGRES_USER=freeblox + - POSTGRES_PASSWORD=FREEBLOXDataBaseDEV@123 + volumes: + - ./apps:/usr/src/app/apps + - ./libs:/usr/src/app/libs + game: + container_name: fblx-game + build: + context: . + dockerfile: Dockerfile.dev + args: + - SERVICE=game + networks: + - fblx + environment: + - NATS_ENTRYPOINT=nats://nats:4222 + - POSTGRES_HOST=postgres + - POSTGRES_USER=freeblox + - POSTGRES_PASSWORD=FREEBLOXDataBaseDEV@123 + volumes: + - ./apps:/usr/src/app/apps + - ./libs:/usr/src/app/libs + player: + container_name: fblx-player + build: + context: . + dockerfile: Dockerfile.dev + args: + - SERVICE=player + networks: + - fblx + environment: + - NATS_ENTRYPOINT=nats://nats:4222 + - POSTGRES_HOST=postgres + - POSTGRES_USER=freeblox + - POSTGRES_PASSWORD=FREEBLOXDataBaseDEV@123 + volumes: + - ./apps:/usr/src/app/apps + - ./libs:/usr/src/app/libs + server: + container_name: fblx-server + build: + context: . + dockerfile: Dockerfile.dev + args: + - SERVICE=server + networks: + - fblx + environment: + - NATS_ENTRYPOINT=nats://nats:4222 + - POSTGRES_HOST=postgres + - POSTGRES_USER=freeblox + - POSTGRES_PASSWORD=FREEBLOXDataBaseDEV@123 + volumes: + - ./apps:/usr/src/app/apps + - ./libs:/usr/src/app/libs + session: + container_name: fblx-session + build: + context: . + dockerfile: Dockerfile.dev + args: + - SERVICE=session + networks: + - fblx + environment: + - NATS_ENTRYPOINT=nats://nats:4222 + - POSTGRES_HOST=postgres + - POSTGRES_USER=freeblox + - POSTGRES_PASSWORD=FREEBLOXDataBaseDEV@123 + volumes: + - ./apps:/usr/src/app/apps + - ./libs:/usr/src/app/libs + web-service: + container_name: fblx-web-service + build: + context: . + dockerfile: Dockerfile.dev + args: + - SERVICE=freeblox-web-service + networks: + - fblx + ports: + - 4555:3000 + environment: + - NATS_ENTRYPOINT=nats://nats:4222 + volumes: + - ./apps:/usr/src/app/apps + - ./libs:/usr/src/app/libs + pgadmin: + container_name: fblx-pgadmin + image: dpage/pgadmin4 + ports: + - 4556:80 + networks: + - fblx + volumes: + - fblx-pgadmin:/var/lib/pgadmin + environment: + - PGADMIN_DEFAULT_EMAIL=freeblox@freeblox.gg + - PGADMIN_DEFAULT_PASSWORD=password +networks: + fblx: +volumes: + fblx-pgadmin: diff --git a/docker/postgres/databases.sh b/docker/postgres/databases.sh new file mode 100755 index 0000000..03ce34a --- /dev/null +++ b/docker/postgres/databases.sh @@ -0,0 +1,23 @@ +#!/bin/bash +# https://stackoverflow.com/a/59466352 +set -e +set -u + +function create_user_and_database() { + local database=$(echo $1) + echo " Creating user and database '$database'" + psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" <<-EOSQL + CREATE DATABASE $database; +EOSQL + psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" -d "$database" <<-EOSQL + CREATE EXTENSION IF NOT EXISTS "uuid-ossp"; +EOSQL +} + +if [ -n "$POSTGRES_MULTIPLE_DATABASES" ]; then + echo "Creating databases: $POSTGRES_MULTIPLE_DATABASES" + for db in $(echo $POSTGRES_MULTIPLE_DATABASES | tr ',' ' '); do + create_user_and_database $db + done + echo "Databases created" +fi diff --git a/libs/shared/src/database/make-knex.ts b/libs/shared/src/database/make-knex.ts new file mode 100644 index 0000000..beef043 --- /dev/null +++ b/libs/shared/src/database/make-knex.ts @@ -0,0 +1,35 @@ +import { registerAs } from '@nestjs/config'; +import { Knex } from 'knex'; +import { join } from 'path'; + +export const getKnex = ( + database: string, + directory: string, + loadExtensions = ['.js'], +) => + ({ + client: 'pg', + connection: { + port: 5432, + host: String(process.env.POSTGRES_HOST), + user: String(process.env.POSTGRES_USER), + password: String(process.env.POSTGRES_PASSWORD), + database, + }, + migrations: { + directory: join(directory, 'database', 'migrations'), + extension: 'ts', + loadExtensions, + }, + seeds: { + directory: join(directory, 'database', 'seeds'), + extension: 'ts', + loadExtensions, + }, + } as Knex.Config); + +export const makeKnex = ( + database: string, + directory: string, + loadExtensions?: string[], +) => registerAs('knex', () => getKnex(database, directory, loadExtensions)); diff --git a/libs/shared/src/database/make-typeorm.ts b/libs/shared/src/database/make-typeorm.ts new file mode 100644 index 0000000..ad0a4d4 --- /dev/null +++ b/libs/shared/src/database/make-typeorm.ts @@ -0,0 +1,17 @@ +import { registerAs } from '@nestjs/config'; +import { TypeOrmModuleOptions } from '@nestjs/typeorm'; + +export const makeTypeOrm = (database: string) => + registerAs( + 'typeorm', + () => + ({ + type: 'postgres', + port: 5432, + host: String(process.env.POSTGRES_HOST), + username: String(process.env.POSTGRES_USER), + password: String(process.env.POSTGRES_PASSWORD), + database, + autoLoadEntities: true, + } as TypeOrmModuleOptions), + ); diff --git a/libs/shared/src/index.ts b/libs/shared/src/index.ts index 1157b1a..a720a19 100644 --- a/libs/shared/src/index.ts +++ b/libs/shared/src/index.ts @@ -1,3 +1,5 @@ export * from './shared.module'; export * from './shared.service'; export * from './utils/nats-client'; +export * from './database/make-typeorm'; +export * from './database/make-knex'; diff --git a/libs/shared/src/utils/nats-client.ts b/libs/shared/src/utils/nats-client.ts index 8034c22..54af97c 100644 --- a/libs/shared/src/utils/nats-client.ts +++ b/libs/shared/src/utils/nats-client.ts @@ -5,6 +5,6 @@ export const natsClient = (name: string) => name, transport: Transport.NATS, options: { - servers: ['nats://localhost:4222'], + servers: [String(process.env.NATS_ENTRYPOINT)], }, }; diff --git a/package.json b/package.json index 26ab5cb..1c86f31 100644 --- a/package.json +++ b/package.json @@ -30,8 +30,11 @@ "@nestjs/swagger": "^7.0.11", "@nestjs/typeorm": "^10.0.0", "axios": "^1.4.0", + "bcrypt": "^5.1.0", "class-transformer": "^0.5.1", "class-validator": "^0.14.0", + "jose": "^4.14.4", + "jsonwebtoken": "^9.0.0", "knex": "^2.4.2", "nats": "^2.15.1", "pg": "^8.11.1", @@ -44,6 +47,7 @@ "@nestjs/cli": "^10.0.5", "@nestjs/schematics": "^10.0.1", "@nestjs/testing": "^10.0.3", + "@types/bcrypt": "^5.0.0", "@types/express": "^4.17.17", "@types/jest": "29.5.2", "@types/node": "^20.3.2", @@ -53,6 +57,7 @@ "eslint": "^8.43.0", "eslint-config-prettier": "^8.8.0", "eslint-plugin-prettier": "^4.2.1", + "glob": "^10.3.1", "jest": "29.5.0", "prettier": "^2.8.8", "source-map-support": "^0.5.21", @@ -87,4 +92,4 @@ "^@freeblox/shared(|/.*)$": "/libs/shared/src/$1" } } -} \ No newline at end of file +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5d79a91..3004828 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -32,12 +32,21 @@ dependencies: axios: specifier: ^1.4.0 version: 1.4.0 + bcrypt: + specifier: ^5.1.0 + version: 5.1.0 class-transformer: specifier: ^0.5.1 version: 0.5.1 class-validator: specifier: ^0.14.0 version: 0.14.0 + jose: + specifier: ^4.14.4 + version: 4.14.4 + jsonwebtoken: + specifier: ^9.0.0 + version: 9.0.0 knex: specifier: ^2.4.2 version: 2.4.2(pg@8.11.1) @@ -70,6 +79,9 @@ devDependencies: '@nestjs/testing': specifier: ^10.0.3 version: 10.0.3(@nestjs/common@10.0.3)(@nestjs/core@10.0.3)(@nestjs/microservices@10.0.3)(@nestjs/platform-express@10.0.3) + '@types/bcrypt': + specifier: ^5.0.0 + version: 5.0.0 '@types/express': specifier: ^4.17.17 version: 4.17.17 @@ -97,6 +109,9 @@ devDependencies: eslint-plugin-prettier: specifier: ^4.2.1 version: 4.2.1(eslint-config-prettier@8.8.0)(eslint@8.43.0)(prettier@2.8.8) + glob: + specifier: ^10.3.1 + version: 10.3.1 jest: specifier: 29.5.0 version: 29.5.0(@types/node@20.3.2)(ts-node@10.9.1) @@ -605,7 +620,6 @@ packages: strip-ansi-cjs: /strip-ansi@6.0.1 wrap-ansi: 8.1.0 wrap-ansi-cjs: /wrap-ansi@7.0.0 - dev: false /@istanbuljs/load-nyc-config@1.1.0: resolution: {integrity: sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==} @@ -890,6 +904,24 @@ packages: resolution: {integrity: sha512-Z7C/xXCiGWsg0KuKsHTKJxbWhpI3Vs5GwLfOean7MGyVFGqdRgBbAjOCh6u4bbjPc/8MJ2pZmK/0DLdCbivLDA==} engines: {node: '>=8'} + /@mapbox/node-pre-gyp@1.0.10: + resolution: {integrity: sha512-4ySo4CjzStuprMwk35H5pPbkymjv1SF3jGLj6rAHp/xT/RF7TL7bd9CTm1xDY49K2qF7jmR/g7k+SkLETP6opA==} + hasBin: true + dependencies: + detect-libc: 2.0.1 + https-proxy-agent: 5.0.1 + make-dir: 3.1.0 + node-fetch: 2.6.11 + nopt: 5.0.0 + npmlog: 5.0.1 + rimraf: 3.0.2 + semver: 7.5.3 + tar: 6.1.15 + transitivePeerDependencies: + - encoding + - supports-color + dev: false + /@nestjs/axios@3.0.0(@nestjs/common@10.0.3)(axios@1.4.0)(reflect-metadata@0.1.13)(rxjs@7.8.1): resolution: {integrity: sha512-ULdH03jDWkS5dy9X69XbUVbhC+0pVnrRcj7bIK/ytTZ76w7CgvTZDJqsIyisg3kNOiljRW/4NIjSf3j6YGvl+g==} peerDependencies: @@ -1208,7 +1240,6 @@ packages: resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} engines: {node: '>=14'} requiresBuild: true - dev: false optional: true /@sinclair/typebox@0.25.24: @@ -1272,6 +1303,12 @@ packages: '@babel/types': 7.22.5 dev: true + /@types/bcrypt@5.0.0: + resolution: {integrity: sha512-agtcFKaruL8TmcvqbndlqHPSJgsolhf/qPWchFlgnW1gECTN/nKbFcoFnvKAQRFfKbh+BO6A3SWdJu9t+xF3Lw==} + dependencies: + '@types/node': 20.3.2 + dev: true + /@types/body-parser@1.19.2: resolution: {integrity: sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==} dependencies: @@ -1682,6 +1719,10 @@ packages: resolution: {integrity: sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==} dev: true + /abbrev@1.1.1: + resolution: {integrity: sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==} + dev: false + /accepts@1.3.8: resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==} engines: {node: '>= 0.6'} @@ -1714,6 +1755,15 @@ packages: engines: {node: '>=0.4.0'} hasBin: true + /agent-base@6.0.2: + resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==} + engines: {node: '>= 6.0.0'} + dependencies: + debug: 4.3.4 + transitivePeerDependencies: + - supports-color + dev: false + /ajv-formats@2.1.1(ajv@8.12.0): resolution: {integrity: sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==} peerDependencies: @@ -1770,7 +1820,6 @@ packages: /ansi-regex@6.0.1: resolution: {integrity: sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==} engines: {node: '>=12'} - dev: false /ansi-styles@3.2.1: resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==} @@ -1793,7 +1842,6 @@ packages: /ansi-styles@6.2.1: resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==} engines: {node: '>=12'} - dev: false /any-promise@1.3.0: resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==} @@ -1815,6 +1863,18 @@ packages: /append-field@1.0.0: resolution: {integrity: sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==} + /aproba@2.0.0: + resolution: {integrity: sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==} + dev: false + + /are-we-there-yet@2.0.0: + resolution: {integrity: sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==} + engines: {node: '>=10'} + dependencies: + delegates: 1.0.0 + readable-stream: 3.6.2 + dev: false + /arg@4.1.3: resolution: {integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==} @@ -1934,6 +1994,18 @@ packages: /base64-js@1.5.1: resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} + /bcrypt@5.1.0: + resolution: {integrity: sha512-RHBS7HI5N5tEnGTmtR/pppX0mmDSBpQ4aCBsj7CEQfYXDcO74A8sIBYcJMuCsis2E81zDxeENYhv66oZwLiA+Q==} + engines: {node: '>= 10.0.0'} + requiresBuild: true + dependencies: + '@mapbox/node-pre-gyp': 1.0.10 + node-addon-api: 5.1.0 + transitivePeerDependencies: + - encoding + - supports-color + dev: false + /binary-extensions@2.2.0: resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==} engines: {node: '>=8'} @@ -1990,7 +2062,6 @@ packages: dependencies: balanced-match: 1.0.2 concat-map: 0.0.1 - dev: true /brace-expansion@2.0.1: resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} @@ -2028,6 +2099,10 @@ packages: node-int64: 0.4.0 dev: true + /buffer-equal-constant-time@1.0.1: + resolution: {integrity: sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==} + dev: false + /buffer-from@1.1.2: resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} @@ -2125,6 +2200,11 @@ packages: fsevents: 2.3.2 dev: true + /chownr@2.0.0: + resolution: {integrity: sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==} + engines: {node: '>=10'} + dev: false + /chrome-trace-event@1.0.3: resolution: {integrity: sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==} engines: {node: '>=6.0'} @@ -2237,6 +2317,11 @@ packages: /color-name@1.1.4: resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + /color-support@1.1.3: + resolution: {integrity: sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==} + hasBin: true + dev: false + /colorette@2.0.19: resolution: {integrity: sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ==} dev: false @@ -2278,7 +2363,6 @@ packages: /concat-map@0.0.1: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} - dev: true /concat-stream@1.6.2: resolution: {integrity: sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==} @@ -2292,6 +2376,10 @@ packages: /consola@2.15.3: resolution: {integrity: sha512-9vAdYbHj6x2fLKC4+oPH0kFzY/orMZyG2Aj+kNylHxKGJ/Ed4dpNyAQYwJOdqO4zdM7XpVHmyejQDcQHrnuXbw==} + /console-control-strings@1.1.0: + resolution: {integrity: sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==} + dev: false + /content-disposition@0.5.4: resolution: {integrity: sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==} engines: {node: '>= 0.6'} @@ -2404,6 +2492,10 @@ packages: resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} engines: {node: '>=0.4.0'} + /delegates@1.0.0: + resolution: {integrity: sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==} + dev: false + /depd@2.0.0: resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} engines: {node: '>= 0.8'} @@ -2412,6 +2504,11 @@ packages: resolution: {integrity: sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==} engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} + /detect-libc@2.0.1: + resolution: {integrity: sha512-463v3ZeIrcWtdgIg6vI6XUncguvr2TnGl4SzDXinkt9mSLpBJKXT3mW6xT3VQdDN11+WVs29pgvivTc4Lp8v+w==} + engines: {node: '>=8'} + dev: false + /detect-newline@3.1.0: resolution: {integrity: sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==} engines: {node: '>=8'} @@ -2464,6 +2561,11 @@ packages: /eastasianwidth@0.2.0: resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} + + /ecdsa-sig-formatter@1.0.11: + resolution: {integrity: sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==} + dependencies: + safe-buffer: 5.2.1 dev: false /ee-first@1.1.1: @@ -2483,7 +2585,6 @@ packages: /emoji-regex@9.2.2: resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} - dev: false /encodeurl@1.0.2: resolution: {integrity: sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==} @@ -2902,7 +3003,6 @@ packages: dependencies: cross-spawn: 7.0.3 signal-exit: 4.0.2 - dev: false /fork-ts-checker-webpack-plugin@8.0.0(typescript@5.1.3)(webpack@5.87.0): resolution: {integrity: sha512-mX3qW3idpueT2klaQXBzrIM/pHw+T0B/V9KHEvNrqijTq9NFnMZU6oreVxDYcf33P8a5cW+67PjodNHthGnNVg==} @@ -2961,6 +3061,13 @@ packages: universalify: 2.0.0 dev: true + /fs-minipass@2.1.0: + resolution: {integrity: sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==} + engines: {node: '>= 8'} + dependencies: + minipass: 3.3.6 + dev: false + /fs-monkey@1.0.4: resolution: {integrity: sha512-INM/fWAxMICjttnD0DX1rBvinKskj5G1w+oy/pnm9u/tSlnBrzFonJMcalKJ30P8RRsPzKcCG7Q8l0jx5Fh9YQ==} dev: true @@ -2979,6 +3086,21 @@ packages: /function-bind@1.1.1: resolution: {integrity: sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==} + /gauge@3.0.2: + resolution: {integrity: sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==} + engines: {node: '>=10'} + dependencies: + aproba: 2.0.0 + color-support: 1.1.3 + console-control-strings: 1.1.0 + has-unicode: 2.0.1 + object-assign: 4.1.1 + signal-exit: 3.0.7 + string-width: 4.2.3 + strip-ansi: 6.0.1 + wide-align: 1.1.5 + dev: false + /gensync@1.0.0-beta.2: resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} engines: {node: '>=6.9.0'} @@ -3044,7 +3166,6 @@ packages: minimatch: 9.0.2 minipass: 6.0.2 path-scurry: 1.10.0 - dev: false /glob@7.2.3: resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} @@ -3055,7 +3176,6 @@ packages: minimatch: 3.1.2 once: 1.4.0 path-is-absolute: 1.0.1 - dev: true /glob@8.1.0: resolution: {integrity: sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==} @@ -3136,6 +3256,10 @@ packages: resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==} engines: {node: '>= 0.4'} + /has-unicode@2.0.1: + resolution: {integrity: sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==} + dev: false + /has@1.0.3: resolution: {integrity: sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==} engines: {node: '>= 0.4.0'} @@ -3165,6 +3289,16 @@ packages: statuses: 2.0.1 toidentifier: 1.0.1 + /https-proxy-agent@5.0.1: + resolution: {integrity: sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==} + engines: {node: '>= 6'} + dependencies: + agent-base: 6.0.2 + debug: 4.3.4 + transitivePeerDependencies: + - supports-color + dev: false + /human-signals@1.1.1: resolution: {integrity: sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==} engines: {node: '>=8.12.0'} @@ -3401,7 +3535,6 @@ packages: '@isaacs/cliui': 8.0.2 optionalDependencies: '@pkgjs/parseargs': 0.11.0 - dev: false /jest-changed-files@29.5.0: resolution: {integrity: sha512-IFG34IUMUaNBIxjQXF/iu7g6EcdMrGRRxaUSw92I/2g2YC6vCdTltl4nHvt7Ci5nSJwXIkCu8Ka1DKF+X7Z1Ag==} @@ -3822,6 +3955,10 @@ packages: - ts-node dev: true + /jose@4.14.4: + resolution: {integrity: sha512-j8GhLiKmUAh+dsFXlX1aJCbt5KMibuKb+d7j1JaOJG6s2UjX1PQlW+OKB/sD4a/5ZYF4RcmYmLSndOoU3Lt/3g==} + dev: false + /js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} dev: true @@ -3880,6 +4017,31 @@ packages: graceful-fs: 4.2.11 dev: true + /jsonwebtoken@9.0.0: + resolution: {integrity: sha512-tuGfYXxkQGDPnLJ7SibiQgVgeDgfbPq2k2ICcbgqW8WxWLBAxKQM/ZCu/IT8SOSwmaYl4dpTFCW5xZv7YbbWUw==} + engines: {node: '>=12', npm: '>=6'} + dependencies: + jws: 3.2.2 + lodash: 4.17.21 + ms: 2.1.3 + semver: 7.5.3 + dev: false + + /jwa@1.4.1: + resolution: {integrity: sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==} + dependencies: + buffer-equal-constant-time: 1.0.1 + ecdsa-sig-formatter: 1.0.11 + safe-buffer: 5.2.1 + dev: false + + /jws@3.2.2: + resolution: {integrity: sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==} + dependencies: + jwa: 1.4.1 + safe-buffer: 5.2.1 + dev: false + /kleur@3.0.3: resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==} engines: {node: '>=6'} @@ -4005,7 +4167,6 @@ packages: engines: {node: '>=10'} dependencies: yallist: 4.0.0 - dev: true /macos-release@2.5.1: resolution: {integrity: sha512-DXqXhEM7gW59OjZO8NIjBCz9AQ1BEMrfiOAl4AYByHCtVHRF4KoGNO8mqQeM8lRCtQe/UnJ4imO/d2HdkKsd+A==} @@ -4024,7 +4185,6 @@ packages: engines: {node: '>=8'} dependencies: semver: 6.3.0 - dev: true /make-error@1.3.6: resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} @@ -4100,7 +4260,6 @@ packages: resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} dependencies: brace-expansion: 1.1.11 - dev: true /minimatch@5.1.6: resolution: {integrity: sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==} @@ -4121,26 +4280,51 @@ packages: engines: {node: '>=16 || 14 >=14.17'} dependencies: brace-expansion: 2.0.1 - dev: false /minimist@1.2.8: resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + /minipass@3.3.6: + resolution: {integrity: sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==} + engines: {node: '>=8'} + dependencies: + yallist: 4.0.0 + dev: false + /minipass@4.2.8: resolution: {integrity: sha512-fNzuVyifolSLFL4NzpF+wEF4qrgqaaKX0haXPQEdQ7NKAN+WecoKMHV09YcuL/DHxrUsYQOK3MiuDf7Ip2OXfQ==} engines: {node: '>=8'} dev: true + /minipass@5.0.0: + resolution: {integrity: sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==} + engines: {node: '>=8'} + dev: false + /minipass@6.0.2: resolution: {integrity: sha512-MzWSV5nYVT7mVyWCwn2o7JH13w2TBRmmSqSRCKzTw+lmft9X4z+3wjvs06Tzijo5z4W/kahUCDpRXTF+ZrmF/w==} engines: {node: '>=16 || 14 >=14.17'} + /minizlib@2.1.2: + resolution: {integrity: sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==} + engines: {node: '>= 8'} + dependencies: + minipass: 3.3.6 + yallist: 4.0.0 + dev: false + /mkdirp@0.5.6: resolution: {integrity: sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==} hasBin: true dependencies: minimist: 1.2.8 + /mkdirp@1.0.4: + resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==} + engines: {node: '>=10'} + hasBin: true + dev: false + /mkdirp@2.1.6: resolution: {integrity: sha512-+hEnITedc8LAtIP9u3HJDFIdcLV2vXP33sqLLIzkv1Db1zO/1OxbvYf0Y1OC/S/Qo5dxHXepofhmxL02PsKe+A==} engines: {node: '>=10'} @@ -4212,6 +4396,10 @@ packages: resolution: {integrity: sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ==} dev: true + /node-addon-api@5.1.0: + resolution: {integrity: sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA==} + dev: false + /node-emoji@1.11.0: resolution: {integrity: sha512-wo2DpQkQp7Sjm2A0cq+sN7EHKO6Sl0ctXeBdFZrL9T9+UywORbufTcTZxom8YqpLQt/FqNMUkOpkZrJVYSKD3A==} dependencies: @@ -4237,6 +4425,14 @@ packages: resolution: {integrity: sha512-QzsYKWhXTWx8h1kIvqfnC++o0pEmpRQA/aenALsL2F4pqNVr7YzcdMlDij5WBnwftRbJCNJL/O7zdKaxKPHqgQ==} dev: true + /nopt@5.0.0: + resolution: {integrity: sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==} + engines: {node: '>=6'} + hasBin: true + dependencies: + abbrev: 1.1.1 + dev: false + /normalize-path@3.0.0: resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} engines: {node: '>=0.10.0'} @@ -4249,6 +4445,15 @@ packages: path-key: 3.1.1 dev: true + /npmlog@5.0.1: + resolution: {integrity: sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==} + dependencies: + are-we-there-yet: 2.0.0 + console-control-strings: 1.1.0 + gauge: 3.0.2 + set-blocking: 2.0.0 + dev: false + /object-assign@4.1.1: resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} engines: {node: '>=0.10.0'} @@ -4394,7 +4599,6 @@ packages: /path-is-absolute@1.0.1: resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} engines: {node: '>=0.10.0'} - dev: true /path-key@3.1.1: resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} @@ -4669,7 +4873,6 @@ packages: inherits: 2.0.4 string_decoder: 1.3.0 util-deprecate: 1.0.2 - dev: true /readdirp@3.6.0: resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} @@ -4760,7 +4963,6 @@ packages: hasBin: true dependencies: glob: 7.2.3 - dev: true /rimraf@4.4.1: resolution: {integrity: sha512-Gk8NlF062+T9CqNGn6h4tls3k6T1+/nXdOcSZVikNVtlRdYpA7wRJJMoXmuvOnLW844rPjdQ7JgXCYM6PPC/og==} @@ -4815,7 +5017,6 @@ packages: /semver@6.3.0: resolution: {integrity: sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==} hasBin: true - dev: true /semver@7.5.3: resolution: {integrity: sha512-QBlUtyVk/5EeHbi7X0fw6liDZc7BBmEaSYn01fMU1OUYbf6GPsbTtd8WmnqbI20SeycoHSeiybkE/q1Q+qlThQ==} @@ -4823,7 +5024,6 @@ packages: hasBin: true dependencies: lru-cache: 6.0.0 - dev: true /send@0.18.0: resolution: {integrity: sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==} @@ -4862,6 +5062,10 @@ packages: transitivePeerDependencies: - supports-color + /set-blocking@2.0.0: + resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==} + dev: false + /setprototypeof@1.2.0: resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} @@ -4902,12 +5106,10 @@ packages: /signal-exit@3.0.7: resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} - dev: true /signal-exit@4.0.2: resolution: {integrity: sha512-MY2/qGx4enyjprQnFaZsHib3Yadh3IXyV2C321GY0pjGfVBu4un0uDJkwgdxqO+Rdx8JMT8IfJIRwbYVz3Ob3Q==} engines: {node: '>=14'} - dev: false /sisteransi@1.0.5: resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} @@ -4989,7 +5191,6 @@ packages: eastasianwidth: 0.2.0 emoji-regex: 9.2.2 strip-ansi: 7.1.0 - dev: false /string_decoder@1.1.1: resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==} @@ -5000,7 +5201,6 @@ packages: resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} dependencies: safe-buffer: 5.2.1 - dev: true /strip-ansi@6.0.1: resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} @@ -5013,7 +5213,6 @@ packages: engines: {node: '>=12'} dependencies: ansi-regex: 6.0.1 - dev: false /strip-bom@3.0.0: resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} @@ -5101,6 +5300,18 @@ packages: engines: {node: '>=6'} dev: true + /tar@6.1.15: + resolution: {integrity: sha512-/zKt9UyngnxIT/EAGYuxaMYgOIJiP81ab9ZfkILq4oNLPFX50qyYmu7jRj9qeXoxmJHjGlbH0+cm2uy1WCs10A==} + engines: {node: '>=10'} + dependencies: + chownr: 2.0.0 + fs-minipass: 2.1.0 + minipass: 5.0.0 + minizlib: 2.1.2 + mkdirp: 1.0.4 + yallist: 4.0.0 + dev: false + /tarn@3.0.2: resolution: {integrity: sha512-51LAVKUSZSVfI05vjPESNc5vwqqZpbXCsU+/+wxlOrUjk2SnFTt97v9ZgQrD4YmxYW1Px6w2KjaDitCfkvgxMQ==} engines: {node: '>=8.0.0'} @@ -5667,6 +5878,12 @@ packages: dependencies: isexe: 2.0.0 + /wide-align@1.1.5: + resolution: {integrity: sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==} + dependencies: + string-width: 4.2.3 + dev: false + /windows-release@4.0.0: resolution: {integrity: sha512-OxmV4wzDKB1x7AZaZgXMVsdJ1qER1ed83ZrTYd5Bwq2HfJVg3DJS8nqlAG4sMoJ7mu8cuRmLEYyU13BKwctRAg==} engines: {node: '>=10'} @@ -5689,7 +5906,6 @@ packages: ansi-styles: 6.2.1 string-width: 5.1.2 strip-ansi: 7.1.0 - dev: false /wrappy@1.0.2: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} @@ -5716,7 +5932,6 @@ packages: /yallist@4.0.0: resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} - dev: true /yaml@1.10.2: resolution: {integrity: sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==} diff --git a/webpack.config.js b/webpack.config.js new file mode 100644 index 0000000..a64f8db --- /dev/null +++ b/webpack.config.js @@ -0,0 +1,34 @@ +/* eslint-disable @typescript-eslint/no-var-requires */ +const glob = require('glob'); +const path = require('path'); + +const writeGlobEntry = (pattern, to) => { + return glob.sync(pattern).reduce( + (collection, file) => ({ + ...collection, + [`${to}/${path.basename(file, '.ts')}`]: file, + }), + {}, + ); +}; + +module.exports = function (config) { + const appName = process.argv[process.argv.length - 1]; + config.entry = { + main: path.join(__dirname, 'apps', appName, 'src', 'main.ts'), + ...writeGlobEntry( + path.resolve('apps', appName, 'src', 'database', 'migrations', '*.ts'), + 'database/migrations', + ), + ...writeGlobEntry( + path.resolve('apps', appName, 'src', 'database', 'seeds', '*.ts'), + 'database/seeds', + ), + }; + config.output = { + path: `${__dirname}/dist/apps/${appName}`, + filename: '[name].js', + libraryTarget: 'commonjs', + }; + return config; +};