@ftisindia/create-app 0.1.3 → 0.1.4

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 (108) hide show
  1. package/package.json +1 -1
  2. package/template/README.md +1 -1
  3. package/template/_package.json +0 -2
  4. package/template/docs/API_REFERENCE.md +13 -0
  5. package/template/docs/OAUTH.md +7 -3
  6. package/template/scripts/gen-module.mjs +2 -0
  7. package/template/src/app.module.ts +16 -22
  8. package/template/src/common/dto/error-response.dto.ts +3 -3
  9. package/template/src/common/dto/membership-response.dto.ts +26 -14
  10. package/template/src/common/dto/mutation-response.dto.ts +1 -1
  11. package/template/src/common/dto/pagination-query.dto.ts +37 -0
  12. package/template/src/common/dto/role-summary.dto.ts +5 -5
  13. package/template/src/common/dto/user-summary.dto.ts +6 -6
  14. package/template/src/common/filters/http-exception.filter.ts +9 -19
  15. package/template/src/common/swagger/api-error-responses.ts +12 -12
  16. package/template/src/config/app.config.ts +3 -3
  17. package/template/src/config/auth.config.ts +3 -3
  18. package/template/src/config/database.config.ts +3 -3
  19. package/template/src/config/env.validation.ts +58 -40
  20. package/template/src/config/index.ts +5 -5
  21. package/template/src/config/rbac.config.ts +3 -3
  22. package/template/src/database/prisma/prisma-transaction.ts +1 -1
  23. package/template/src/database/prisma/prisma.module.ts +2 -2
  24. package/template/src/database/prisma/prisma.service.ts +3 -6
  25. package/template/src/main.ts +11 -11
  26. package/template/src/modules/access-control/access-control.module.ts +9 -9
  27. package/template/src/modules/access-control/application/role-permission-policy.ts +71 -0
  28. package/template/src/modules/access-control/application/route-registry.validator.ts +34 -63
  29. package/template/src/modules/access-control/application/services/ability.factory.ts +5 -9
  30. package/template/src/modules/access-control/application/services/access-control.service.ts +78 -85
  31. package/template/src/modules/access-control/application/services/permission.guard.ts +16 -21
  32. package/template/src/modules/access-control/application/services/rbac-cache.service.ts +7 -9
  33. package/template/src/modules/access-control/dto/access-control-response.dto.ts +32 -20
  34. package/template/src/modules/access-control/dto/create-role.dto.ts +6 -6
  35. package/template/src/modules/access-control/dto/update-role-permissions.dto.ts +3 -10
  36. package/template/src/modules/access-control/dto/update-role.dto.ts +6 -6
  37. package/template/src/modules/access-control/presentation/access-control.controller.ts +69 -74
  38. package/template/src/modules/access-control/presentation/permissions.decorator.ts +3 -3
  39. package/template/src/modules/access-control/presentation/public.decorator.ts +2 -2
  40. package/template/src/modules/access-control/types/permission-key.ts +19 -19
  41. package/template/src/modules/access-control/types/route-permission-registry.ts +76 -76
  42. package/template/src/modules/audit/application/services/audit.service.ts +7 -7
  43. package/template/src/modules/audit/audit.module.ts +4 -4
  44. package/template/src/modules/audit/dto/audit-response.dto.ts +18 -18
  45. package/template/src/modules/audit/dto/list-audit-logs-query.dto.ts +14 -14
  46. package/template/src/modules/audit/presentation/audit.controller.ts +17 -23
  47. package/template/src/modules/auth/application/services/auth.service.ts +147 -110
  48. package/template/src/modules/auth/application/services/password.service.ts +2 -2
  49. package/template/src/modules/auth/application/services/token.service.ts +20 -21
  50. package/template/src/modules/auth/auth.module.ts +20 -47
  51. package/template/src/modules/auth/dto/auth-response.dto.ts +9 -10
  52. package/template/src/modules/auth/dto/login.dto.ts +4 -4
  53. package/template/src/modules/auth/dto/logout.dto.ts +1 -1
  54. package/template/src/modules/auth/dto/oauth-exchange.dto.ts +4 -5
  55. package/template/src/modules/auth/dto/refresh-token.dto.ts +4 -5
  56. package/template/src/modules/auth/dto/signup.dto.ts +5 -11
  57. package/template/src/modules/auth/infrastructure/passport/google-auth.guard.ts +6 -14
  58. package/template/src/modules/auth/infrastructure/passport/google-oauth-state.store.ts +98 -0
  59. package/template/src/modules/auth/infrastructure/passport/google.strategy.ts +21 -30
  60. package/template/src/modules/auth/infrastructure/passport/jwt-auth.guard.ts +3 -3
  61. package/template/src/modules/auth/infrastructure/passport/jwt.strategy.ts +11 -11
  62. package/template/src/modules/auth/presentation/auth.controller.ts +45 -45
  63. package/template/src/modules/auth/presentation/current-user.decorator.ts +3 -5
  64. package/template/src/modules/auth/presentation/google-oauth-exception.filter.ts +5 -10
  65. package/template/src/modules/health/dto/health-response.dto.ts +5 -5
  66. package/template/src/modules/health/health.module.ts +2 -2
  67. package/template/src/modules/health/presentation/health.controller.ts +13 -13
  68. package/template/src/modules/invitations/application/services/invitations.service.ts +127 -176
  69. package/template/src/modules/invitations/dto/accept-invitation.dto.ts +6 -7
  70. package/template/src/modules/invitations/dto/create-invitation.dto.ts +14 -15
  71. package/template/src/modules/invitations/dto/invitation-response.dto.ts +37 -29
  72. package/template/src/modules/invitations/dto/invitation-token.dto.ts +4 -4
  73. package/template/src/modules/invitations/invitations.module.ts +5 -5
  74. package/template/src/modules/invitations/presentation/invitations.controller.ts +61 -63
  75. package/template/src/modules/memberships/application/services/memberships.service.ts +70 -84
  76. package/template/src/modules/memberships/dto/transfer-owner.dto.ts +4 -4
  77. package/template/src/modules/memberships/dto/update-billing-contact.dto.ts +2 -2
  78. package/template/src/modules/memberships/dto/update-membership-owner.dto.ts +2 -2
  79. package/template/src/modules/memberships/dto/update-membership-role.dto.ts +4 -4
  80. package/template/src/modules/memberships/dto/update-membership-status.dto.ts +3 -3
  81. package/template/src/modules/memberships/memberships.module.ts +4 -4
  82. package/template/src/modules/memberships/presentation/memberships.controller.ts +83 -99
  83. package/template/src/modules/organisations/application/services/organisations.service.ts +21 -23
  84. package/template/src/modules/organisations/dto/create-organisation.dto.ts +6 -13
  85. package/template/src/modules/organisations/dto/organisation-response.dto.ts +14 -14
  86. package/template/src/modules/organisations/infrastructure/repositories/organisations.repository.ts +4 -7
  87. package/template/src/modules/organisations/organisations.module.ts +5 -5
  88. package/template/src/modules/organisations/presentation/organisations.controller.ts +14 -23
  89. package/template/src/modules/organisations/types/default-organisation-data.ts +3 -9
  90. package/template/src/modules/request-context/application/services/request-context.service.ts +15 -7
  91. package/template/src/modules/request-context/presentation/org-scope.guard.ts +4 -9
  92. package/template/src/modules/request-context/presentation/request-context.interceptor.ts +4 -9
  93. package/template/src/modules/request-context/presentation/request-context.middleware.ts +7 -8
  94. package/template/src/modules/request-context/request-context.module.ts +7 -7
  95. package/template/src/modules/request-context/types/request-context.ts +2 -2
  96. package/template/src/modules/sample/application/services/sample.service.ts +10 -8
  97. package/template/src/modules/sample/dto/sample-echo.dto.ts +3 -3
  98. package/template/src/modules/sample/dto/sample-response.dto.ts +12 -12
  99. package/template/src/modules/sample/presentation/sample.controller.ts +25 -42
  100. package/template/src/modules/sample/sample.module.ts +4 -4
  101. package/template/src/modules/settings/application/services/settings.service.ts +15 -27
  102. package/template/src/modules/settings/dto/setting-response.dto.ts +9 -9
  103. package/template/src/modules/settings/dto/update-setting.dto.ts +5 -5
  104. package/template/src/modules/settings/presentation/settings.controller.ts +29 -35
  105. package/template/src/modules/settings/settings.module.ts +5 -5
  106. package/template/src/modules/settings/types/setting-definitions.ts +49 -33
  107. package/template/test/auth-refresh.spec.ts +90 -0
  108. package/template/test/role-permission-policy.spec.ts +94 -0
