@ftisindia/create-app 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (151) hide show
  1. package/LICENSE +144 -0
  2. package/bin/index.mjs +2 -0
  3. package/package.json +36 -0
  4. package/src/copy.mjs +45 -0
  5. package/src/log.mjs +23 -0
  6. package/src/main.mjs +221 -0
  7. package/src/run.mjs +52 -0
  8. package/src/substitute.mjs +40 -0
  9. package/template/.env.example +36 -0
  10. package/template/.github/workflows/ci.yml +36 -0
  11. package/template/.husky/pre-commit +1 -0
  12. package/template/README.md +146 -0
  13. package/template/_editorconfig +8 -0
  14. package/template/_gitignore +7 -0
  15. package/template/_nvmrc +1 -0
  16. package/template/_package.json +107 -0
  17. package/template/_prettierignore +5 -0
  18. package/template/_prettierrc +6 -0
  19. package/template/docs/API_REFERENCE.md +123 -0
  20. package/template/docs/GETTING_STARTED.md +65 -0
  21. package/template/docs/MODULE_COMPLETION_CHECKLIST.md +40 -0
  22. package/template/docs/OAUTH.md +46 -0
  23. package/template/docs/SAMPLE_MODULE.md +23 -0
  24. package/template/docs/api.http +269 -0
  25. package/template/eslint.config.mjs +51 -0
  26. package/template/nest-cli.json +8 -0
  27. package/template/prisma/migrations/20260530000000_init/migration.sql +248 -0
  28. package/template/prisma/schema.prisma +299 -0
  29. package/template/prisma/seed.ts +44 -0
  30. package/template/scripts/db-create.mjs +126 -0
  31. package/template/scripts/gen-module.mjs +217 -0
  32. package/template/scripts/seed-test-user-org.ts +264 -0
  33. package/template/scripts/setup-local.mjs +224 -0
  34. package/template/scripts/test-db.mjs +69 -0
  35. package/template/src/app.module.ts +58 -0
  36. package/template/src/common/decorators/.gitkeep +1 -0
  37. package/template/src/common/dto/error-response.dto.ts +17 -0
  38. package/template/src/common/dto/membership-response.dto.ts +51 -0
  39. package/template/src/common/dto/mutation-response.dto.ts +11 -0
  40. package/template/src/common/dto/role-summary.dto.ts +18 -0
  41. package/template/src/common/dto/user-summary.dto.ts +23 -0
  42. package/template/src/common/enums/.gitkeep +1 -0
  43. package/template/src/common/filters/.gitkeep +1 -0
  44. package/template/src/common/filters/http-exception.filter.ts +78 -0
  45. package/template/src/common/guards/.gitkeep +1 -0
  46. package/template/src/common/interceptors/.gitkeep +1 -0
  47. package/template/src/common/pipes/.gitkeep +1 -0
  48. package/template/src/common/swagger/api-error-responses.ts +54 -0
  49. package/template/src/common/types/.gitkeep +1 -0
  50. package/template/src/config/app.config.ts +7 -0
  51. package/template/src/config/auth.config.ts +33 -0
  52. package/template/src/config/database.config.ts +6 -0
  53. package/template/src/config/env.validation.ts +131 -0
  54. package/template/src/config/index.ts +5 -0
  55. package/template/src/config/rbac.config.ts +6 -0
  56. package/template/src/database/prisma/prisma-transaction.ts +22 -0
  57. package/template/src/database/prisma/prisma.module.ts +9 -0
  58. package/template/src/database/prisma/prisma.service.ts +16 -0
  59. package/template/src/main.ts +42 -0
  60. package/template/src/modules/access-control/access-control.module.ts +24 -0
  61. package/template/src/modules/access-control/application/route-registry.validator.ts +289 -0
  62. package/template/src/modules/access-control/application/services/ability.factory.ts +28 -0
  63. package/template/src/modules/access-control/application/services/access-control.service.ts +478 -0
  64. package/template/src/modules/access-control/application/services/permission.guard.ts +77 -0
  65. package/template/src/modules/access-control/application/services/rbac-cache.service.ts +148 -0
  66. package/template/src/modules/access-control/dto/access-control-response.dto.ts +79 -0
  67. package/template/src/modules/access-control/dto/create-role.dto.ts +18 -0
  68. package/template/src/modules/access-control/dto/update-role-permissions.dto.ts +23 -0
  69. package/template/src/modules/access-control/dto/update-role.dto.ts +19 -0
  70. package/template/src/modules/access-control/presentation/access-control.controller.ts +157 -0
  71. package/template/src/modules/access-control/presentation/permissions.decorator.ts +8 -0
  72. package/template/src/modules/access-control/presentation/public.decorator.ts +7 -0
  73. package/template/src/modules/access-control/types/permission-key.ts +37 -0
  74. package/template/src/modules/access-control/types/rbac-context.ts +11 -0
  75. package/template/src/modules/access-control/types/route-permission-registry.ts +129 -0
  76. package/template/src/modules/audit/application/services/audit.service.ts +97 -0
  77. package/template/src/modules/audit/audit.module.ts +13 -0
  78. package/template/src/modules/audit/dto/audit-response.dto.ts +75 -0
  79. package/template/src/modules/audit/dto/list-audit-logs-query.dto.ts +75 -0
  80. package/template/src/modules/audit/presentation/audit.controller.ts +37 -0
  81. package/template/src/modules/auth/application/services/auth.service.ts +509 -0
  82. package/template/src/modules/auth/application/services/password.service.ts +15 -0
  83. package/template/src/modules/auth/application/services/token.service.ts +95 -0
  84. package/template/src/modules/auth/auth.module.ts +73 -0
  85. package/template/src/modules/auth/dto/auth-response.dto.ts +29 -0
  86. package/template/src/modules/auth/dto/login.dto.ts +15 -0
  87. package/template/src/modules/auth/dto/logout.dto.ts +3 -0
  88. package/template/src/modules/auth/dto/oauth-exchange.dto.ts +15 -0
  89. package/template/src/modules/auth/dto/refresh-token.dto.ts +14 -0
  90. package/template/src/modules/auth/dto/signup.dto.ts +27 -0
  91. package/template/src/modules/auth/infrastructure/passport/google-auth.guard.ts +27 -0
  92. package/template/src/modules/auth/infrastructure/passport/google.strategy.ts +56 -0
  93. package/template/src/modules/auth/infrastructure/passport/jwt-auth.guard.ts +5 -0
  94. package/template/src/modules/auth/infrastructure/passport/jwt.strategy.ts +43 -0
  95. package/template/src/modules/auth/presentation/auth.controller.ts +148 -0
  96. package/template/src/modules/auth/presentation/current-user.decorator.ts +11 -0
  97. package/template/src/modules/auth/presentation/google-oauth-exception.filter.ts +33 -0
  98. package/template/src/modules/auth/types/authenticated-user.ts +7 -0
  99. package/template/src/modules/auth/types/google-auth-profile.ts +6 -0
  100. package/template/src/modules/auth/types/jwt-payload.ts +5 -0
  101. package/template/src/modules/health/dto/health-response.dto.ts +9 -0
  102. package/template/src/modules/health/health.module.ts +7 -0
  103. package/template/src/modules/health/presentation/health.controller.ts +33 -0
  104. package/template/src/modules/invitations/application/services/invitations.service.ts +967 -0
  105. package/template/src/modules/invitations/dto/accept-invitation.dto.ts +24 -0
  106. package/template/src/modules/invitations/dto/create-invitation.dto.ts +100 -0
  107. package/template/src/modules/invitations/dto/invitation-response.dto.ts +108 -0
  108. package/template/src/modules/invitations/dto/invitation-token.dto.ts +15 -0
  109. package/template/src/modules/invitations/invitations.module.ts +12 -0
  110. package/template/src/modules/invitations/presentation/invitations.controller.ts +149 -0
  111. package/template/src/modules/memberships/application/services/memberships.service.ts +455 -0
  112. package/template/src/modules/memberships/dto/transfer-owner.dto.ts +11 -0
  113. package/template/src/modules/memberships/dto/update-billing-contact.dto.ts +8 -0
  114. package/template/src/modules/memberships/dto/update-membership-owner.dto.ts +8 -0
  115. package/template/src/modules/memberships/dto/update-membership-role.dto.ts +11 -0
  116. package/template/src/modules/memberships/dto/update-membership-status.dto.ts +9 -0
  117. package/template/src/modules/memberships/memberships.module.ts +12 -0
  118. package/template/src/modules/memberships/presentation/memberships.controller.ts +193 -0
  119. package/template/src/modules/organisations/application/services/organisations.service.ts +147 -0
  120. package/template/src/modules/organisations/dto/create-organisation.dto.ts +32 -0
  121. package/template/src/modules/organisations/dto/organisation-response.dto.ts +62 -0
  122. package/template/src/modules/organisations/infrastructure/repositories/organisations.repository.ts +24 -0
  123. package/template/src/modules/organisations/organisations.module.ts +12 -0
  124. package/template/src/modules/organisations/presentation/organisations.controller.ts +37 -0
  125. package/template/src/modules/organisations/types/default-organisation-data.ts +18 -0
  126. package/template/src/modules/platform-admin/.gitkeep +1 -0
  127. package/template/src/modules/request-context/application/services/request-context.service.ts +79 -0
  128. package/template/src/modules/request-context/presentation/org-scope.guard.ts +26 -0
  129. package/template/src/modules/request-context/presentation/request-context.interceptor.ts +26 -0
  130. package/template/src/modules/request-context/presentation/request-context.middleware.ts +31 -0
  131. package/template/src/modules/request-context/request-context.module.ts +25 -0
  132. package/template/src/modules/request-context/types/request-context.ts +29 -0
  133. package/template/src/modules/sample/application/services/sample.service.ts +67 -0
  134. package/template/src/modules/sample/dto/sample-echo.dto.ts +10 -0
  135. package/template/src/modules/sample/dto/sample-response.dto.ts +41 -0
  136. package/template/src/modules/sample/presentation/sample.controller.ts +63 -0
  137. package/template/src/modules/sample/sample.module.ts +11 -0
  138. package/template/src/modules/settings/application/services/settings.service.ts +139 -0
  139. package/template/src/modules/settings/dto/setting-response.dto.ts +27 -0
  140. package/template/src/modules/settings/dto/update-setting.dto.ts +16 -0
  141. package/template/src/modules/settings/presentation/settings.controller.ts +66 -0
  142. package/template/src/modules/settings/settings.module.ts +12 -0
  143. package/template/src/modules/settings/types/setting-definitions.ts +104 -0
  144. package/template/src/modules/users/.gitkeep +1 -0
  145. package/template/test/.gitkeep +1 -0
  146. package/template/test/jest-e2e.json +9 -0
  147. package/template/test/permission.guard.spec.ts +22 -0
  148. package/template/test/route-registry.validator.spec.ts +90 -0
  149. package/template/test/security.e2e-spec.ts +102 -0
  150. package/template/tsconfig.build.json +4 -0
  151. package/template/tsconfig.json +18 -0
