@urbansolv/create-nestjs-app 1.2.4 → 1.2.7

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.
Files changed (65) hide show
  1. package/dist/templates/nestjs-app/src/common/dto/api-response.dto.ts +2 -2
  2. package/dist/templates/nestjs-app/src/common/filters/http-exception.filter.ts +1 -1
  3. package/dist/templates/nestjs-app/src/common/interceptors/transform.interceptor.ts +2 -2
  4. package/dist/templates/nestjs-app/src/common/prisma/prisma.service.ts +0 -1
  5. package/dist/templates/nestjs-app/src/modules/auth/auth.service.ts +0 -7
  6. package/dist/templates/nestjs-app/src/modules/auth/controllers/v1/auth.controller.ts +4 -0
  7. package/dist/templates/nestjs-app/src/modules/users/controllers/v1/users.controller.ts +2 -2
  8. package/dist/templates/nestjs-app/src/modules/users/users.service.ts +4 -0
  9. package/package.json +2 -1
  10. package/templates/nestjs-app/.editorconfig +12 -0
  11. package/templates/nestjs-app/.env.example +24 -0
  12. package/templates/nestjs-app/.eslintrc.js +25 -0
  13. package/templates/nestjs-app/.prettierrc +8 -0
  14. package/templates/nestjs-app/README.md +133 -0
  15. package/templates/nestjs-app/nest-cli.json +10 -0
  16. package/templates/nestjs-app/package.json +88 -0
  17. package/templates/nestjs-app/prisma/schema.prisma +79 -0
  18. package/templates/nestjs-app/prisma/seed.ts +153 -0
  19. package/templates/nestjs-app/src/app.module.ts +68 -0
  20. package/templates/nestjs-app/src/common/constants/permissions.constant.ts +27 -0
  21. package/templates/nestjs-app/src/common/decorators/api-response.decorator.ts +44 -0
  22. package/templates/nestjs-app/src/common/decorators/get-user.decorator.ts +11 -0
  23. package/templates/nestjs-app/src/common/decorators/permissions.decorator.ts +5 -0
  24. package/templates/nestjs-app/src/common/decorators/public.decorator.ts +4 -0
  25. package/templates/nestjs-app/src/common/dto/api-response.dto.ts +21 -0
  26. package/templates/nestjs-app/src/common/dto/pagination.dto.ts +33 -0
  27. package/templates/nestjs-app/src/common/filters/http-exception.filter.ts +56 -0
  28. package/templates/nestjs-app/src/common/guards/jwt-auth.guard.ts +32 -0
  29. package/templates/nestjs-app/src/common/guards/permissions.guard.ts +53 -0
  30. package/templates/nestjs-app/src/common/interceptors/logging.interceptor.ts +37 -0
  31. package/templates/nestjs-app/src/common/interceptors/transform.interceptor.ts +55 -0
  32. package/templates/nestjs-app/src/common/prisma/prisma.module.ts +9 -0
  33. package/templates/nestjs-app/src/common/prisma/prisma.service.ts +45 -0
  34. package/templates/nestjs-app/src/common/utils/password.util.ts +13 -0
  35. package/templates/nestjs-app/src/config/app.config.ts +10 -0
  36. package/templates/nestjs-app/src/config/database.config.ts +5 -0
  37. package/templates/nestjs-app/src/config/env.validation.ts +30 -0
  38. package/templates/nestjs-app/src/config/jwt.config.ts +8 -0
  39. package/templates/nestjs-app/src/config/swagger.config.ts +6 -0
  40. package/templates/nestjs-app/src/main.ts +93 -0
  41. package/templates/nestjs-app/src/modules/auth/auth.module.ts +28 -0
  42. package/templates/nestjs-app/src/modules/auth/auth.service.ts +173 -0
  43. package/templates/nestjs-app/src/modules/auth/controllers/v1/auth.controller.ts +37 -0
  44. package/templates/nestjs-app/src/modules/auth/core/dto/auth-response.dto.ts +19 -0
  45. package/templates/nestjs-app/src/modules/auth/core/dto/login-response.dto.ts +10 -0
  46. package/templates/nestjs-app/src/modules/auth/core/dto/login.dto.ts +15 -0
  47. package/templates/nestjs-app/src/modules/auth/core/dto/register.dto.ts +30 -0
  48. package/templates/nestjs-app/src/modules/auth/core/interfaces/jwt-payload.interface.ts +7 -0
  49. package/templates/nestjs-app/src/modules/auth/core/strategies/jwt.strategy.ts +54 -0
  50. package/templates/nestjs-app/src/modules/health/health.controller.ts +29 -0
  51. package/templates/nestjs-app/src/modules/health/health.module.ts +7 -0
  52. package/templates/nestjs-app/src/modules/users/controllers/v1/users.controller.ts +118 -0
  53. package/templates/nestjs-app/src/modules/users/core/dto/change-position.dto.ts +9 -0
  54. package/templates/nestjs-app/src/modules/users/core/dto/create-user.dto.ts +35 -0
  55. package/templates/nestjs-app/src/modules/users/core/dto/manage-permissions.dto.ts +13 -0
  56. package/templates/nestjs-app/src/modules/users/core/dto/update-user.dto.ts +30 -0
  57. package/templates/nestjs-app/src/modules/users/core/dto/user-query.dto.ts +22 -0
  58. package/templates/nestjs-app/src/modules/users/core/dto/user-response.dto.ts +32 -0
  59. package/templates/nestjs-app/src/modules/users/core/entities/user.entity.ts +45 -0
  60. package/templates/nestjs-app/src/modules/users/core/helpers/user-transform.helper.ts +31 -0
  61. package/templates/nestjs-app/src/modules/users/users.module.ts +10 -0
  62. package/templates/nestjs-app/src/modules/users/users.service.ts +344 -0
  63. package/templates/nestjs-app/test/app.e2e-spec.ts +40 -0
  64. package/templates/nestjs-app/test/jest-e2e.json +9 -0
  65. package/templates/nestjs-app/tsconfig.json +26 -0