@@ -1,4 +1,4 @@
1
- import { Prisma } from "@prisma/client";
1
+ import { Prisma } from '@prisma/client';
2
2
 
3
3
  type TransactionOptions = {
4
4
  maxWait?: number;
@@ -1,5 +1,5 @@
1
- import { Global, Module } from "@nestjs/common";
2
- import { PrismaService } from "./prisma.service";
1
+ import { Global, Module } from '@nestjs/common';
2
+ import { PrismaService } from './prisma.service';
3
3
 
4
4
  @Global()
5
5
  @Module({
@@ -1,11 +1,8 @@
1
- import { Injectable, OnModuleDestroy, OnModuleInit } from "@nestjs/common";
2
- import { PrismaClient } from "@prisma/client";
1
+ import { Injectable, OnModuleDestroy, OnModuleInit } from '@nestjs/common';
2
+ import { PrismaClient } from '@prisma/client';
3
3
 
4
4
  @Injectable()
5
- export class PrismaService
6
- extends PrismaClient
7
- implements OnModuleInit, OnModuleDestroy
8
- {
5
+ export class PrismaService extends PrismaClient implements OnModuleInit, OnModuleDestroy {
9
6
  async onModuleInit() {
10
7
  await this.$connect();
11
8
  }
@@ -1,9 +1,9 @@
1
- import { ValidationPipe } from "@nestjs/common";
2
- import { ConfigService } from "@nestjs/config";
3
- import { NestFactory } from "@nestjs/core";
4
- import { DocumentBuilder, SwaggerModule } from "@nestjs/swagger";
5
- import { AppModule } from "./app.module";
6
- import { HttpExceptionFilter } from "./common/filters/http-exception.filter";
1
+ import { ValidationPipe } from '@nestjs/common';
2
+ import { ConfigService } from '@nestjs/config';
3
+ import { NestFactory } from '@nestjs/core';
4
+ import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger';
5
+ import { AppModule } from './app.module';
6
+ import { HttpExceptionFilter } from './common/filters/http-exception.filter';
7
7
 
8
8
  async function bootstrap() {
9
9
  const app = await NestFactory.create(AppModule);
@@ -22,21 +22,21 @@ async function bootstrap() {
22
22
  const document = SwaggerModule.createDocument(
23
23
  app,
24
24
  new DocumentBuilder()
25
- .setTitle("Foundation Starter API")
25
+ .setTitle('Foundation Starter API')
26
26
  .setDescription(
27
- "Modular-monolith starter API with auth, organisations, RBAC, audit, and settings.",
27
+ 'Modular-monolith starter API with auth, organisations, RBAC, audit, and settings.',
28
28
  )
29
- .setVersion("0.1.0")
29
+ .setVersion('0.1.0')
30
30
  .addBearerAuth()
31
31
  .build(),
32
32
  );
33
- SwaggerModule.setup("docs", app, document, {
33
+ SwaggerModule.setup('docs', app, document, {
34
34
  swaggerOptions: {
35
35
  persistAuthorization: true,
36
36
  },
37
37
  });
38
38
 
39
- await app.listen(config.get<number>("app.port") ?? 3000);
39
+ await app.listen(config.get<number>('app.port') ?? 3000);
40
40
  }
41
41
 
42
42
  void bootstrap();
@@ -1,12 +1,12 @@
1
- import { Global, Module } from "@nestjs/common";
2
- import { DiscoveryModule } from "@nestjs/core";
3
- import { AuthModule } from "../auth/auth.module";
4
- import { RouteRegistryValidator } from "./application/route-registry.validator";
5
- import { AbilityFactory } from "./application/services/ability.factory";
6
- import { AccessControlService } from "./application/services/access-control.service";
7
- import { PermissionGuard } from "./application/services/permission.guard";
8
- import { RbacCacheService } from "./application/services/rbac-cache.service";
9
- import { AccessControlController } from "./presentation/access-control.controller";
1
+ import { Global, Module } from '@nestjs/common';
2
+ import { DiscoveryModule } from '@nestjs/core';
3
+ import { AuthModule } from '../auth/auth.module';
4
+ import { RouteRegistryValidator } from './application/route-registry.validator';
5
+ import { AbilityFactory } from './application/services/ability.factory';
6
+ import { AccessControlService } from './application/services/access-control.service';
7
+ import { PermissionGuard } from './application/services/permission.guard';
8
+ import { RbacCacheService } from './application/services/rbac-cache.service';
9
+ import { AccessControlController } from './presentation/access-control.controller';
10
10
 
11
11
  @Global()
12
12
  @Module({
@@ -0,0 +1,71 @@
1
+ import { ForbiddenException } from '@nestjs/common';
2
+
3
+ /**
4
+ * Prevents privilege self-elevation: an actor may only assign or invite to a
5
+ * role whose permission set is a subset of the actor's own effective
6
+ * permissions. Organisation owners bypass the check.
7
+ *
8
+ * Both MembershipsService.assignRole and InvitationsService (create/resend)
9
+ * call this with the candidate role's permission keys and the actor's RBAC
10
+ * context, so a member holding only e.g. `roles.manage` / `memberships.invite`
11
+ * cannot grant a role carrying permissions they do not themselves hold.
12
+ */
13
+ export function assertRoleWithinActorPermissions(args: {
14
+ actorIsOwner: boolean;
15
+ actorPermissionKeys: readonly string[];
16
+ rolePermissionKeys: readonly string[];
17
+ }) {
18
+ if (args.actorIsOwner) {
19
+ return;
20
+ }
21
+
22
+ const heldKeys = new Set(args.actorPermissionKeys);
23
+ const exceeded = args.rolePermissionKeys.filter((key) => !heldKeys.has(key)).sort();
24
+
25
+ if (exceeded.length > 0) {
26
+ throw new ForbiddenException(
27
+ `You cannot grant a role with permissions you do not hold: ${exceeded.join(', ')}.`,
28
+ );
29
+ }
30
+ }
31
+
32
+ /**
33
+ * Prevents privilege escalation when a role's permission set is rewritten: a
34
+ * non-owner actor may only add or remove permission keys they themselves hold.
35
+ * Permission keys outside the actor's own set must stay unchanged — they can be
36
+ * neither granted nor revoked by someone who does not hold them. Owners bypass.
37
+ */
38
+ export function assertPermissionChangeWithinActor(args: {
39
+ actorIsOwner: boolean;
40
+ actorPermissionKeys: readonly string[];
41
+ previousPermissionKeys: readonly string[];
42
+ nextPermissionKeys: readonly string[];
43
+ }) {
44
+ if (args.actorIsOwner) {
45
+ return;
46
+ }
47
+
48
+ const previous = new Set(args.previousPermissionKeys);
49
+ const next = new Set(args.nextPermissionKeys);
50
+ const changed = new Set<string>();
51
+
52
+ for (const key of args.nextPermissionKeys) {
53
+ if (!previous.has(key)) {
54
+ changed.add(key);
55
+ }
56
+ }
57
+ for (const key of args.previousPermissionKeys) {
58
+ if (!next.has(key)) {
59
+ changed.add(key);
60
+ }
61
+ }
62
+
63
+ const heldKeys = new Set(args.actorPermissionKeys);
64
+ const exceeded = [...changed].filter((key) => !heldKeys.has(key)).sort();
65
+
66
+ if (exceeded.length > 0) {
67
+ throw new ForbiddenException(
68
+ `You cannot grant or revoke permissions you do not hold: ${exceeded.join(', ')}.`,
69
+ );
70
+ }
71
+ }
@@ -1,17 +1,9 @@
1
- import {
2
- Injectable,
3
- OnApplicationBootstrap,
4
- RequestMethod,
5
- } from "@nestjs/common";
6
- import { METHOD_METADATA, PATH_METADATA } from "@nestjs/common/constants";
7
- import { DiscoveryService, MetadataScanner, Reflector } from "@nestjs/core";
8
- import { REQUIRED_PERMISSIONS_KEY } from "../presentation/permissions.decorator";
9
- import {
10
- PermissionKey,
11
- permissionKeys,
12
- RoutePermissionEntry,
13
- } from "../types/permission-key";
14
- import { routePermissionRegistry } from "../types/route-permission-registry";
1
+ import { Injectable, OnApplicationBootstrap, RequestMethod } from '@nestjs/common';
2
+ import { METHOD_METADATA, PATH_METADATA } from '@nestjs/common/constants';
3
+ import { DiscoveryService, MetadataScanner, Reflector } from '@nestjs/core';
4
+ import { REQUIRED_PERMISSIONS_KEY } from '../presentation/permissions.decorator';
5
+ import { PermissionKey, permissionKeys, RoutePermissionEntry } from '../types/permission-key';
6
+ import { routePermissionRegistry } from '../types/route-permission-registry';
15
7
 
16
8
  type ControllerInstance = Record<string, unknown>;
17
9
  type RouteHandler = (...args: unknown[]) => unknown;
@@ -33,28 +25,16 @@ export class RouteRegistryValidator implements OnApplicationBootstrap {
33
25
  const enforcedRoutes = this.discoverPermissionRoutes();
34
26
  const registryRoutes = routePermissionRegistry.map(normalizeEntry);
35
27
 
36
- const enforcedByRoute = mapByRoute(
37
- enforcedRoutes,
38
- "enforced route metadata",
39
- );
40
- const registryByRoute = mapByRoute(
41
- registryRoutes,
42
- "routePermissionRegistry",
43
- );
28
+ const enforcedByRoute = mapByRoute(enforcedRoutes, 'enforced route metadata');
29
+ const registryByRoute = mapByRoute(registryRoutes, 'routePermissionRegistry');
44
30
  const missingFromRegistry = enforcedRoutes.filter(
45
31
  (entry) => !registryByRoute.has(routeKey(entry)),
46
32
  );
47
33
  const staleRegistryEntries = registryRoutes.filter(
48
34
  (entry) => !enforcedByRoute.has(routeKey(entry)),
49
35
  );
50
- const permissionMismatches = collectPermissionMismatches(
51
- registryByRoute,
52
- enforcedByRoute,
53
- );
54
- const unknownPermissions = collectUnknownPermissions([
55
- ...registryRoutes,
56
- ...enforcedRoutes,
57
- ]);
36
+ const permissionMismatches = collectPermissionMismatches(registryByRoute, enforcedByRoute);
37
+ const unknownPermissions = collectUnknownPermissions([...registryRoutes, ...enforcedRoutes]);
58
38
 
59
39
  if (
60
40
  missingFromRegistry.length > 0 ||
@@ -83,16 +63,12 @@ export class RouteRegistryValidator implements OnApplicationBootstrap {
83
63
  continue;
84
64
  }
85
65
 
86
- const controllerPaths = toPathParts(
87
- this.reflector.get(PATH_METADATA, metatype),
88
- );
66
+ const controllerPaths = toPathParts(this.reflector.get(PATH_METADATA, metatype));
89
67
  const prototype = Object.getPrototypeOf(instance) as object | null;
90
68
 
91
- for (const methodName of this.metadataScanner.getAllMethodNames(
92
- prototype,
93
- )) {
69
+ for (const methodName of this.metadataScanner.getAllMethodNames(prototype)) {
94
70
  const handler = instance[methodName];
95
- if (typeof handler !== "function") {
71
+ if (typeof handler !== 'function') {
96
72
  continue;
97
73
  }
98
74
 
@@ -112,9 +88,7 @@ export class RouteRegistryValidator implements OnApplicationBootstrap {
112
88
  continue;
113
89
  }
114
90
 
115
- const methodPaths = toPathParts(
116
- this.reflector.get(PATH_METADATA, handler),
117
- );
91
+ const methodPaths = toPathParts(this.reflector.get(PATH_METADATA, handler));
118
92
  for (const path of combinePaths(controllerPaths, methodPaths)) {
119
93
  entries.push(
120
94
  normalizeEntry({
@@ -143,10 +117,7 @@ function collectPermissionMismatches(
143
117
  continue;
144
118
  }
145
119
 
146
- if (
147
- permissionListKey(registered.permissions) !==
148
- permissionListKey(enforced.permissions)
149
- ) {
120
+ if (permissionListKey(registered.permissions) !== permissionListKey(enforced.permissions)) {
150
121
  mismatches.push({
151
122
  key,
152
123
  registryPermissions: registered.permissions,
@@ -176,48 +147,48 @@ function formatRegistryDriftError({
176
147
  permissionMismatches: RoutePermissionDiff[];
177
148
  unknownPermissions: string[];
178
149
  }) {
179
- const sections = ["Route permission registry drift detected."];
150
+ const sections = ['Route permission registry drift detected.'];
180
151
 
181
152
  if (missingFromRegistry.length > 0) {
182
153
  sections.push(
183
154
  [
184
- "Permission-gated routes missing from routePermissionRegistry:",
155
+ 'Permission-gated routes missing from routePermissionRegistry:',
185
156
  ...missingFromRegistry.map(formatEntry),
186
- ].join("\n"),
157
+ ].join('\n'),
187
158
  );
188
159
  }
189
160
 
190
161
  if (staleRegistryEntries.length > 0) {
191
162
  sections.push(
192
163
  [
193
- "routePermissionRegistry entries without matching permission-gated routes:",
164
+ 'routePermissionRegistry entries without matching permission-gated routes:',
194
165
  ...staleRegistryEntries.map(formatEntry),
195
- ].join("\n"),
166
+ ].join('\n'),
196
167
  );
197
168
  }
198
169
 
199
170
  if (permissionMismatches.length > 0) {
200
171
  sections.push(
201
172
  [
202
- "routePermissionRegistry permission mismatches:",
173
+ 'routePermissionRegistry permission mismatches:',
203
174
  ...permissionMismatches.map(
204
175
  (diff) =>
205
- `- ${diff.key}: registry [${diff.registryPermissions.join(", ")}], enforced [${diff.enforcedPermissions.join(", ")}]`,
176
+ `- ${diff.key}: registry [${diff.registryPermissions.join(', ')}], enforced [${diff.enforcedPermissions.join(', ')}]`,
206
177
  ),
207
- ].join("\n"),
178
+ ].join('\n'),
208
179
  );
209
180
  }
210
181
 
211
182
  if (unknownPermissions.length > 0) {
212
183
  sections.push(
213
184
  [
214
- "Unknown permission keys referenced by guarded routes or routePermissionRegistry:",
185
+ 'Unknown permission keys referenced by guarded routes or routePermissionRegistry:',
215
186
  ...unknownPermissions.map((permission) => `- ${permission}`),
216
- ].join("\n"),
187
+ ].join('\n'),
217
188
  );
218
189
  }
219
190
 
220
- return sections.join("\n\n");
191
+ return sections.join('\n\n');
221
192
  }
222
193
 
223
194
  function mapByRoute(entries: RoutePermissionEntry[], source: string) {
@@ -248,7 +219,7 @@ function combinePaths(controllerPaths: string[], methodPaths: string[]) {
248
219
 
249
220
  for (const controllerPath of controllerPaths) {
250
221
  for (const methodPath of methodPaths) {
251
- paths.push(normalizePath([controllerPath, methodPath].join("/")));
222
+ paths.push(normalizePath([controllerPath, methodPath].join('/')));
252
223
  }
253
224
  }
254
225
 
@@ -257,19 +228,19 @@ function combinePaths(controllerPaths: string[], methodPaths: string[]) {
257
228
 
258
229
  function toPathParts(metadata: string | string[] | undefined) {
259
230
  if (Array.isArray(metadata)) {
260
- return metadata.length > 0 ? metadata : [""];
231
+ return metadata.length > 0 ? metadata : [''];
261
232
  }
262
233
 
263
- return [metadata ?? ""];
234
+ return [metadata ?? ''];
264
235
  }
265
236
 
266
237
  function normalizePath(path: string) {
267
238
  const trimmed = path
268
- .split("/")
239
+ .split('/')
269
240
  .filter((part) => part.length > 0)
270
- .join("/");
241
+ .join('/');
271
242
 
272
- return trimmed ? `/${trimmed}` : "/";
243
+ return trimmed ? `/${trimmed}` : '/';
273
244
  }
274
245
 
275
246
  function requestMethodToString(method: RequestMethod) {
@@ -281,9 +252,9 @@ function routeKey(entry: RoutePermissionEntry) {
281
252
  }
282
253
 
283
254
  function permissionListKey(permissions: PermissionKey[]) {
284
- return permissions.join("\0");
255
+ return permissions.join('\0');
285
256
  }
286
257
 
287
258
  function formatEntry(entry: RoutePermissionEntry) {
288
- return `- ${routeKey(entry)} -> [${entry.permissions.join(", ")}]`;
259
+ return `- ${routeKey(entry)} -> [${entry.permissions.join(', ')}]`;
289
260
  }
@@ -1,11 +1,7 @@
1
- import {
2
- AbilityBuilder,
3
- createMongoAbility,
4
- MongoAbility,
5
- } from "@casl/ability";
6
- import { Injectable } from "@nestjs/common";
7
- import { permissionKeyToRule } from "../../types/permission-key";
8
- import { RbacContext } from "../../types/rbac-context";
1
+ import { AbilityBuilder, createMongoAbility, MongoAbility } from '@casl/ability';
2
+ import { Injectable } from '@nestjs/common';
3
+ import { permissionKeyToRule } from '../../types/permission-key';
4
+ import { RbacContext } from '../../types/rbac-context';
9
5
 
10
6
  export type AppAbility = MongoAbility<[string, string]>;
11
7
 
@@ -15,7 +11,7 @@ export class AbilityFactory {
15
11
  const { can, build } = new AbilityBuilder<AppAbility>(createMongoAbility);
16
12
 
17
13
  if (context.isOwner) {
18
- can("manage", "all");
14
+ can('manage', 'all');
19
15
  }
20
16
 
21
17
  for (const key of context.permissionKeys) {