@@ -0,0 +1,146 @@
1
+ # __PROJECT_NAME__
2
+
3
+ NestJS modular-monolith starter with email/password auth, Google OAuth handoff, organisations, memberships, invitations, per-organisation RBAC with CASL, request context, audit logs, typed settings, Swagger, and a complete sample module.
4
+
5
+ ## Quickstart
6
+
7
+ Install dependencies if the scaffolder did not already do it:
8
+
9
+ ```bash
10
+ npm install
11
+ ```
12
+
13
+ Run the guided local setup:
14
+
15
+ ```bash
16
+ npm run setup:local
17
+ ```
18
+
19
+ Make sure PostgreSQL is running before setup starts. If your local PostgreSQL user,
20
+ password, host, or port differs from the default, update `.env` when the setup prompt
21
+ opens it for review.
22
+
23
+ The setup command checks and creates `.env` values, creates the `app` and `app_test` PostgreSQL databases if they are missing, generates Prisma, deploys migrations, seeds permissions, and prints demo login credentials. It is non-destructive: it does not drop, reset, truncate, or overwrite existing data.
24
+
25
+ Start the API:
26
+
27
+ ```bash
28
+ npm run start:dev
29
+ ```
30
+
31
+ Open:
32
+
33
+ ```txt
34
+ http://localhost:3000/health
35
+ http://localhost:3000/docs
36
+ ```
37
+
38
+ Use the printed demo email/password at `POST /auth/login`, then paste the returned access token into Swagger's bearer authorization.
39
+
40
+ For a fresher-friendly API walkthrough, response-shape notes, and Swagger usage
41
+ rules, read `docs/API_REFERENCE.md`.
42
+
43
+ ## PostgreSQL
44
+
45
+ The default `.env` expects a local PostgreSQL server reachable at:
46
+
47
+ ```txt
48
+ postgresql://postgres:postgres@localhost:5432/app
49
+ ```
50
+
51
+ Any reachable PostgreSQL works. Update `DATABASE_URL` and `TEST_DATABASE_URL` in `.env` if your local user, password, host, port, or database names differ. `npm run db:create` creates missing databases only; it never deletes existing databases.
52
+
53
+ ## Daily Commands
54
+
55
+ ```bash
56
+ npm run setup:local # guided, idempotent local bootstrap
57
+ npm run start:dev # start in watch mode
58
+ npm run lint # ESLint
59
+ npm run format # Prettier write
60
+ npm run build # Nest build
61
+ npm test # unit tests
62
+ npm run test:e2e # e2e tests against TEST_DATABASE_URL
63
+ ```
64
+
65
+ `npm run db:reset` is the only destructive database command and is intentionally separate.
66
+
67
+ ## Core Endpoints
68
+
69
+ ```txt
70
+ GET /health
71
+ GET /docs
72
+ POST /auth/signup
73
+ POST /auth/login
74
+ POST /auth/refresh
75
+ POST /auth/oauth/exchange
76
+ POST /auth/logout
77
+ GET /auth/google
78
+ GET /auth/google/callback
79
+ GET /auth/me
80
+ POST /organisations
81
+ GET /organisations/:orgId/memberships/me
82
+ GET /organisations/:orgId/memberships
83
+ PATCH /organisations/:orgId/memberships/:membershipId/status
84
+ PATCH /organisations/:orgId/memberships/:membershipId/role
85
+ PATCH /organisations/:orgId/memberships/:membershipId/owner
86
+ PATCH /organisations/:orgId/memberships/:membershipId/billing-contact
87
+ POST /organisations/:orgId/memberships/transfer-owner
88
+ POST /organisations/:orgId/invitations
89
+ GET /organisations/:orgId/invitations
90
+ POST /organisations/:orgId/invitations/:invitationId/revoke
91
+ POST /organisations/:orgId/invitations/:invitationId/resend
92
+ POST /invitations/accept
93
+ POST /invitations/decline
94
+ GET /organisations/:orgId/access-control/permissions
95
+ GET /organisations/:orgId/access-control/route-permissions
96
+ GET /organisations/:orgId/access-control/roles
97
+ POST /organisations/:orgId/access-control/roles
98
+ PATCH /organisations/:orgId/access-control/roles/:roleId
99
+ DELETE /organisations/:orgId/access-control/roles/:roleId
100
+ GET /organisations/:orgId/access-control/roles/:roleId/permissions
101
+ PUT /organisations/:orgId/access-control/roles/:roleId/permissions
102
+ GET /organisations/:orgId/audit
103
+ GET /organisations/:orgId/settings
104
+ GET /organisations/:orgId/settings/:key
105
+ PATCH /organisations/:orgId/settings
106
+ GET /organisations/:orgId/sample/status
107
+ POST /organisations/:orgId/sample/echo
108
+ ```
109
+
110
+ ## Architecture Rules
111
+
112
+ - JWTs contain identity only. Organisation membership, roles, and permissions are read per request.
113
+ - Organisation-scoped routes use path scope: `/organisations/:orgId/...`.
114
+ - Protected organisation routes use `JwtAuthGuard`, `OrgScopeGuard`, `PermissionGuard`, and `@RequirePermissions(...)`.
115
+ - Every permission-gated route must be represented in `route-permission-registry.ts`; startup and tests fail on drift.
116
+ - Mutating business flows should write audit rows.
117
+ - Setup and CI commands are non-destructive. Use `npm run db:reset` only when you explicitly want to reset a local database.
118
+
119
+ ## Adding Features
120
+
121
+ Use the sample module as the copy-paste reference:
122
+
123
+ ```txt
124
+ src/modules/sample
125
+ docs/SAMPLE_MODULE.md
126
+ docs/MODULE_COMPLETION_CHECKLIST.md
127
+ ```
128
+
129
+ You can also scaffold a starter module:
130
+
131
+ ```bash
132
+ npm run gen:module -- billing-reports
133
+ ```
134
+
135
+ The generator creates a guarded org-scoped controller, DTO, service with request-context and audit patterns, permission keys, route registry entries, and a starter spec. It prints the `AppModule` import you still need to add.
136
+
137
+ ## REST Client
138
+
139
+ `docs/api.http` contains a REST Client flow for health, login, organisation setup, invitations, RBAC, settings, audit, and the sample endpoint.
140
+
141
+ ## API Documentation
142
+
143
+ Swagger is mounted at `/docs` and includes tags, request schemas, response DTOs,
144
+ common error responses, bearer authorization, route parameters, and examples for
145
+ all implemented API routes. See `docs/API_REFERENCE.md` before adding or changing
146
+ controllers.
@@ -0,0 +1,8 @@
1
+ root = true
2
+
3
+ [*]
4
+ charset = utf-8
5
+ indent_size = 2
6
+ indent_style = space
7
+ insert_final_newline = true
8
+ trim_trailing_whitespace = true
@@ -0,0 +1,7 @@
1
+ node_modules/
2
+ dist/
3
+ coverage/
4
+ *.log
5
+ .env
6
+ .env.*
7
+ !.env.example
@@ -0,0 +1 @@
1
+ 20
@@ -0,0 +1,107 @@
1
+ {
2
+ "name": "__PROJECT_NAME__",
3
+ "version": "0.1.0",
4
+ "private": true,
5
+ "license": "UNLICENSED",
6
+ "scripts": {
7
+ "build": "nest build",
8
+ "db:create": "node scripts/db-create.mjs",
9
+ "format": "prettier --write .",
10
+ "format:check": "prettier --check .",
11
+ "gen:module": "node scripts/gen-module.mjs",
12
+ "lint": "eslint .",
13
+ "prepare": "husky || true",
14
+ "start": "nest start",
15
+ "start:dev": "nest start --watch",
16
+ "start:prod": "node dist/main.js",
17
+ "prisma:generate": "prisma generate",
18
+ "prisma:migrate": "prisma migrate dev --name init",
19
+ "prisma:deploy": "prisma migrate deploy",
20
+ "prisma:seed": "prisma db seed",
21
+ "seed:test-user-org": "tsx scripts/seed-test-user-org.ts",
22
+ "setup": "node scripts/setup-local.mjs",
23
+ "setup:local": "node scripts/setup-local.mjs",
24
+ "setup:ci": "node scripts/setup-local.mjs --yes",
25
+ "test": "jest",
26
+ "test:cov": "jest --coverage",
27
+ "test:e2e": "node scripts/test-db.mjs && jest --config test/jest-e2e.json --runInBand",
28
+ "test:watch": "jest --watch",
29
+ "db:reset": "prisma migrate reset"
30
+ },
31
+ "dependencies": {
32
+ "@casl/ability": "6.8.1",
33
+ "@nestjs/common": "11.1.24",
34
+ "@nestjs/config": "4.0.4",
35
+ "@nestjs/core": "11.1.24",
36
+ "@nestjs/jwt": "11.0.2",
37
+ "@nestjs/passport": "11.0.5",
38
+ "@nestjs/platform-express": "11.1.24",
39
+ "@nestjs/swagger": "11.4.4",
40
+ "@nestjs/throttler": "6.5.0",
41
+ "@prisma/client": "5.22.0",
42
+ "bcryptjs": "3.0.3",
43
+ "class-transformer": "0.5.1",
44
+ "class-validator": "0.15.1",
45
+ "express-session": "1.19.0",
46
+ "passport": "0.7.0",
47
+ "passport-google-oauth20": "2.0.0",
48
+ "passport-jwt": "4.0.1",
49
+ "pg": "8.21.0",
50
+ "reflect-metadata": "0.2.2",
51
+ "rxjs": "7.8.2",
52
+ "zod": "4.4.3"
53
+ },
54
+ "devDependencies": {
55
+ "@eslint/js": "10.0.1",
56
+ "@nestjs/cli": "11.0.21",
57
+ "@nestjs/schematics": "11.1.0",
58
+ "@nestjs/testing": "11.1.24",
59
+ "@types/express-session": "1.19.0",
60
+ "@types/jest": "30.0.0",
61
+ "@types/node": "20.19.41",
62
+ "@types/passport": "1.0.17",
63
+ "@types/passport-google-oauth20": "2.0.17",
64
+ "@types/passport-jwt": "4.0.1",
65
+ "@types/pg": "8.20.0",
66
+ "@types/supertest": "7.2.0",
67
+ "eslint": "10.4.1",
68
+ "eslint-config-prettier": "10.1.8",
69
+ "globals": "17.6.0",
70
+ "husky": "9.1.7",
71
+ "jest": "30.4.2",
72
+ "lint-staged": "17.0.6",
73
+ "prettier": "3.8.3",
74
+ "prisma": "5.22.0",
75
+ "supertest": "7.2.2",
76
+ "ts-jest": "29.4.11",
77
+ "ts-node": "10.9.2",
78
+ "tsconfig-paths": "4.2.0",
79
+ "tsx": "4.22.3",
80
+ "typescript-eslint": "8.60.0",
81
+ "typescript": "5.9.3"
82
+ },
83
+ "lint-staged": {
84
+ "*.{js,mjs,ts,json,md,yml,yaml}": "prettier --write",
85
+ "*.{js,mjs,ts}": "eslint --fix"
86
+ },
87
+ "prisma": {
88
+ "seed": "tsx prisma/seed.ts"
89
+ },
90
+ "jest": {
91
+ "moduleFileExtensions": [
92
+ "js",
93
+ "json",
94
+ "ts"
95
+ ],
96
+ "rootDir": ".",
97
+ "testRegex": ".*\\.spec\\.ts$",
98
+ "transform": {
99
+ "^.+\\.(t|j)s$": "ts-jest"
100
+ },
101
+ "collectCoverageFrom": [
102
+ "src/**/*.(t|j)s"
103
+ ],
104
+ "coverageDirectory": "coverage",
105
+ "testEnvironment": "node"
106
+ }
107
+ }
@@ -0,0 +1,5 @@
1
+ coverage
2
+ dist
3
+ node_modules
4
+ package-lock.json
5
+ *.prisma
@@ -0,0 +1,6 @@
1
+ {
2
+ "singleQuote": true,
3
+ "trailingComma": "all",
4
+ "printWidth": 100,
5
+ "endOfLine": "auto"
6
+ }
@@ -0,0 +1,123 @@
1
+ # API Reference
2
+
3
+ The generated app exposes interactive Swagger documentation at:
4
+
5
+ ```txt
6
+ http://localhost:3000/docs
7
+ ```
8
+
9
+ Start the API first:
10
+
11
+ ```bash
12
+ npm run start:dev
13
+ ```
14
+
15
+ ## Authentication In Swagger
16
+
17
+ 1. Run `npm run setup:local` and copy the printed demo email and password.
18
+ 2. Open `/docs`.
19
+ 3. Run `POST /auth/login` with the demo credentials.
20
+ 4. Copy `accessToken` from the response.
21
+ 5. Click `Authorize` in Swagger.
22
+ 6. Paste the token as the bearer token value.
23
+
24
+ After authorization, Swagger can call protected routes directly.
25
+
26
+ ## Common Response Shape
27
+
28
+ Successful responses are documented per endpoint with typed response DTOs. Most
29
+ date values are ISO strings.
30
+
31
+ Errors use one common envelope:
32
+
33
+ ```json
34
+ {
35
+ "error": {
36
+ "code": "BAD_REQUEST",
37
+ "message": "Validation failed.",
38
+ "details": {}
39
+ }
40
+ }
41
+ ```
42
+
43
+ Typical status codes:
44
+
45
+ ```txt
46
+ 400 invalid body, route parameter, or query string
47
+ 401 missing or invalid bearer token
48
+ 403 authenticated user lacks org access or permission
49
+ 404 resource not found
50
+ 409 request conflicts with current state
51
+ 410 invitation token expired or no longer usable
52
+ 429 auth endpoint rate limit exceeded
53
+ 503 health check dependency failure
54
+ ```
55
+
56
+ ## Organisation-Scoped Routes
57
+
58
+ Most business routes are scoped under:
59
+
60
+ ```txt
61
+ /organisations/:orgId/...
62
+ ```
63
+
64
+ For those routes:
65
+
66
+ - `JwtAuthGuard` verifies the user identity.
67
+ - `OrgScopeGuard` loads the active membership for `orgId`.
68
+ - `PermissionGuard` checks the route permissions.
69
+ - `route-permission-registry.ts` is validated at startup and in tests, so
70
+ permission-gated routes must stay registered.
71
+
72
+ Use `POST /organisations` first if you need a fresh `orgId`.
73
+
74
+ ## Endpoint Groups
75
+
76
+ | Group | Purpose |
77
+ | -------------- | ----------------------------------------------------------------------- |
78
+ | Health | Confirm API and database availability. |
79
+ | Auth | Signup, login, refresh, logout, Google OAuth handoff, current user. |
80
+ | Organisations | Create an organisation and seed owner role/settings. |
81
+ | Memberships | Read memberships and manage status, role, owner, billing contact. |
82
+ | Invitations | Invite, resend, revoke, accept, and decline organisation invitations. |
83
+ | Access control | List permissions, route-permission mapping, and manage roles. |
84
+ | Settings | Read and update typed organisation settings. |
85
+ | Audit | Search organisation audit events. |
86
+ | Sample | Copy-paste reference for request context, RBAC, DTOs, and audit writes. |
87
+
88
+ ## REST Client File
89
+
90
+ `docs/api.http` contains a runnable flow for VS Code REST Client or compatible
91
+ HTTP clients. It mirrors the Swagger groups and is useful when a developer wants
92
+ repeatable API calls outside the browser.
93
+
94
+ Update the variables at the top after setup:
95
+
96
+ ```http
97
+ @baseUrl = http://localhost:3000
98
+ @email = owner@example.com
99
+ @password = replace-with-printed-setup-password
100
+ @orgId = replace-with-org-id
101
+ @roleId = replace-with-role-id
102
+ ```
103
+
104
+ ## Keeping Swagger Complete
105
+
106
+ When adding a new endpoint, add these pieces in the same change:
107
+
108
+ - `@ApiTags` on the controller, or reuse the existing controller tag.
109
+ - `@ApiOperation` on every handler.
110
+ - `@ApiBearerAuth` for protected routes.
111
+ - `@ApiParam` and query DTO properties for route/query inputs.
112
+ - `@ApiProperty` or `@ApiPropertyOptional` on request DTO fields.
113
+ - A response DTO and `@ApiOkResponse` or `@ApiCreatedResponse`.
114
+ - `ApiErrorResponses` or `ApiProtectedErrorResponses`.
115
+ - A matching entry in `docs/api.http` for important flows.
116
+
117
+ Run these checks before handing the module to another developer:
118
+
119
+ ```bash
120
+ npm run lint
121
+ npm run build
122
+ npm test
123
+ ```
@@ -0,0 +1,65 @@
1
+ # Getting Started
2
+
3
+ ## One-Command Local Setup
4
+
5
+ ```bash
6
+ npm run setup:local
7
+ ```
8
+
9
+ The setup script:
10
+
11
+ - creates `.env` from `.env.example` if needed
12
+ - fills missing `DATABASE_URL`, `TEST_DATABASE_URL`, `JWT_SECRET`, and `TEST_USER_PASSWORD`
13
+ - creates the dev and test PostgreSQL databases if they do not exist
14
+ - generates Prisma Client
15
+ - deploys migrations
16
+ - seeds the permission catalogue
17
+ - seeds a demo owner, organisation, roles, settings, and membership
18
+ - prints the demo login credentials
19
+
20
+ It is non-destructive. It does not drop, reset, truncate, or overwrite existing application data.
21
+
22
+ ## PostgreSQL
23
+
24
+ The default expects a local PostgreSQL server with user `postgres`, password `postgres`, and host `localhost:5432`.
25
+
26
+ ```txt
27
+ DATABASE_URL=postgresql://postgres:postgres@localhost:5432/app
28
+ TEST_DATABASE_URL=postgresql://postgres:postgres@localhost:5432/app_test
29
+ ```
30
+
31
+ If your database differs, edit `.env` and rerun `npm run setup:local`.
32
+
33
+ ## Start The API
34
+
35
+ ```bash
36
+ npm run start:dev
37
+ ```
38
+
39
+ Open:
40
+
41
+ ```txt
42
+ http://localhost:3000/health
43
+ http://localhost:3000/docs
44
+ ```
45
+
46
+ Login through Swagger with the printed demo credentials and authorize with the returned bearer token.
47
+
48
+ Read `docs/API_REFERENCE.md` for the full Swagger workflow, response shapes,
49
+ error envelope, endpoint groups, and the checklist for keeping future endpoints
50
+ documented.
51
+
52
+ ## Useful Commands
53
+
54
+ ```bash
55
+ npm run setup:local
56
+ npm run db:create
57
+ npm run lint
58
+ npm run format
59
+ npm run build
60
+ npm test
61
+ npm run test:e2e
62
+ npm run start:dev
63
+ ```
64
+
65
+ `npm run db:reset` is intentionally separate and destructive.
@@ -0,0 +1,40 @@
1
+ # Module Completion Checklist
2
+
3
+ Use this checklist before considering a new organisation-scoped module complete.
4
+
5
+ ## Structure
6
+
7
+ - [ ] Module follows `application`, `dto`, `presentation`, `types`, and `infrastructure` folders where relevant.
8
+ - [ ] Controller route is path-scoped with `/organisations/:orgId/...` for organisation data.
9
+ - [ ] Controller uses `JwtAuthGuard`, `OrgScopeGuard`, and `PermissionGuard`.
10
+ - [ ] Every protected handler has `@RequirePermissions(...)`.
11
+ - [ ] Route-permission registry includes every protected route.
12
+
13
+ ## Security
14
+
15
+ - [ ] Service calls `requestContext.assertOrgScope(orgId)` before reading or writing organisation data.
16
+ - [ ] Queries filter by `orgId` explicitly.
17
+ - [ ] Raw SQL, if any, filters by `orgId` explicitly.
18
+ - [ ] Cross-organisation IDs are rejected or return not found.
19
+ - [ ] Owner-only behavior is enforced in the service layer, not only through route metadata.
20
+
21
+ ## Data and Transactions
22
+
23
+ - [ ] Writes that touch multiple rows use a transaction.
24
+ - [ ] Uniqueness and integrity rules are backed by database constraints where practical.
25
+ - [ ] Prisma known errors that users can trigger are mapped to clear HTTP errors.
26
+ - [ ] Cache invalidation, if needed, runs after the transaction commits.
27
+
28
+ ## Audit and Settings
29
+
30
+ - [ ] Security-sensitive, billing, identity, access-control, and organisation-configuration writes create audit rows.
31
+ - [ ] Audit metadata includes previous and next values when useful.
32
+ - [ ] New setting keys are added to typed setting definitions and validated.
33
+
34
+ ## Verification
35
+
36
+ - [ ] Build passes.
37
+ - [ ] Prisma generate passes.
38
+ - [ ] Happy-path API flow is verified.
39
+ - [ ] Missing-permission and cross-org access are verified.
40
+ - [ ] Seed/setup scripts remain idempotent.
@@ -0,0 +1,46 @@
1
+ # OAuth
2
+
3
+ ## Google OAuth
4
+
5
+ Google OAuth is optional and disabled by default.
6
+
7
+ When enabled, the flow is:
8
+
9
+ ```txt
10
+ GET /auth/google
11
+ GET /auth/google/callback
12
+ POST /auth/oauth/exchange
13
+ ```
14
+
15
+ The callback redirects to `AUTH_GOOGLE_SUCCESS_REDIRECT_URL` with a short-lived one-time `code`. The client exchanges that code with `POST /auth/oauth/exchange` to receive JWT access and refresh tokens.
16
+
17
+ Failures redirect to `AUTH_GOOGLE_ERROR_REDIRECT_URL?error=oauth_failed`.
18
+
19
+ ## State And PKCE
20
+
21
+ Google OAuth uses state and PKCE. A short-lived `oauth.sid` session cookie is scoped only to `/auth/google` and `/auth/google/callback` so Passport can store and verify the OAuth state and PKCE code verifier during the handshake.
22
+
23
+ The rest of the app remains stateless JWT. There is no `passport.session()` and no persistent login session.
24
+
25
+ ## Auth Endpoint Hardening
26
+
27
+ Login uses the same generic failure response and a dummy-hash path for missing users so
28
+ the password-check timing stays consistent. Signup returns a generic conflict response
29
+ for duplicate emails, but it is still weakly enumerable by design because successful
30
+ signup immediately returns tokens. Full non-enumerable signup requires a deferred
31
+ email-verification flow; rate-limit auth endpoints before exposing the app publicly.
32
+
33
+ Set these values when `AUTH_GOOGLE_ENABLED=true`:
34
+
35
+ ```txt
36
+ AUTH_GOOGLE_CLIENT_ID=
37
+ AUTH_GOOGLE_CLIENT_SECRET=
38
+ AUTH_GOOGLE_CALLBACK_URL=http://localhost:3000/auth/google/callback
39
+ AUTH_GOOGLE_SUCCESS_REDIRECT_URL=http://localhost:3000/auth/google/success
40
+ AUTH_GOOGLE_ERROR_REDIRECT_URL=http://localhost:3000/auth/google/error
41
+ SESSION_SECRET=
42
+ ```
43
+
44
+ `SESSION_SECRET` must be at least 32 random characters.
45
+
46
+ The default `express-session` memory store is intended for development and single-instance deployments. In multi-instance production deployments, use sticky sessions or replace the store with a shared store such as Redis.
@@ -0,0 +1,23 @@
1
+ # Sample Module
2
+
3
+ The generated app includes a small `SampleModule` under `src/modules/sample`.
4
+
5
+ It exists to demonstrate the expected shape of an organisation-scoped module:
6
+
7
+ - path-scoped routes under `/organisations/:orgId/sample`
8
+ - `JwtAuthGuard`, `OrgScopeGuard`, and `PermissionGuard`
9
+ - explicit permission metadata
10
+ - service-level `requestContext.assertOrgScope(orgId)`
11
+ - request-context reads in service responses
12
+ - DTO validation
13
+ - audit writes for mutating commands
14
+ - route-permission registry entries
15
+
16
+ ## Routes
17
+
18
+ ```txt
19
+ GET /organisations/:orgId/sample/status organisations.read
20
+ POST /organisations/:orgId/sample/echo organisations.update
21
+ ```
22
+
23
+ The module is intentionally simple and has no database table. Replace or delete it when your first real business module is ready.