@@ -1,8 +1,8 @@
1
1
  import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
2
2
 
3
3
  export class ApiResponseDto<T = any> {
4
- @ApiProperty({ example: true })
5
- success: boolean;
4
+ @ApiProperty({ example: 200 })
5
+ statusCode: number;
6
6
 
7
7
  @ApiProperty({ example: 'Operation successful' })
8
8
  message: string;
@@ -43,7 +43,7 @@ export class HttpExceptionFilter implements ExceptionFilter {
43
43
  );
44
44
 
45
45
  const errorResponse: ApiResponseDto<null> = {
46
- success: false,
46
+ statusCode: status,
47
47
  message,
48
48
  data: null,
49
49
  errors,
@@ -18,7 +18,7 @@ export class TransformInterceptor<T> implements NestInterceptor<T, ApiResponseDt
18
18
  return next.handle().pipe(
19
19
  map((data) => {
20
20
  // If data is already in ApiResponseDto format, return as is
21
- if (data && typeof data === 'object' && 'success' in data && 'message' in data) {
21
+ if (data && typeof data === 'object' && 'statusCode' in data && 'message' in data) {
22
22
  return data;
23
23
  }
24
24
 
@@ -43,7 +43,7 @@ export class TransformInterceptor<T> implements NestInterceptor<T, ApiResponseDt
43
43
  }
44
44
 
45
45
  return {
46
- success: true,
46
+ statusCode: response.statusCode || HttpStatus.OK,
47
47
  message,
48
48
  data,
49
49
  timestamp: new Date().toISOString(),
@@ -8,7 +8,6 @@ export class PrismaService extends PrismaClient implements OnModuleInit, OnModul
8
8
  constructor() {
9
9
  super({
10
10
  log: [
11
- { level: 'query', emit: 'event' },
12
11
  { level: 'error', emit: 'stdout' },
13
12
  { level: 'warn', emit: 'stdout' },
14
13
  ],
@@ -48,13 +48,6 @@ export class AuthService {
48
48
  throw new UnauthorizedException('Account is inactive');
49
49
  }
50
50
 
51
- console.log({
52
- passwordInput: loginDto.password,
53
- passwordHash: user.password,
54
- isActive: user.is_active,
55
- deletedAt: user.deleted_at,
56
- });
57
-
58
51
  // Verify password
59
52
  const isPasswordValid = await PasswordUtil.compare(password, user.password);
60
53
  if (!isPasswordValid) {
@@ -6,9 +6,13 @@ import { RegisterDto } from '../../core/dto/register.dto';
6
6
  import { AuthResponseDto } from '../../core/dto/auth-response.dto';
7
7
  import { Public } from '@common/decorators/public.decorator';
8
8
  import { ApiSuccessResponse } from '@common/decorators/api-response.decorator';
9
+ import { JwtAuthGuard } from '@common/guards/jwt-auth.guard';
10
+ import { PermissionsGuard } from '@common/guards/permissions.guard';
9
11
 
10
12
  @ApiTags('Authentication')
13
+ @ApiBearerAuth('JWT-auth')
11
14
  @Controller({ path: 'auth', version: '1' })
15
+ @UseGuards(JwtAuthGuard, PermissionsGuard)
12
16
  export class AuthController {
13
17
  constructor(private readonly authService: AuthService) {}
14
18
 
@@ -24,13 +24,13 @@ import { Permissions } from '@common/decorators/permissions.decorator';
24
24
  import { PERMISSIONS } from '@common/constants/permissions.constant';
25
25
  import { ApiSuccessResponse, ApiSuccessArrayResponse } from '@common/decorators/api-response.decorator';
26
26
  import { PaginatedResponseDto } from '@common/dto/pagination.dto';
27
- import { AuthGuard } from '@nestjs/passport';
28
27
  import { PermissionsGuard } from '@common/guards/permissions.guard';
28
+ import { JwtAuthGuard } from '@common/guards/jwt-auth.guard';
29
29
 
30
30
  @ApiTags('Users')
31
31
  @ApiBearerAuth('JWT-auth')
32
32
  @Controller({ path: 'users', version: '1' })
33
- @UseGuards(AuthGuard('jwt'), PermissionsGuard)
33
+ @UseGuards(JwtAuthGuard, PermissionsGuard)
34
34
  export class UsersController {
35
35
  constructor(private readonly usersService: UsersService) {}
36
36
 
@@ -114,6 +114,10 @@ export class UsersService {
114
114
  this.prisma.user.count({ where }),
115
115
  ]);
116
116
 
117
+ if (total === 0) {
118
+ throw new NotFoundException('No users found');
119
+ }
120
+
117
121
  return {
118
122
  data: UserTransformHelper.toEntities(users),
119
123
  meta: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@urbansolv/create-nestjs-app",
3
- "version": "1.2.4",
3
+ "version": "1.2.7",
4
4
  "description": "CLI generator for Urbansolv NestJS boilerplate with RBAC and Prisma",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
@@ -39,6 +39,7 @@
39
39
  "homepage": "https://github.com/godwimp/create-nestjs-app#readme",
40
40
  "files": [
41
41
  "dist",
42
+ "templates",
42
43
  "README.md",
43
44
  "LICENSE"
44
45
  ],
@@ -0,0 +1,12 @@
1
+ root = true
2
+
3
+ [*]
4
+ charset = utf-8
5
+ indent_style = space
6
+ indent_size = 2
7
+ end_of_line = lf
8
+ insert_final_newline = true
9
+ trim_trailing_whitespace = true
10
+
11
+ [*.md]
12
+ trim_trailing_whitespace = false
@@ -0,0 +1,24 @@
1
+ # Application
2
+ NODE_ENV=development
3
+ PORT=3000
4
+ APP_NAME=__PROJECT_NAME__
5
+
6
+ # Database
7
+ DATABASE_URL="postgresql://username:password@localhost:5432/__DATABASE_NAME__?schema=public"
8
+
9
+ # JWT
10
+ JWT_SECRET=your-super-secret-jwt-key-change-this-in-production
11
+ JWT_EXPIRATION=7d
12
+ JWT_REFRESH_SECRET=your-super-secret-refresh-key-change-this-in-production
13
+ JWT_REFRESH_EXPIRATION=30d
14
+
15
+ # CORS
16
+ CORS_ORIGIN=http://localhost:3001
17
+
18
+ # API
19
+ API_PREFIX=api
20
+ API_VERSION=v1
21
+
22
+ # Swagger
23
+ SWAGGER_ENABLED=true
24
+ SWAGGER_PATH=docs
@@ -0,0 +1,25 @@
1
+ module.exports = {
2
+ parser: '@typescript-eslint/parser',
3
+ parserOptions: {
4
+ project: 'tsconfig.json',
5
+ tsconfigRootDir: __dirname,
6
+ sourceType: 'module',
7
+ },
8
+ plugins: ['@typescript-eslint/eslint-plugin'],
9
+ extends: [
10
+ 'plugin:@typescript-eslint/recommended',
11
+ 'plugin:prettier/recommended',
12
+ ],
13
+ root: true,
14
+ env: {
15
+ node: true,
16
+ jest: true,
17
+ },
18
+ ignorePatterns: ['.eslintrc.js'],
19
+ rules: {
20
+ '@typescript-eslint/interface-name-prefix': 'off',
21
+ '@typescript-eslint/explicit-function-return-type': 'off',
22
+ '@typescript-eslint/explicit-module-boundary-types': 'off',
23
+ '@typescript-eslint/no-explicit-any': 'off',
24
+ },
25
+ };
@@ -0,0 +1,8 @@
1
+ {
2
+ "singleQuote": true,
3
+ "trailingComma": "all",
4
+ "tabWidth": 2,
5
+ "semi": true,
6
+ "printWidth": 100,
7
+ "arrowParens": "always"
8
+ }
@@ -0,0 +1,133 @@
1
+ # Urbansolv NestJS Backend
2
+
3
+ Backend application built with NestJS following Urbansolv standardization manual.
4
+
5
+ ## Features
6
+
7
+ - šŸ” JWT Authentication with RBAC
8
+ - šŸ“ Complete User Management
9
+ - šŸŽÆ Permission-based Access Control
10
+ - šŸ“š Auto-generated Swagger Documentation
11
+ - āœ… Input Validation
12
+ - šŸ”„ API Versioning
13
+ - šŸ“Š Structured Logging
14
+ - šŸ—ƒļø Prisma ORM with PostgreSQL
15
+ - 🧪 Testing Setup (Unit & E2E)
16
+
17
+ ## Prerequisites
18
+
19
+ - Node.js >= 18.x
20
+ - PostgreSQL >= 14.x
21
+ - npm/yarn/pnpm
22
+
23
+ ## Installation
24
+ ```bash
25
+ # Install dependencies
26
+ npm install
27
+
28
+ # Setup environment variables
29
+ cp .env.example .env
30
+ # Edit .env with your database credentials
31
+
32
+ # Generate Prisma Client
33
+ npm run prisma:generate
34
+
35
+ # Run migrations
36
+ npm run prisma:migrate
37
+
38
+ # Seed database
39
+ npm run prisma:seed
40
+ ```
41
+
42
+ ## Running the Application
43
+ ```bash
44
+ # Development
45
+ npm run start:dev
46
+
47
+ # Production build
48
+ npm run build
49
+ npm run start:prod
50
+
51
+ # Debug mode
52
+ npm run start:debug
53
+ ```
54
+
55
+ ## Database Commands
56
+ ```bash
57
+ # Generate Prisma Client
58
+ npm run prisma:generate
59
+
60
+ # Create migration
61
+ npm run prisma:migrate
62
+
63
+ # Seed database
64
+ npm run prisma:seed
65
+
66
+ # Open Prisma Studio
67
+ npm run prisma:studio
68
+ ```
69
+
70
+ ## Testing
71
+ ```bash
72
+ # Unit tests
73
+ npm run test
74
+
75
+ # E2E tests
76
+ npm run test:e2e
77
+
78
+ # Test coverage
79
+ npm run test:cov
80
+ ```
81
+
82
+ ## API Documentation
83
+
84
+ After starting the application, visit:
85
+ - Swagger UI: `http://localhost:3000/api-docs`
86
+
87
+ ## Default Users
88
+
89
+ After seeding the database:
90
+
91
+ **Admin User:**
92
+ - Email: `admin@urbansolv.com`
93
+ - Password: `password123`
94
+ - All permissions granted
95
+
96
+ **Member User:**
97
+ - Email: `member@urbansolv.com`
98
+ - Password: `password123`
99
+ - Limited permissions (VIEW_USER only)
100
+
101
+ ## Project Structure
102
+ ```
103
+ src/
104
+ ā”œā”€ā”€ common/ # Shared resources
105
+ │ ā”œā”€ā”€ decorators/ # Custom decorators
106
+ │ ā”œā”€ā”€ filters/ # Exception filters
107
+ │ ā”œā”€ā”€ guards/ # Auth & permission guards
108
+ │ ā”œā”€ā”€ interceptors/ # Logging & transform
109
+ │ ā”œā”€ā”€ prisma/ # Prisma service
110
+ │ └── utils/ # Helper functions
111
+ ā”œā”€ā”€ config/ # Configuration files
112
+ ā”œā”€ā”€ modules/ # Feature modules
113
+ │ ā”œā”€ā”€ auth/ # Authentication
114
+ │ ā”œā”€ā”€ users/ # User management
115
+ │ └── health/ # Health checks
116
+ ā”œā”€ā”€ app.module.ts # Root module
117
+ └── main.ts # Application entry point
118
+ ```
119
+
120
+ ## Environment Variables
121
+
122
+ See `.env.example` for all available configuration options.
123
+
124
+ ## License
125
+
126
+ Proprietary - Urbansolv
127
+ ```
128
+
129
+ ### 20.2 .gitkeep for migrations
130
+
131
+ **templates/nestjs-app/prisma/migrations/.gitkeep**
132
+ ```
133
+ # This file keeps the migrations directory in git
@@ -0,0 +1,10 @@
1
+ {
2
+ "$schema": "https://json.schemastore.org/nest-cli",
3
+ "collection": "@nestjs/schematics",
4
+ "sourceRoot": "src",
5
+ "compilerOptions": {
6
+ "deleteOutDir": true,
7
+ "webpack": true,
8
+ "tsConfigPath": "tsconfig.json"
9
+ }
10
+ }
@@ -0,0 +1,88 @@
1
+ {
2
+ "name": "my-app",
3
+ "version": "1.0.0",
4
+ "description": "Urbansolv NestJS Application",
5
+ "author": "Urbansolv",
6
+ "private": true,
7
+ "license": "UNLICENSED",
8
+ "scripts": {
9
+ "build": "npm run clean && tsc && npm run copy-templates",
10
+ "clean": "rm -rf dist",
11
+ "copy-templates": "cp -r templates dist/",
12
+ "dev": "ts-node src/cli.ts",
13
+ "prepublishOnly": "npm run build",
14
+ "format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
15
+ "start": "nest start",
16
+ "start:dev": "nest start --watch",
17
+ "start:debug": "nest start --debug --watch",
18
+ "start:prod": "node dist/main",
19
+ "lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
20
+ "test": "echo \"No tests yet\" && exit 0",
21
+ "test:watch": "jest --watch",
22
+ "test:cov": "jest --coverage",
23
+ "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
24
+ "test:e2e": "jest --config ./test/jest-e2e.json",
25
+ "prisma:generate": "prisma generate",
26
+ "prisma:migrate": "prisma migrate dev",
27
+ "prisma:studio": "prisma studio",
28
+ "prisma:seed": "ts-node prisma/seed.ts"
29
+ },
30
+ "dependencies": {
31
+ "@nestjs/common": "^11.0.1",
32
+ "@nestjs/config": "^4.0.2",
33
+ "@nestjs/core": "^11.0.1",
34
+ "@nestjs/jwt": "^11.0.2",
35
+ "@nestjs/passport": "^11.0.5",
36
+ "@nestjs/platform-express": "^11.0.1",
37
+ "@nestjs/swagger": "^11.2.3",
38
+ "@prisma/client": "^6.19.0",
39
+ "bcryptjs": "^3.0.3",
40
+ "class-transformer": "^0.5.1",
41
+ "class-validator": "^0.14.0",
42
+ "joi": "^18.0.2",
43
+ "passport": "^0.7.0",
44
+ "passport-jwt": "^4.0.1",
45
+ "reflect-metadata": "^0.2.2",
46
+ "rxjs": "^7.8.1"
47
+ },
48
+ "devDependencies": {
49
+ "@nestjs/cli": "^11.0.0",
50
+ "@nestjs/schematics": "^11.0.0",
51
+ "@nestjs/testing": "^11.0.1",
52
+ "@types/bcrypt": "^6.0.0",
53
+ "@types/express": "^5.0.5",
54
+ "@types/jest": "^30.0.0",
55
+ "@types/node": "^22.10.7",
56
+ "@types/passport-jwt": "^4.0.1",
57
+ "@types/supertest": "^6.0.2",
58
+ "@typescript-eslint/eslint-plugin": "^8.54.0",
59
+ "@typescript-eslint/parser": "^8.54.0",
60
+ "eslint": "^9.18.0",
61
+ "eslint-config-prettier": "^10.0.1",
62
+ "eslint-plugin-prettier": "^5.2.2",
63
+ "jest": "^30.0.0",
64
+ "prettier": "^3.4.2",
65
+ "prisma": "^6.19.0",
66
+ "source-map-support": "^0.5.21",
67
+ "supertest": "^7.0.0",
68
+ "ts-jest": "^29.2.5",
69
+ "ts-loader": "^9.5.2",
70
+ "ts-node": "^10.9.2",
71
+ "tsconfig-paths": "^4.2.0",
72
+ "typescript": "^5.7.3"
73
+ },
74
+ "prisma": {
75
+ "seed": "ts-node prisma/seed.ts"
76
+ },
77
+ "jest": {
78
+ "moduleFileExtensions": ["js", "json", "ts"],
79
+ "rootDir": "src",
80
+ "testRegex": ".*\\.spec\\.ts$",
81
+ "transform": {
82
+ "^.+\\.(t|j)s$": "ts-jest"
83
+ },
84
+ "collectCoverageFrom": ["**/*.(t|j)s"],
85
+ "coverageDirectory": "../coverage",
86
+ "testEnvironment": "node"
87
+ }
88
+ }
@@ -0,0 +1,79 @@
1
+ // This is your Prisma schema file,
2
+ // learn more about it in the docs: https://pris.ly/d/prisma-schema
3
+
4
+ generator client {
5
+ provider = "prisma-client-js"
6
+ }
7
+
8
+ datasource db {
9
+ provider = "postgresql"
10
+ url = env("DATABASE_URL")
11
+ }
12
+
13
+ // ============================================
14
+ // RBAC Schema
15
+ // ============================================
16
+
17
+ model User {
18
+ id Int @id @default(autoincrement())
19
+ email String @unique
20
+ password String
21
+ first_name String
22
+ last_name String
23
+ is_active Boolean @default(true)
24
+ created_at DateTime @default(now())
25
+ updated_at DateTime @updatedAt
26
+ deleted_at DateTime?
27
+
28
+ // Relations
29
+ position_id Int
30
+ position Position @relation(fields: [position_id], references: [id])
31
+
32
+ @@map("users")
33
+ }
34
+
35
+ model Position {
36
+ id Int @id @default(autoincrement())
37
+ name String @unique
38
+ description String?
39
+ is_active Boolean @default(true)
40
+ created_at DateTime @default(now())
41
+ updated_at DateTime @updatedAt
42
+ deleted_at DateTime?
43
+
44
+ // Relations
45
+ users User[]
46
+ position_permissions PositionPermission[]
47
+
48
+ @@map("positions")
49
+ }
50
+
51
+ model Permission {
52
+ id Int @id @default(autoincrement())
53
+ name String @unique
54
+ description String?
55
+ resource String // e.g., 'USER', 'TRANSACTION', 'REPORT'
56
+ action String // e.g., 'VIEW', 'ADD', 'UPDATE', 'DELETE'
57
+ created_at DateTime @default(now())
58
+ updated_at DateTime @updatedAt
59
+ deleted_at DateTime?
60
+
61
+ // Relations
62
+ position_permissions PositionPermission[]
63
+
64
+ @@map("permissions")
65
+ }
66
+
67
+ model PositionPermission {
68
+ id Int @id @default(autoincrement())
69
+ position_id Int
70
+ permission_id Int
71
+ created_at DateTime @default(now())
72
+
73
+ // Relations
74
+ position Position @relation(fields: [position_id], references: [id], onDelete: Cascade)
75
+ permission Permission @relation(fields: [permission_id], references: [id], onDelete: Cascade)
76
+
77
+ @@unique([position_id, permission_id])
78
+ @@map("position_permissions")
79
+ }
@@ -0,0 +1,153 @@
1
+ import { PrismaClient } from '@prisma/client';
2
+ import * as bcrypt from 'bcryptjs';
3
+
4
+ const prisma = new PrismaClient();
5
+
6
+ async function main() {
7
+ console.log('🌱 Starting database seeding...\n');
8
+
9
+ // Clear existing data
10
+ console.log('šŸ—‘ļø Clearing existing data...');
11
+ await prisma.positionPermission.deleteMany();
12
+ await prisma.user.deleteMany();
13
+ await prisma.permission.deleteMany();
14
+ await prisma.position.deleteMany();
15
+
16
+ // ============================================
17
+ // Seed Positions
18
+ // ============================================
19
+ console.log('šŸ“‹ Seeding positions...');
20
+ const adminPosition = await prisma.position.create({
21
+ data: {
22
+ name: 'Administrator',
23
+ description: 'Full system access with all permissions',
24
+ },
25
+ });
26
+
27
+ const memberPosition = await prisma.position.create({
28
+ data: {
29
+ name: 'Member',
30
+ description: 'Standard user with limited permissions',
31
+ },
32
+ });
33
+
34
+ console.log('āœ… Positions seeded');
35
+
36
+ // ============================================
37
+ // Seed Permissions
38
+ // ============================================
39
+ console.log('šŸ” Seeding permissions...');
40
+
41
+ const permissionsData = [
42
+ // User Management
43
+ { name: 'VIEW_USER', resource: 'USER', action: 'VIEW', description: 'View user information' },
44
+ { name: 'ADD_USER', resource: 'USER', action: 'ADD', description: 'Create new user' },
45
+ { name: 'UPDATE_USER', resource: 'USER', action: 'UPDATE', description: 'Update user information' },
46
+ { name: 'DELETE_USER', resource: 'USER', action: 'DELETE', description: 'Delete user' },
47
+ { name: 'MANAGE_USER_PERMISSION', resource: 'USER', action: 'MANAGE_PERMISSION', description: 'Assign or revoke user permissions' },
48
+ { name: 'CHANGE_USER_POSITION', resource: 'USER', action: 'CHANGE_POSITION', description: 'Change user position/role' },
49
+
50
+ // Position Management
51
+ { name: 'VIEW_POSITION', resource: 'POSITION', action: 'VIEW', description: 'View position information' },
52
+ { name: 'ADD_POSITION', resource: 'POSITION', action: 'ADD', description: 'Create new position' },
53
+ { name: 'UPDATE_POSITION', resource: 'POSITION', action: 'UPDATE', description: 'Update position information' },
54
+ { name: 'DELETE_POSITION', resource: 'POSITION', action: 'DELETE', description: 'Delete position' },
55
+
56
+ // Permission Management
57
+ { name: 'VIEW_PERMISSION', resource: 'PERMISSION', action: 'VIEW', description: 'View permission information' },
58
+ { name: 'ADD_PERMISSION', resource: 'PERMISSION', action: 'ADD', description: 'Create new permission' },
59
+ { name: 'UPDATE_PERMISSION', resource: 'PERMISSION', action: 'UPDATE', description: 'Update permission information' },
60
+ { name: 'DELETE_PERMISSION', resource: 'PERMISSION', action: 'DELETE', description: 'Delete permission' },
61
+ ];
62
+
63
+ const permissions = await Promise.all(
64
+ permissionsData.map((permission) =>
65
+ prisma.permission.create({ data: permission })
66
+ )
67
+ );
68
+
69
+ console.log(`āœ… ${permissions.length} permissions seeded`);
70
+
71
+ // ============================================
72
+ // Assign Permissions to Positions
73
+ // ============================================
74
+ console.log('šŸ”— Assigning permissions to positions...');
75
+
76
+ // Admin gets all permissions
77
+ const adminPermissions = permissions.map((permission) => ({
78
+ position_id: adminPosition.id,
79
+ permission_id: permission.id,
80
+ }));
81
+
82
+ await prisma.positionPermission.createMany({
83
+ data: adminPermissions,
84
+ });
85
+
86
+ // Member gets only VIEW_USER permission
87
+ const viewUserPermission = permissions.find((p) => p.name === 'VIEW_USER');
88
+ if (viewUserPermission) {
89
+ await prisma.positionPermission.create({
90
+ data: {
91
+ position_id: memberPosition.id,
92
+ permission_id: viewUserPermission.id,
93
+ },
94
+ });
95
+ }
96
+
97
+ console.log('āœ… Permissions assigned to positions');
98
+
99
+ // ============================================
100
+ // Seed Users
101
+ // ============================================
102
+ console.log('šŸ‘„ Seeding users...');
103
+
104
+ const defaultPassword = 'password123';
105
+ const hashedPassword = await bcrypt.hash(defaultPassword, 10);
106
+
107
+ const adminUser = await prisma.user.create({
108
+ data: {
109
+ email: 'admin@urbansolv.co.id',
110
+ password: hashedPassword,
111
+ first_name: 'Admin',
112
+ last_name: 'Urbansolv',
113
+ position_id: adminPosition.id,
114
+ is_active: true,
115
+ },
116
+ });
117
+
118
+ const memberUser = await prisma.user.create({
119
+ data: {
120
+ email: 'member@urbansolv.co.id',
121
+ password: hashedPassword,
122
+ first_name: 'Member',
123
+ last_name: 'User',
124
+ position_id: memberPosition.id,
125
+ is_active: true,
126
+ },
127
+ });
128
+
129
+ console.log('āœ… Users seeded');
130
+
131
+ // ============================================
132
+ // Summary
133
+ // ============================================
134
+ console.log('\n✨ Database seeding completed!\n');
135
+ console.log('šŸ“Š Summary:');
136
+ console.log(` - Positions: ${await prisma.position.count()}`);
137
+ console.log(` - Permissions: ${await prisma.permission.count()}`);
138
+ console.log(` - Users: ${await prisma.user.count()}`);
139
+ console.log(` - Position-Permission Links: ${await prisma.positionPermission.count()}\n`);
140
+
141
+ console.log('šŸ”‘ Default Users:');
142
+ console.log(` Admin: ${adminUser.email} / ${defaultPassword}`);
143
+ console.log(` Member: ${memberUser.email} / ${defaultPassword}\n`);
144
+ }
145
+
146
+ main()
147
+ .catch((e) => {
148
+ console.error('āŒ Error seeding database:', e);
149
+ process.exit(1);
150
+ })
151
+ .finally(async () => {
152
+ await prisma.$disconnect();
153
+ });