speedrun-cli 2.6.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.
- package/CHANGELOG.md +96 -0
- package/LICENSE +21 -0
- package/README.md +620 -0
- package/bin/cli.js +224 -0
- package/index.js +12 -0
- package/package.json +74 -0
- package/src/constants.js +65 -0
- package/src/generator.js +271 -0
- package/src/index.js +13 -0
- package/src/moduleGenerator.js +586 -0
- package/src/postSetup.js +365 -0
- package/src/prompts.js +189 -0
- package/src/utils.js +112 -0
- package/templates/README.md +81 -0
- package/templates/base/eslint.config.mjs +34 -0
- package/templates/base/gitignore +78 -0
- package/templates/base/nest-cli.json +8 -0
- package/templates/base/src/common/constants/cookie.config.ts +20 -0
- package/templates/base/src/common/decorators/get-user.decorator.ts +13 -0
- package/templates/base/src/common/decorators/public.decorator.ts +4 -0
- package/templates/base/src/common/decorators/roles.decorator.ts +4 -0
- package/templates/base/src/common/dtos/pagination.dto.ts +29 -0
- package/templates/base/src/common/filters/http-exception.filter.ts +108 -0
- package/templates/base/src/common/guards/auth.guard.ts +51 -0
- package/templates/base/src/common/guards/refresh-token.guard.ts +39 -0
- package/templates/base/src/common/guards/roles.guard.ts +45 -0
- package/templates/base/src/common/interceptors/response.interceptor.ts +55 -0
- package/templates/base/src/common/interfaces/api-response.interface.ts +26 -0
- package/templates/base/src/common/middleware/correlation-id.middleware.ts +20 -0
- package/templates/base/src/common/validators/password.validator.ts +37 -0
- package/templates/base/src/config/config.module.ts +14 -0
- package/templates/base/src/config/logger.config.ts +109 -0
- package/templates/base/src/main.ts +84 -0
- package/templates/base/src/modules/auth/auth.controller.ts +133 -0
- package/templates/base/src/modules/auth/dtos/login.dto.ts +11 -0
- package/templates/base/src/modules/auth/dtos/signup.dto.ts +8 -0
- package/templates/base/src/modules/health/health.controller.ts +20 -0
- package/templates/base/src/modules/health/health.module.ts +7 -0
- package/templates/base/src/modules/users/dtos/update-profile.dto.ts +12 -0
- package/templates/base/src/modules/users/dtos/update-user.dto.ts +13 -0
- package/templates/base/src/modules/users/users.controller.ts +65 -0
- package/templates/base/test/app.e2e-spec.ts +24 -0
- package/templates/base/test/jest-e2e.json +9 -0
- package/templates/base/tsconfig.build.json +4 -0
- package/templates/base/tsconfig.json +24 -0
- package/templates/base-crud/CRUD_README.md +385 -0
- package/templates/base-crud/src/common/base/base.controller.ts +321 -0
- package/templates/base-crud/src/common/base/base.service.ts +192 -0
- package/templates/base-crud/src/common/base/index.ts +20 -0
- package/templates/base-crud/src/common/base/swagger/api-response.dto.ts +82 -0
- package/templates/base-crud/src/common/base/swagger/paginated.dto.ts +102 -0
- package/templates/base-crud/src/modules/products/dto/create-product.dto.ts +44 -0
- package/templates/base-crud/src/modules/products/dto/product.dto.ts +38 -0
- package/templates/base-crud/src/modules/products/dto/update-product.dto.ts +12 -0
- package/templates/base-crud/src/modules/products/products.controller.ts +94 -0
- package/templates/base-crud-drizzle/src/modules/products/products.controller.ts +79 -0
- package/templates/base-crud-drizzle/src/modules/products/products.module.ts +24 -0
- package/templates/base-crud-drizzle/src/modules/products/products.service.ts +140 -0
- package/templates/base-crud-drizzle/src/modules/products/schema/products.schema.ts +39 -0
- package/templates/base-crud-mongoose/src/modules/products/products.controller.ts +79 -0
- package/templates/base-crud-mongoose/src/modules/products/products.module.ts +26 -0
- package/templates/base-crud-mongoose/src/modules/products/products.service.ts +133 -0
- package/templates/base-crud-mongoose/src/modules/products/schemas/product.schema.ts +67 -0
- package/templates/base-crud-prisma/src/modules/products/products.module.ts +12 -0
- package/templates/base-crud-prisma/src/modules/products/products.service.ts +100 -0
- package/templates/base-crud-typeorm/src/modules/products/entities/product.entity.ts +58 -0
- package/templates/base-crud-typeorm/src/modules/products/products.controller.ts +79 -0
- package/templates/base-crud-typeorm/src/modules/products/products.module.ts +25 -0
- package/templates/base-crud-typeorm/src/modules/products/products.service.ts +102 -0
- package/templates/database/mongodb/.env.example +22 -0
- package/templates/database/mysql/.env.example +24 -0
- package/templates/database/mysql/drizzle.config.ts +13 -0
- package/templates/database/mysql/package.json +5 -0
- package/templates/database/mysql/prisma/schema.prisma +55 -0
- package/templates/database/mysql/src/database/drizzle.ts +13 -0
- package/templates/database/mysql/src/database/schema.ts +58 -0
- package/templates/database/postgres/.env.example +24 -0
- package/templates/database/postgres/drizzle.config.ts +13 -0
- package/templates/database/postgres/package.json +8 -0
- package/templates/database/postgres/prisma/schema.prisma +55 -0
- package/templates/database/sqlite/.env.example +24 -0
- package/templates/database/sqlite/drizzle.config.ts +13 -0
- package/templates/database/sqlite/package.json +8 -0
- package/templates/database/sqlite/prisma/schema.prisma +48 -0
- package/templates/database/sqlite/src/database/drizzle.ts +11 -0
- package/templates/database/sqlite/src/database/schema.ts +52 -0
- package/templates/orm/drizzle/drizzle.config.ts +13 -0
- package/templates/orm/drizzle/package.json +90 -0
- package/templates/orm/drizzle/src/app.module.ts +73 -0
- package/templates/orm/drizzle/src/config/env.validation.ts +55 -0
- package/templates/orm/drizzle/src/database/database.module.ts +25 -0
- package/templates/orm/drizzle/src/database/drizzle.ts +13 -0
- package/templates/orm/drizzle/src/database/schema.ts +60 -0
- package/templates/orm/drizzle/src/database/seed.ts +87 -0
- package/templates/orm/drizzle/src/modules/auth/auth.module.ts +12 -0
- package/templates/orm/drizzle/src/modules/auth/auth.service.ts +298 -0
- package/templates/orm/drizzle/src/modules/health/health.controller.ts +34 -0
- package/templates/orm/drizzle/src/modules/health/health.module.ts +7 -0
- package/templates/orm/drizzle/src/modules/users/users.module.ts +10 -0
- package/templates/orm/drizzle/src/modules/users/users.service.ts +152 -0
- package/templates/orm/mongoose/.env.example +22 -0
- package/templates/orm/mongoose/package.json +86 -0
- package/templates/orm/mongoose/src/app.module.ts +73 -0
- package/templates/orm/mongoose/src/config/env.validation.ts +55 -0
- package/templates/orm/mongoose/src/database/database.module.ts +19 -0
- package/templates/orm/mongoose/src/database/seed.ts +107 -0
- package/templates/orm/mongoose/src/modules/auth/auth.module.ts +21 -0
- package/templates/orm/mongoose/src/modules/auth/auth.service.ts +272 -0
- package/templates/orm/mongoose/src/modules/auth/dtos/login.dto.ts +10 -0
- package/templates/orm/mongoose/src/modules/auth/dtos/signup.dto.ts +8 -0
- package/templates/orm/mongoose/src/modules/health/health.controller.ts +26 -0
- package/templates/orm/mongoose/src/modules/health/health.module.ts +7 -0
- package/templates/orm/mongoose/src/modules/users/dtos/update-profile.dto.ts +24 -0
- package/templates/orm/mongoose/src/modules/users/dtos/update-user.dto.ts +13 -0
- package/templates/orm/mongoose/src/modules/users/users.module.ts +19 -0
- package/templates/orm/mongoose/src/modules/users/users.service.ts +179 -0
- package/templates/orm/mongoose/src/schemas/index.ts +2 -0
- package/templates/orm/mongoose/src/schemas/refresh-token.schema.ts +55 -0
- package/templates/orm/mongoose/src/schemas/user.schema.ts +53 -0
- package/templates/orm/prisma/package.json +102 -0
- package/templates/orm/prisma/prisma/schema.prisma +60 -0
- package/templates/orm/prisma/prisma/seed.ts +69 -0
- package/templates/orm/prisma/src/app.module.ts +55 -0
- package/templates/orm/prisma/src/config/env.validation.ts +56 -0
- package/templates/orm/prisma/src/modules/auth/auth.module.ts +17 -0
- package/templates/orm/prisma/src/modules/auth/auth.service.ts +308 -0
- package/templates/orm/prisma/src/modules/health/health.controller.ts +26 -0
- package/templates/orm/prisma/src/modules/health/health.module.ts +10 -0
- package/templates/orm/prisma/src/modules/users/users.module.ts +11 -0
- package/templates/orm/prisma/src/modules/users/users.service.ts +105 -0
- package/templates/orm/prisma/src/prisma/prisma.module.ts +9 -0
- package/templates/orm/prisma/src/prisma/prisma.service.ts +16 -0
- package/templates/orm/typeorm/package.json +100 -0
- package/templates/orm/typeorm/src/app.module.ts +55 -0
- package/templates/orm/typeorm/src/config/env.validation.ts +58 -0
- package/templates/orm/typeorm/src/database/data-source.ts +19 -0
- package/templates/orm/typeorm/src/database/database.module.ts +27 -0
- package/templates/orm/typeorm/src/database/seed.ts +76 -0
- package/templates/orm/typeorm/src/entities/index.ts +2 -0
- package/templates/orm/typeorm/src/entities/refresh-token.entity.ts +45 -0
- package/templates/orm/typeorm/src/entities/user.entity.ts +51 -0
- package/templates/orm/typeorm/src/modules/auth/auth.module.ts +19 -0
- package/templates/orm/typeorm/src/modules/auth/auth.service.ts +286 -0
- package/templates/orm/typeorm/src/modules/health/health.controller.ts +24 -0
- package/templates/orm/typeorm/src/modules/health/health.module.ts +9 -0
- package/templates/orm/typeorm/src/modules/users/users.module.ts +13 -0
- package/templates/orm/typeorm/src/modules/users/users.service.ts +108 -0
- package/templates/swagger/src/common/dtos/pagination.dto.ts +43 -0
- package/templates/swagger/src/main.ts +116 -0
- package/templates/swagger/src/modules/auth/auth.controller.ts +235 -0
- package/templates/swagger/src/modules/auth/dtos/login.dto.ts +20 -0
- package/templates/swagger/src/modules/auth/dtos/signup.dto.ts +14 -0
- package/templates/swagger/src/modules/users/dtos/update-profile.dto.ts +19 -0
- package/templates/swagger/src/modules/users/dtos/update-user.dto.ts +23 -0
- package/templates/swagger/src/modules/users/users.controller.ts +214 -0
- package/templates/swagger-mongoose/src/modules/auth/dtos/login.dto.ts +20 -0
- package/templates/swagger-mongoose/src/modules/auth/dtos/signup.dto.ts +14 -0
- package/templates/swagger-mongoose/src/modules/users/dtos/update-profile.dto.ts +40 -0
- package/templates/swagger-mongoose/src/modules/users/dtos/update-user.dto.ts +23 -0
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { Config } from 'drizzle-kit';
|
|
2
|
+
import * as dotenv from 'dotenv';
|
|
3
|
+
|
|
4
|
+
dotenv.config();
|
|
5
|
+
|
|
6
|
+
export default {
|
|
7
|
+
schema: './src/database/schema.ts',
|
|
8
|
+
out: './drizzle',
|
|
9
|
+
dialect: 'postgresql',
|
|
10
|
+
dbCredentials: {
|
|
11
|
+
url: process.env.DATABASE_URL!,
|
|
12
|
+
},
|
|
13
|
+
} satisfies Config;
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "nestjs-auth-drizzle",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "NestJS Authentication API with Drizzle ORM",
|
|
5
|
+
"author": "",
|
|
6
|
+
"private": true,
|
|
7
|
+
"license": "MIT",
|
|
8
|
+
"scripts": {
|
|
9
|
+
"build": "nest build",
|
|
10
|
+
"format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
|
|
11
|
+
"start": "nest start",
|
|
12
|
+
"start:dev": "nest start --watch",
|
|
13
|
+
"start:debug": "nest start --debug --watch",
|
|
14
|
+
"start:prod": "node dist/main",
|
|
15
|
+
"lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
|
|
16
|
+
"test": "jest",
|
|
17
|
+
"test:watch": "jest --watch",
|
|
18
|
+
"test:cov": "jest --coverage",
|
|
19
|
+
"test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
|
|
20
|
+
"test:e2e": "jest --config ./test/jest-e2e.json",
|
|
21
|
+
"db:generate": "drizzle-kit generate",
|
|
22
|
+
"db:migrate": "drizzle-kit migrate",
|
|
23
|
+
"db:push": "drizzle-kit push",
|
|
24
|
+
"db:studio": "drizzle-kit studio",
|
|
25
|
+
"db:seed": "ts-node src/database/seed.ts"
|
|
26
|
+
},
|
|
27
|
+
"dependencies": {
|
|
28
|
+
"@nestjs/common": "^11.0.0",
|
|
29
|
+
"@nestjs/config": "^4.0.0",
|
|
30
|
+
"@nestjs/core": "^11.0.0",
|
|
31
|
+
"@nestjs/jwt": "^11.0.0",
|
|
32
|
+
"@nestjs/platform-express": "^11.0.0",
|
|
33
|
+
"@nestjs/throttler": "^6.4.0",
|
|
34
|
+
"bcrypt": "^6.0.0",
|
|
35
|
+
"class-transformer": "^0.5.1",
|
|
36
|
+
"class-validator": "^0.14.2",
|
|
37
|
+
"cookie-parser": "^1.4.7",
|
|
38
|
+
"drizzle-orm": "^0.38.0",
|
|
39
|
+
"helmet": "^8.1.0",
|
|
40
|
+
"zod": "^4.1.12",
|
|
41
|
+
"nestjs-pino": "^4.2.0",
|
|
42
|
+
"pino-http": "^10.4.0",
|
|
43
|
+
"pino-pretty": "^13.0.0",
|
|
44
|
+
"reflect-metadata": "^0.2.2",
|
|
45
|
+
"rxjs": "^7.8.1"
|
|
46
|
+
},
|
|
47
|
+
"devDependencies": {
|
|
48
|
+
"@nestjs/cli": "^11.0.0",
|
|
49
|
+
"@nestjs/schematics": "^11.0.0",
|
|
50
|
+
"@nestjs/testing": "^11.0.0",
|
|
51
|
+
"@types/bcrypt": "^6.0.0",
|
|
52
|
+
"@types/cookie-parser": "^1.4.10",
|
|
53
|
+
"@types/express": "^5.0.0",
|
|
54
|
+
"@types/jest": "^30.0.0",
|
|
55
|
+
"@types/node": "^22.10.0",
|
|
56
|
+
"@types/supertest": "^6.0.2",
|
|
57
|
+
"dotenv": "^16.4.7",
|
|
58
|
+
"drizzle-kit": "^0.30.0",
|
|
59
|
+
"eslint": "^9.16.0",
|
|
60
|
+
"eslint-config-prettier": "^10.0.0",
|
|
61
|
+
"eslint-plugin-prettier": "^5.2.2",
|
|
62
|
+
"jest": "^30.0.0",
|
|
63
|
+
"prettier": "^3.4.2",
|
|
64
|
+
"source-map-support": "^0.5.21",
|
|
65
|
+
"supertest": "^7.0.0",
|
|
66
|
+
"ts-jest": "^29.2.5",
|
|
67
|
+
"ts-loader": "^9.5.1",
|
|
68
|
+
"ts-node": "^10.9.2",
|
|
69
|
+
"tsconfig-paths": "^4.2.0",
|
|
70
|
+
"typescript": "^5.7.2",
|
|
71
|
+
"typescript-eslint": "^8.18.0"
|
|
72
|
+
},
|
|
73
|
+
"jest": {
|
|
74
|
+
"moduleFileExtensions": [
|
|
75
|
+
"js",
|
|
76
|
+
"json",
|
|
77
|
+
"ts"
|
|
78
|
+
],
|
|
79
|
+
"rootDir": "src",
|
|
80
|
+
"testRegex": ".*\\.spec\\.ts$",
|
|
81
|
+
"transform": {
|
|
82
|
+
"^.+\\.(t|j)s$": "ts-jest"
|
|
83
|
+
},
|
|
84
|
+
"collectCoverageFrom": [
|
|
85
|
+
"**/*.(t|j)s"
|
|
86
|
+
],
|
|
87
|
+
"coverageDirectory": "../coverage",
|
|
88
|
+
"testEnvironment": "node"
|
|
89
|
+
}
|
|
90
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { Module } from '@nestjs/common';
|
|
2
|
+
import { ConfigModule } from '@nestjs/config';
|
|
3
|
+
import { APP_FILTER, APP_GUARD, APP_INTERCEPTOR } from '@nestjs/core';
|
|
4
|
+
import { ThrottlerModule, ThrottlerGuard } from '@nestjs/throttler';
|
|
5
|
+
import { LoggerModule } from 'nestjs-pino';
|
|
6
|
+
|
|
7
|
+
import { loggerConfig } from './config/logger.config';
|
|
8
|
+
import { validate } from './config/env.validation';
|
|
9
|
+
import { DatabaseModule } from './database/database.module';
|
|
10
|
+
import { AuthModule } from './modules/auth/auth.module';
|
|
11
|
+
import { UsersModule } from './modules/users/users.module';
|
|
12
|
+
import { HealthModule } from './modules/health/health.module';
|
|
13
|
+
import { AuthGuard } from './common/guards/auth.guard';
|
|
14
|
+
import { RolesGuard } from './common/guards/roles.guard';
|
|
15
|
+
import { HttpExceptionFilter } from './common/filters/http-exception.filter';
|
|
16
|
+
import { ResponseInterceptor } from './common/interceptors/response.interceptor';
|
|
17
|
+
|
|
18
|
+
@Module({
|
|
19
|
+
imports: [
|
|
20
|
+
// Configuration
|
|
21
|
+
ConfigModule.forRoot({
|
|
22
|
+
isGlobal: true,
|
|
23
|
+
validate,
|
|
24
|
+
}),
|
|
25
|
+
|
|
26
|
+
// Logging
|
|
27
|
+
LoggerModule.forRoot(loggerConfig),
|
|
28
|
+
|
|
29
|
+
// Rate limiting
|
|
30
|
+
ThrottlerModule.forRoot([
|
|
31
|
+
{
|
|
32
|
+
ttl: 60000, // 1 minute
|
|
33
|
+
limit: 100, // 100 requests per minute
|
|
34
|
+
},
|
|
35
|
+
]),
|
|
36
|
+
|
|
37
|
+
// Database (PostgreSQL via Drizzle)
|
|
38
|
+
DatabaseModule,
|
|
39
|
+
|
|
40
|
+
// Feature modules
|
|
41
|
+
AuthModule,
|
|
42
|
+
UsersModule,
|
|
43
|
+
HealthModule,
|
|
44
|
+
],
|
|
45
|
+
providers: [
|
|
46
|
+
// Global exception filter
|
|
47
|
+
{
|
|
48
|
+
provide: APP_FILTER,
|
|
49
|
+
useClass: HttpExceptionFilter,
|
|
50
|
+
},
|
|
51
|
+
// Global response interceptor
|
|
52
|
+
{
|
|
53
|
+
provide: APP_INTERCEPTOR,
|
|
54
|
+
useClass: ResponseInterceptor,
|
|
55
|
+
},
|
|
56
|
+
// Global rate limiter
|
|
57
|
+
{
|
|
58
|
+
provide: APP_GUARD,
|
|
59
|
+
useClass: ThrottlerGuard,
|
|
60
|
+
},
|
|
61
|
+
// Global auth guard
|
|
62
|
+
{
|
|
63
|
+
provide: APP_GUARD,
|
|
64
|
+
useClass: AuthGuard,
|
|
65
|
+
},
|
|
66
|
+
// Global roles guard
|
|
67
|
+
{
|
|
68
|
+
provide: APP_GUARD,
|
|
69
|
+
useClass: RolesGuard,
|
|
70
|
+
},
|
|
71
|
+
],
|
|
72
|
+
})
|
|
73
|
+
export class AppModule {}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
|
|
3
|
+
export const envSchema = z.object({
|
|
4
|
+
NODE_ENV: z
|
|
5
|
+
.enum(['development', 'production', 'test'])
|
|
6
|
+
.default('development'),
|
|
7
|
+
PORT: z.coerce.number().default(8080),
|
|
8
|
+
|
|
9
|
+
// Database
|
|
10
|
+
DATABASE_URL: z.string().min(1, 'DATABASE_URL is required'),
|
|
11
|
+
|
|
12
|
+
// JWT Secrets
|
|
13
|
+
JWT_ACCESS_SECRET: z
|
|
14
|
+
.string()
|
|
15
|
+
.min(32, 'JWT_ACCESS_SECRET must be at least 32 characters'),
|
|
16
|
+
JWT_REFRESH_SECRET: z
|
|
17
|
+
.string()
|
|
18
|
+
.min(32, 'JWT_REFRESH_SECRET must be at least 32 characters'),
|
|
19
|
+
JWT_ACCESS_EXPIRY: z
|
|
20
|
+
.string()
|
|
21
|
+
.regex(/^\d+[smhd]$/, 'JWT_ACCESS_EXPIRY must be in format: 60m, 1h, etc.')
|
|
22
|
+
.default('15m'),
|
|
23
|
+
JWT_REFRESH_EXPIRY: z
|
|
24
|
+
.string()
|
|
25
|
+
.regex(/^\d+[smhd]$/, 'JWT_REFRESH_EXPIRY must be in format: 7d, 30d, etc.')
|
|
26
|
+
.default('7d'),
|
|
27
|
+
|
|
28
|
+
// CORS
|
|
29
|
+
CORS_ORIGIN: z
|
|
30
|
+
.string()
|
|
31
|
+
.refine((val) => {
|
|
32
|
+
const origins = val.split(',').map((o) => o.trim());
|
|
33
|
+
const urlRegex = /^https?:\/\/.+/;
|
|
34
|
+
return origins.every((origin) => urlRegex.test(origin) || origin === '*');
|
|
35
|
+
}, 'CORS_ORIGIN must be valid URL(s) or "*"')
|
|
36
|
+
.default('http://localhost:3000'),
|
|
37
|
+
|
|
38
|
+
// Logging
|
|
39
|
+
LOG_LEVEL: z
|
|
40
|
+
.enum(['fatal', 'error', 'warn', 'info', 'debug', 'trace'])
|
|
41
|
+
.default('info'),
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
export type Env = z.infer<typeof envSchema>;
|
|
45
|
+
|
|
46
|
+
export function validate(config: Record<string, unknown>) {
|
|
47
|
+
const result = envSchema.safeParse(config);
|
|
48
|
+
if (!result.success) {
|
|
49
|
+
const errors = result.error.issues
|
|
50
|
+
.map((e) => `${e.path.join('.')}: ${e.message}`)
|
|
51
|
+
.join('\n');
|
|
52
|
+
throw new Error(`Environment validation failed:\n${errors}`);
|
|
53
|
+
}
|
|
54
|
+
return result.data;
|
|
55
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { Module, Global } from '@nestjs/common';
|
|
2
|
+
import { ConfigService } from '@nestjs/config';
|
|
3
|
+
import { drizzle } from 'drizzle-orm/node-postgres';
|
|
4
|
+
import { Pool } from 'pg';
|
|
5
|
+
import * as schema from './schema';
|
|
6
|
+
|
|
7
|
+
export const DRIZZLE = Symbol('DRIZZLE');
|
|
8
|
+
|
|
9
|
+
@Global()
|
|
10
|
+
@Module({
|
|
11
|
+
providers: [
|
|
12
|
+
{
|
|
13
|
+
provide: DRIZZLE,
|
|
14
|
+
inject: [ConfigService],
|
|
15
|
+
useFactory: (configService: ConfigService) => {
|
|
16
|
+
const pool = new Pool({
|
|
17
|
+
connectionString: configService.get<string>('DATABASE_URL'),
|
|
18
|
+
});
|
|
19
|
+
return drizzle(pool, { schema });
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
],
|
|
23
|
+
exports: [DRIZZLE],
|
|
24
|
+
})
|
|
25
|
+
export class DatabaseModule {}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { drizzle } from 'drizzle-orm/node-postgres';
|
|
2
|
+
import { Pool } from 'pg';
|
|
3
|
+
import * as schema from './schema';
|
|
4
|
+
|
|
5
|
+
// Create PostgreSQL connection pool
|
|
6
|
+
const pool = new Pool({
|
|
7
|
+
connectionString: process.env.DATABASE_URL,
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
// Create drizzle instance with schema
|
|
11
|
+
export const db = drizzle(pool, { schema });
|
|
12
|
+
|
|
13
|
+
export type DrizzleDB = typeof db;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import {
|
|
2
|
+
pgTable,
|
|
3
|
+
varchar,
|
|
4
|
+
text,
|
|
5
|
+
timestamp,
|
|
6
|
+
boolean,
|
|
7
|
+
pgEnum,
|
|
8
|
+
uuid,
|
|
9
|
+
} from 'drizzle-orm/pg-core';
|
|
10
|
+
import { relations } from 'drizzle-orm';
|
|
11
|
+
|
|
12
|
+
// UserRole enum
|
|
13
|
+
export const userRoleEnum = pgEnum('user_role', ['USER', 'ADMIN']);
|
|
14
|
+
|
|
15
|
+
// Users table
|
|
16
|
+
export const users = pgTable('users', {
|
|
17
|
+
id: uuid('id').defaultRandom().primaryKey(),
|
|
18
|
+
fullName: varchar('full_name', { length: 255 }).notNull(),
|
|
19
|
+
email: varchar('email', { length: 255 }).notNull().unique(),
|
|
20
|
+
passwordHash: text('password_hash').notNull(),
|
|
21
|
+
role: userRoleEnum('role').default('USER').notNull(),
|
|
22
|
+
isActive: boolean('is_active').default(true).notNull(),
|
|
23
|
+
createdAt: timestamp('created_at').defaultNow().notNull(),
|
|
24
|
+
updatedAt: timestamp('updated_at').defaultNow().notNull(),
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
// Refresh tokens table
|
|
28
|
+
export const refreshTokens = pgTable('refresh_tokens', {
|
|
29
|
+
id: uuid('id').defaultRandom().primaryKey(),
|
|
30
|
+
token: text('token').notNull(),
|
|
31
|
+
userId: uuid('user_id')
|
|
32
|
+
.notNull()
|
|
33
|
+
.references(() => users.id, { onDelete: 'cascade' }),
|
|
34
|
+
userAgent: varchar('user_agent', { length: 500 }).notNull(),
|
|
35
|
+
ipAddress: varchar('ip_address', { length: 45 }).notNull(),
|
|
36
|
+
expiresAt: timestamp('expires_at').notNull(),
|
|
37
|
+
isRevoked: boolean('is_revoked').default(false).notNull(),
|
|
38
|
+
createdAt: timestamp('created_at').defaultNow().notNull(),
|
|
39
|
+
updatedAt: timestamp('updated_at').defaultNow().notNull(),
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
// Relations
|
|
43
|
+
export const usersRelations = relations(users, ({ many }) => ({
|
|
44
|
+
refreshTokens: many(refreshTokens),
|
|
45
|
+
}));
|
|
46
|
+
|
|
47
|
+
export const refreshTokensRelations = relations(refreshTokens, ({ one }) => ({
|
|
48
|
+
user: one(users, {
|
|
49
|
+
fields: [refreshTokens.userId],
|
|
50
|
+
references: [users.id],
|
|
51
|
+
}),
|
|
52
|
+
}));
|
|
53
|
+
|
|
54
|
+
// Types
|
|
55
|
+
export type User = typeof users.$inferSelect;
|
|
56
|
+
export type NewUser = typeof users.$inferInsert;
|
|
57
|
+
export type RefreshToken = typeof refreshTokens.$inferSelect;
|
|
58
|
+
export type NewRefreshToken = typeof refreshTokens.$inferInsert;
|
|
59
|
+
|
|
60
|
+
export type UserRole = 'USER' | 'ADMIN';
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { drizzle } from 'drizzle-orm/node-postgres';
|
|
2
|
+
import { Pool } from 'pg';
|
|
3
|
+
import * as bcrypt from 'bcrypt';
|
|
4
|
+
import * as dotenv from 'dotenv';
|
|
5
|
+
import { eq } from 'drizzle-orm';
|
|
6
|
+
import { users } from './schema';
|
|
7
|
+
|
|
8
|
+
dotenv.config();
|
|
9
|
+
|
|
10
|
+
const SALT_ROUNDS = 12;
|
|
11
|
+
|
|
12
|
+
async function seed() {
|
|
13
|
+
const databaseUrl = process.env.DATABASE_URL;
|
|
14
|
+
|
|
15
|
+
if (!databaseUrl) {
|
|
16
|
+
console.error('ā DATABASE_URL is not defined in environment variables');
|
|
17
|
+
process.exit(1);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
console.log('š± Starting database seed...\n');
|
|
21
|
+
|
|
22
|
+
const pool = new Pool({ connectionString: databaseUrl });
|
|
23
|
+
const db = drizzle(pool);
|
|
24
|
+
|
|
25
|
+
try {
|
|
26
|
+
// Check if admin already exists
|
|
27
|
+
const [existingAdmin] = await db
|
|
28
|
+
.select()
|
|
29
|
+
.from(users)
|
|
30
|
+
.where(eq(users.email, 'admin@example.com'))
|
|
31
|
+
.limit(1);
|
|
32
|
+
|
|
33
|
+
if (existingAdmin) {
|
|
34
|
+
console.log('ā ļø Admin user already exists, skipping...');
|
|
35
|
+
} else {
|
|
36
|
+
// Create admin user
|
|
37
|
+
const adminPasswordHash = await bcrypt.hash('Admin@123', SALT_ROUNDS);
|
|
38
|
+
|
|
39
|
+
await db.insert(users).values({
|
|
40
|
+
fullName: 'Admin User',
|
|
41
|
+
email: 'admin@example.com',
|
|
42
|
+
passwordHash: adminPasswordHash,
|
|
43
|
+
role: 'ADMIN',
|
|
44
|
+
isActive: true,
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
console.log('ā Created admin user');
|
|
48
|
+
console.log(' Email: admin@example.com');
|
|
49
|
+
console.log(' Password: Admin@123');
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Check if regular user already exists
|
|
53
|
+
const [existingUser] = await db
|
|
54
|
+
.select()
|
|
55
|
+
.from(users)
|
|
56
|
+
.where(eq(users.email, 'user@example.com'))
|
|
57
|
+
.limit(1);
|
|
58
|
+
|
|
59
|
+
if (existingUser) {
|
|
60
|
+
console.log('ā ļø Regular user already exists, skipping...');
|
|
61
|
+
} else {
|
|
62
|
+
// Create regular user
|
|
63
|
+
const userPasswordHash = await bcrypt.hash('User@123', SALT_ROUNDS);
|
|
64
|
+
|
|
65
|
+
await db.insert(users).values({
|
|
66
|
+
fullName: 'Regular User',
|
|
67
|
+
email: 'user@example.com',
|
|
68
|
+
passwordHash: userPasswordHash,
|
|
69
|
+
role: 'USER',
|
|
70
|
+
isActive: true,
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
console.log('ā Created regular user');
|
|
74
|
+
console.log(' Email: user@example.com');
|
|
75
|
+
console.log(' Password: User@123');
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
console.log('\nā
Database seed completed successfully!\n');
|
|
79
|
+
} catch (error) {
|
|
80
|
+
console.error('ā Seed failed:', error);
|
|
81
|
+
process.exit(1);
|
|
82
|
+
} finally {
|
|
83
|
+
await pool.end();
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
seed();
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { Module } from '@nestjs/common';
|
|
2
|
+
import { JwtModule } from '@nestjs/jwt';
|
|
3
|
+
import { AuthController } from './auth.controller';
|
|
4
|
+
import { AuthService } from './auth.service';
|
|
5
|
+
|
|
6
|
+
@Module({
|
|
7
|
+
imports: [JwtModule.register({})],
|
|
8
|
+
controllers: [AuthController],
|
|
9
|
+
providers: [AuthService],
|
|
10
|
+
exports: [AuthService, JwtModule],
|
|
11
|
+
})
|
|
12
|
+
export class AuthModule {}
|