exguard-endpoint 1.0.8 → 1.0.9

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/dist/index.cjs CHANGED
@@ -65,6 +65,10 @@ ExGuardModule = __decorateClass([
65
65
  var import_common2 = require("@nestjs/common");
66
66
  var EXGUARD_PERMISSIONS_KEY = "exguard_permissions";
67
67
  var EXGUARD_ROLES_KEY = "exguard_roles";
68
+ var EXGUARD_PERMISSIONS_ALL_KEY = "exguard_permissions_all";
69
+ var EXGUARD_ROLES_ALL_KEY = "exguard_roles_all";
70
+ var RequirePermissions = (permissions) => (0, import_common2.SetMetadata)(EXGUARD_PERMISSIONS_KEY, permissions);
71
+ var RequireRoles = (roles) => (0, import_common2.SetMetadata)(EXGUARD_ROLES_KEY, roles);
68
72
  var cache = /* @__PURE__ */ new Map();
69
73
  var CACHE_TTL = 5 * 60 * 1e3;
70
74
  function getCached(key) {
@@ -120,6 +124,7 @@ var ExGuardPermissionGuard = class {
120
124
  }
121
125
  const meData = await meResponse.json();
122
126
  authResult = { allowed: true, user: meData.data || meData };
127
+ console.log("[ExGuard DEBUG] Full user object:", JSON.stringify(authResult.user, null, 2));
123
128
  setCache(cacheKey, authResult);
124
129
  } catch (error) {
125
130
  console.error("[ExGuard] Auth error:", error);
@@ -134,22 +139,32 @@ var ExGuardPermissionGuard = class {
134
139
  }
135
140
  const handler = context.getHandler();
136
141
  const permMeta = this.reflector.get(EXGUARD_PERMISSIONS_KEY, handler);
142
+ console.log("[ExGuard DEBUG] permMeta from reflector:", permMeta);
143
+ console.log("[ExGuard DEBUG] handler:", handler);
144
+ console.log("[ExGuard DEBUG] handler name:", handler.name);
137
145
  if (permMeta) {
138
- const { permissions, requireAll } = permMeta;
146
+ const permissions = Array.isArray(permMeta) ? permMeta : permMeta.permissions;
147
+ const requireAll = permMeta.requireAll ?? this.reflector.get(EXGUARD_PERMISSIONS_ALL_KEY, handler) ?? false;
139
148
  const userPermissions = authResult.user.modules ? authResult.user.modules.flatMap((m) => m.permissions) : [];
149
+ console.log("[ExGuard DEBUG] Required permissions:", permissions);
150
+ console.log("[ExGuard DEBUG] User permissions:", userPermissions);
151
+ console.log("[ExGuard DEBUG] requireAll:", requireAll);
140
152
  if (requireAll) {
141
153
  if (!permissions.every((p) => userPermissions.includes(p))) {
142
154
  throw new import_common2.ForbiddenException("Insufficient permissions");
143
155
  }
144
156
  } else {
145
- if (!permissions.some((p) => userPermissions.includes(p))) {
157
+ const hasPermission = permissions.some((p) => userPermissions.includes(p));
158
+ console.log("[ExGuard DEBUG] Permission check result:", hasPermission);
159
+ if (!hasPermission) {
146
160
  throw new import_common2.ForbiddenException("Insufficient permissions");
147
161
  }
148
162
  }
149
163
  }
150
164
  const roleMeta = this.reflector.get(EXGUARD_ROLES_KEY, handler);
151
165
  if (roleMeta) {
152
- const { roles, requireAll } = roleMeta;
166
+ const roles = Array.isArray(roleMeta) ? roleMeta : roleMeta.roles;
167
+ const requireAll = roleMeta.requireAll ?? this.reflector.get(EXGUARD_ROLES_ALL_KEY, handler) ?? false;
153
168
  const userRoles = authResult.user.roles || [];
154
169
  if (requireAll) {
155
170
  if (!roles.every((r) => userRoles.includes(r))) {
@@ -177,18 +192,6 @@ ExGuardPermissionGuard = __decorateClass([
177
192
  __decorateParam(0, (0, import_common2.Optional)()),
178
193
  __decorateParam(0, (0, import_common2.Inject)("EXGUARD_BASE_URL"))
179
194
  ], ExGuardPermissionGuard);
180
- function RequirePermissions(permissions, requireAll = false) {
181
- return function(target, propertyKey, descriptor) {
182
- Reflect.defineMetadata(EXGUARD_PERMISSIONS_KEY, { permissions, requireAll }, descriptor.value);
183
- return descriptor;
184
- };
185
- }
186
- function RequireRoles(roles, requireAll = false) {
187
- return function(target, propertyKey, descriptor) {
188
- Reflect.defineMetadata(EXGUARD_ROLES_KEY, { roles, requireAll }, descriptor.value);
189
- return descriptor;
190
- };
191
- }
192
195
  function clearExGuardCache() {
193
196
  cache.clear();
194
197
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/module.ts","../src/guard.ts"],"sourcesContent":["export { ExGuardModule } from './module.js';\nexport { ExGuardPermissionGuard, RequirePermissions, RequireRoles, clearExGuardCache, EXGUARD_PERMISSIONS_KEY, EXGUARD_ROLES_KEY } from './guard.js';\n","import { Module, Global, DynamicModule } from '@nestjs/common';\n\nexport interface ExGuardModuleOptions {\n baseUrl: string;\n}\n\n@Global()\n@Module({})\nexport class ExGuardModule {\n static forRoot(options: ExGuardModuleOptions): DynamicModule;\n static forRoot(baseUrl: string): DynamicModule;\n static forRoot(optionsOrUrl: ExGuardModuleOptions | string): DynamicModule {\n const baseUrl = typeof optionsOrUrl === 'string' ? optionsOrUrl : optionsOrUrl.baseUrl;\n \n return {\n module: ExGuardModule,\n providers: [\n {\n provide: 'EXGUARD_BASE_URL',\n useValue: baseUrl,\n },\n ],\n exports: ['EXGUARD_BASE_URL'],\n };\n }\n}\n","import { Injectable, CanActivate, ExecutionContext, UnauthorizedException, ForbiddenException, Inject, Optional } from '@nestjs/common';\nimport { Reflector } from '@nestjs/core';\n\nexport const EXGUARD_PERMISSIONS_KEY = 'exguard_permissions';\nexport const EXGUARD_ROLES_KEY = 'exguard_roles';\n\nconst cache = new Map<string, { data: any; timestamp: number }>();\nconst CACHE_TTL = 5 * 60 * 1000; // 5 minutes\n\nfunction getCached(key: string): any {\n const entry = cache.get(key);\n if (!entry) return null;\n if (Date.now() - entry.timestamp > CACHE_TTL) {\n cache.delete(key);\n return null;\n }\n return entry.data;\n}\n\nfunction setCache(key: string, data: any): void {\n cache.set(key, { data, timestamp: Date.now() });\n}\n\n@Injectable()\nexport class ExGuardPermissionGuard implements CanActivate {\n constructor(\n @Optional() @Inject('EXGUARD_BASE_URL') private baseUrl: string,\n private reflector: Reflector,\n ) {}\n\n async canActivate(context: ExecutionContext): Promise<boolean> {\n const request = context.switchToHttp().getRequest();\n const token = this.extractToken(request);\n\n if (!token) {\n throw new UnauthorizedException('No token provided');\n }\n\n if (!this.baseUrl) {\n console.warn('[ExGuard] Base URL not configured. Access denied.');\n return false;\n }\n\n // Check cache first\n const cacheKey = `auth:${token}`;\n let authResult = getCached(cacheKey);\n\n if (!authResult) {\n try {\n // Verify token\n const verifyResponse = await fetch(`${this.baseUrl}/guard/verify-token`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${token}`,\n },\n body: JSON.stringify({ id_token: token }),\n });\n\n if (!verifyResponse.ok) {\n throw new ForbiddenException('Invalid token');\n }\n\n // Get user access\n const meResponse = await fetch(`${this.baseUrl}/guard/me`, {\n method: 'GET',\n headers: {\n 'Authorization': `Bearer ${token}`,\n },\n });\n\n if (!meResponse.ok) {\n throw new ForbiddenException('Failed to get user access');\n }\n\n const meData = await meResponse.json();\n authResult = { allowed: true, user: meData.data || meData };\n \n // Cache the result\n setCache(cacheKey, authResult);\n } catch (error) {\n console.error('[ExGuard] Auth error:', error);\n throw new ForbiddenException('Authentication failed');\n }\n }\n\n if (!authResult.allowed) {\n throw new ForbiddenException(authResult.error || 'Access denied');\n }\n\n if (!authResult.user) {\n throw new ForbiddenException('User not found');\n }\n\n const handler = context.getHandler();\n const permMeta = this.reflector.get(EXGUARD_PERMISSIONS_KEY, handler);\n\n if (permMeta) {\n const { permissions, requireAll } = permMeta;\n const userPermissions = authResult.user.modules ? authResult.user.modules.flatMap((m: any) => m.permissions) : [];\n\n if (requireAll) {\n if (!permissions.every((p: string) => userPermissions.includes(p))) {\n throw new ForbiddenException('Insufficient permissions');\n }\n } else {\n if (!permissions.some((p: string) => userPermissions.includes(p))) {\n throw new ForbiddenException('Insufficient permissions');\n }\n }\n }\n\n const roleMeta = this.reflector.get(EXGUARD_ROLES_KEY, handler);\n\n if (roleMeta) {\n const { roles, requireAll } = roleMeta;\n const userRoles = authResult.user.roles || [];\n\n if (requireAll) {\n if (!roles.every((r: string) => userRoles.includes(r))) {\n throw new ForbiddenException('Insufficient roles');\n }\n } else {\n if (!roles.some((r: string) => userRoles.includes(r))) {\n throw new ForbiddenException('Insufficient roles');\n }\n }\n }\n\n request.user = authResult.user;\n return true;\n }\n\n private extractToken(request: any): string | null {\n const auth = request.headers?.authorization;\n if (auth?.startsWith('Bearer ')) {\n return auth.substring(7);\n }\n return request.headers?.['x-access-token'] || null;\n }\n}\n\nexport function RequirePermissions(permissions: string[], requireAll = false) {\n return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {\n Reflect.defineMetadata(EXGUARD_PERMISSIONS_KEY, { permissions, requireAll }, descriptor.value);\n return descriptor;\n };\n}\n\nexport function RequireRoles(roles: string[], requireAll = false) {\n return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {\n Reflect.defineMetadata(EXGUARD_ROLES_KEY, { roles, requireAll }, descriptor.value);\n return descriptor;\n };\n}\n\nexport function clearExGuardCache(): void {\n cache.clear();\n}\n"],"mappings":"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","names":["import_common"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/module.ts","../src/guard.ts"],"sourcesContent":["export { ExGuardModule } from './module.js';\nexport { ExGuardPermissionGuard, RequirePermissions, RequireRoles, clearExGuardCache, EXGUARD_PERMISSIONS_KEY, EXGUARD_ROLES_KEY } from './guard.js';\n","import { Module, Global, DynamicModule } from '@nestjs/common';\n\nexport interface ExGuardModuleOptions {\n baseUrl: string;\n}\n\n@Global()\n@Module({})\nexport class ExGuardModule {\n static forRoot(options: ExGuardModuleOptions): DynamicModule;\n static forRoot(baseUrl: string): DynamicModule;\n static forRoot(optionsOrUrl: ExGuardModuleOptions | string): DynamicModule {\n const baseUrl = typeof optionsOrUrl === 'string' ? optionsOrUrl : optionsOrUrl.baseUrl;\n \n return {\n module: ExGuardModule,\n providers: [\n {\n provide: 'EXGUARD_BASE_URL',\n useValue: baseUrl,\n },\n ],\n exports: ['EXGUARD_BASE_URL'],\n };\n }\n}\n","import { Injectable, CanActivate, ExecutionContext, UnauthorizedException, ForbiddenException, Inject, Optional, SetMetadata } from '@nestjs/common';\nimport { Reflector } from '@nestjs/core';\n\nexport const EXGUARD_PERMISSIONS_KEY = 'exguard_permissions';\nexport const EXGUARD_ROLES_KEY = 'exguard_roles';\nexport const EXGUARD_PERMISSIONS_ALL_KEY = 'exguard_permissions_all';\nexport const EXGUARD_ROLES_ALL_KEY = 'exguard_roles_all';\n\nexport const RequirePermissions = (permissions: string[]) => SetMetadata(EXGUARD_PERMISSIONS_KEY, permissions);\nexport const RequireRoles = (roles: string[]) => SetMetadata(EXGUARD_ROLES_KEY, roles);\nexport const RequirePermissionsAll = () => SetMetadata(EXGUARD_PERMISSIONS_ALL_KEY, true);\nexport const RequireRolesAll = () => SetMetadata(EXGUARD_ROLES_ALL_KEY, true);\n\nconst cache = new Map<string, { data: any; timestamp: number }>();\nconst CACHE_TTL = 5 * 60 * 1000; // 5 minutes\n\nfunction getCached(key: string): any {\n const entry = cache.get(key);\n if (!entry) return null;\n if (Date.now() - entry.timestamp > CACHE_TTL) {\n cache.delete(key);\n return null;\n }\n return entry.data;\n}\n\nfunction setCache(key: string, data: any): void {\n cache.set(key, { data, timestamp: Date.now() });\n}\n\n@Injectable()\nexport class ExGuardPermissionGuard implements CanActivate {\n constructor(\n @Optional() @Inject('EXGUARD_BASE_URL') private baseUrl: string,\n private reflector: Reflector,\n ) {}\n\n async canActivate(context: ExecutionContext): Promise<boolean> {\n const request = context.switchToHttp().getRequest();\n const token = this.extractToken(request);\n\n if (!token) {\n throw new UnauthorizedException('No token provided');\n }\n\n if (!this.baseUrl) {\n console.warn('[ExGuard] Base URL not configured. Access denied.');\n return false;\n }\n\n // Check cache first\n const cacheKey = `auth:${token}`;\n let authResult = getCached(cacheKey);\n\n if (!authResult) {\n try {\n // Verify token\n const verifyResponse = await fetch(`${this.baseUrl}/guard/verify-token`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${token}`,\n },\n body: JSON.stringify({ id_token: token }),\n });\n\n if (!verifyResponse.ok) {\n throw new ForbiddenException('Invalid token');\n }\n\n // Get user access\n const meResponse = await fetch(`${this.baseUrl}/guard/me`, {\n method: 'GET',\n headers: {\n 'Authorization': `Bearer ${token}`,\n },\n });\n\n if (!meResponse.ok) {\n throw new ForbiddenException('Failed to get user access');\n }\n\n const meData = await meResponse.json();\n authResult = { allowed: true, user: meData.data || meData };\n \n console.log('[ExGuard DEBUG] Full user object:', JSON.stringify(authResult.user, null, 2));\n \n // Cache the result\n setCache(cacheKey, authResult);\n } catch (error) {\n console.error('[ExGuard] Auth error:', error);\n throw new ForbiddenException('Authentication failed');\n }\n }\n\n if (!authResult.allowed) {\n throw new ForbiddenException(authResult.error || 'Access denied');\n }\n\n if (!authResult.user) {\n throw new ForbiddenException('User not found');\n }\n\n const handler = context.getHandler();\n const permMeta = this.reflector.get(EXGUARD_PERMISSIONS_KEY, handler);\n\n console.log('[ExGuard DEBUG] permMeta from reflector:', permMeta);\n console.log('[ExGuard DEBUG] handler:', handler);\n console.log('[ExGuard DEBUG] handler name:', handler.name);\n\n if (permMeta) {\n const permissions = Array.isArray(permMeta) ? permMeta : permMeta.permissions;\n const requireAll = permMeta.requireAll ?? this.reflector.get(EXGUARD_PERMISSIONS_ALL_KEY, handler) ?? false;\n const userPermissions = authResult.user.modules ? authResult.user.modules.flatMap((m: any) => m.permissions) : [];\n\n console.log('[ExGuard DEBUG] Required permissions:', permissions);\n console.log('[ExGuard DEBUG] User permissions:', userPermissions);\n console.log('[ExGuard DEBUG] requireAll:', requireAll);\n\n if (requireAll) {\n if (!permissions.every((p: string) => userPermissions.includes(p))) {\n throw new ForbiddenException('Insufficient permissions');\n }\n } else {\n const hasPermission = permissions.some((p: string) => userPermissions.includes(p));\n console.log('[ExGuard DEBUG] Permission check result:', hasPermission);\n if (!hasPermission) {\n throw new ForbiddenException('Insufficient permissions');\n }\n }\n }\n\n const roleMeta = this.reflector.get(EXGUARD_ROLES_KEY, handler);\n\n if (roleMeta) {\n const roles = Array.isArray(roleMeta) ? roleMeta : roleMeta.roles;\n const requireAll = roleMeta.requireAll ?? this.reflector.get(EXGUARD_ROLES_ALL_KEY, handler) ?? false;\n const userRoles = authResult.user.roles || [];\n\n if (requireAll) {\n if (!roles.every((r: string) => userRoles.includes(r))) {\n throw new ForbiddenException('Insufficient roles');\n }\n } else {\n if (!roles.some((r: string) => userRoles.includes(r))) {\n throw new ForbiddenException('Insufficient roles');\n }\n }\n }\n\n request.user = authResult.user;\n return true;\n }\n\n private extractToken(request: any): string | null {\n const auth = request.headers?.authorization;\n if (auth?.startsWith('Bearer ')) {\n return auth.substring(7);\n }\n return request.headers?.['x-access-token'] || null;\n }\n}\n\nexport function clearExGuardCache(): void {\n cache.clear();\n}\n"],"mappings":"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","names":["import_common"]}
package/dist/index.d.cts CHANGED
@@ -1,3 +1,4 @@
1
+ import * as _nestjs_common from '@nestjs/common';
1
2
  import { DynamicModule, CanActivate, ExecutionContext } from '@nestjs/common';
2
3
  import { Reflector } from '@nestjs/core';
3
4
 
@@ -11,6 +12,8 @@ declare class ExGuardModule {
11
12
 
12
13
  declare const EXGUARD_PERMISSIONS_KEY = "exguard_permissions";
13
14
  declare const EXGUARD_ROLES_KEY = "exguard_roles";
15
+ declare const RequirePermissions: (permissions: string[]) => _nestjs_common.CustomDecorator<string>;
16
+ declare const RequireRoles: (roles: string[]) => _nestjs_common.CustomDecorator<string>;
14
17
  declare class ExGuardPermissionGuard implements CanActivate {
15
18
  private baseUrl;
16
19
  private reflector;
@@ -18,8 +21,6 @@ declare class ExGuardPermissionGuard implements CanActivate {
18
21
  canActivate(context: ExecutionContext): Promise<boolean>;
19
22
  private extractToken;
20
23
  }
21
- declare function RequirePermissions(permissions: string[], requireAll?: boolean): (target: any, propertyKey: string, descriptor: PropertyDescriptor) => PropertyDescriptor;
22
- declare function RequireRoles(roles: string[], requireAll?: boolean): (target: any, propertyKey: string, descriptor: PropertyDescriptor) => PropertyDescriptor;
23
24
  declare function clearExGuardCache(): void;
24
25
 
25
26
  export { EXGUARD_PERMISSIONS_KEY, EXGUARD_ROLES_KEY, ExGuardModule, ExGuardPermissionGuard, RequirePermissions, RequireRoles, clearExGuardCache };
package/dist/index.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import * as _nestjs_common from '@nestjs/common';
1
2
  import { DynamicModule, CanActivate, ExecutionContext } from '@nestjs/common';
2
3
  import { Reflector } from '@nestjs/core';
3
4
 
@@ -11,6 +12,8 @@ declare class ExGuardModule {
11
12
 
12
13
  declare const EXGUARD_PERMISSIONS_KEY = "exguard_permissions";
13
14
  declare const EXGUARD_ROLES_KEY = "exguard_roles";
15
+ declare const RequirePermissions: (permissions: string[]) => _nestjs_common.CustomDecorator<string>;
16
+ declare const RequireRoles: (roles: string[]) => _nestjs_common.CustomDecorator<string>;
14
17
  declare class ExGuardPermissionGuard implements CanActivate {
15
18
  private baseUrl;
16
19
  private reflector;
@@ -18,8 +21,6 @@ declare class ExGuardPermissionGuard implements CanActivate {
18
21
  canActivate(context: ExecutionContext): Promise<boolean>;
19
22
  private extractToken;
20
23
  }
21
- declare function RequirePermissions(permissions: string[], requireAll?: boolean): (target: any, propertyKey: string, descriptor: PropertyDescriptor) => PropertyDescriptor;
22
- declare function RequireRoles(roles: string[], requireAll?: boolean): (target: any, propertyKey: string, descriptor: PropertyDescriptor) => PropertyDescriptor;
23
24
  declare function clearExGuardCache(): void;
24
25
 
25
26
  export { EXGUARD_PERMISSIONS_KEY, EXGUARD_ROLES_KEY, ExGuardModule, ExGuardPermissionGuard, RequirePermissions, RequireRoles, clearExGuardCache };
package/dist/index.js CHANGED
@@ -33,9 +33,13 @@ ExGuardModule = __decorateClass([
33
33
  ], ExGuardModule);
34
34
 
35
35
  // src/guard.ts
36
- import { Injectable, UnauthorizedException, ForbiddenException, Inject, Optional } from "@nestjs/common";
36
+ import { Injectable, UnauthorizedException, ForbiddenException, Inject, Optional, SetMetadata } from "@nestjs/common";
37
37
  var EXGUARD_PERMISSIONS_KEY = "exguard_permissions";
38
38
  var EXGUARD_ROLES_KEY = "exguard_roles";
39
+ var EXGUARD_PERMISSIONS_ALL_KEY = "exguard_permissions_all";
40
+ var EXGUARD_ROLES_ALL_KEY = "exguard_roles_all";
41
+ var RequirePermissions = (permissions) => SetMetadata(EXGUARD_PERMISSIONS_KEY, permissions);
42
+ var RequireRoles = (roles) => SetMetadata(EXGUARD_ROLES_KEY, roles);
39
43
  var cache = /* @__PURE__ */ new Map();
40
44
  var CACHE_TTL = 5 * 60 * 1e3;
41
45
  function getCached(key) {
@@ -91,6 +95,7 @@ var ExGuardPermissionGuard = class {
91
95
  }
92
96
  const meData = await meResponse.json();
93
97
  authResult = { allowed: true, user: meData.data || meData };
98
+ console.log("[ExGuard DEBUG] Full user object:", JSON.stringify(authResult.user, null, 2));
94
99
  setCache(cacheKey, authResult);
95
100
  } catch (error) {
96
101
  console.error("[ExGuard] Auth error:", error);
@@ -105,22 +110,32 @@ var ExGuardPermissionGuard = class {
105
110
  }
106
111
  const handler = context.getHandler();
107
112
  const permMeta = this.reflector.get(EXGUARD_PERMISSIONS_KEY, handler);
113
+ console.log("[ExGuard DEBUG] permMeta from reflector:", permMeta);
114
+ console.log("[ExGuard DEBUG] handler:", handler);
115
+ console.log("[ExGuard DEBUG] handler name:", handler.name);
108
116
  if (permMeta) {
109
- const { permissions, requireAll } = permMeta;
117
+ const permissions = Array.isArray(permMeta) ? permMeta : permMeta.permissions;
118
+ const requireAll = permMeta.requireAll ?? this.reflector.get(EXGUARD_PERMISSIONS_ALL_KEY, handler) ?? false;
110
119
  const userPermissions = authResult.user.modules ? authResult.user.modules.flatMap((m) => m.permissions) : [];
120
+ console.log("[ExGuard DEBUG] Required permissions:", permissions);
121
+ console.log("[ExGuard DEBUG] User permissions:", userPermissions);
122
+ console.log("[ExGuard DEBUG] requireAll:", requireAll);
111
123
  if (requireAll) {
112
124
  if (!permissions.every((p) => userPermissions.includes(p))) {
113
125
  throw new ForbiddenException("Insufficient permissions");
114
126
  }
115
127
  } else {
116
- if (!permissions.some((p) => userPermissions.includes(p))) {
128
+ const hasPermission = permissions.some((p) => userPermissions.includes(p));
129
+ console.log("[ExGuard DEBUG] Permission check result:", hasPermission);
130
+ if (!hasPermission) {
117
131
  throw new ForbiddenException("Insufficient permissions");
118
132
  }
119
133
  }
120
134
  }
121
135
  const roleMeta = this.reflector.get(EXGUARD_ROLES_KEY, handler);
122
136
  if (roleMeta) {
123
- const { roles, requireAll } = roleMeta;
137
+ const roles = Array.isArray(roleMeta) ? roleMeta : roleMeta.roles;
138
+ const requireAll = roleMeta.requireAll ?? this.reflector.get(EXGUARD_ROLES_ALL_KEY, handler) ?? false;
124
139
  const userRoles = authResult.user.roles || [];
125
140
  if (requireAll) {
126
141
  if (!roles.every((r) => userRoles.includes(r))) {
@@ -148,18 +163,6 @@ ExGuardPermissionGuard = __decorateClass([
148
163
  __decorateParam(0, Optional()),
149
164
  __decorateParam(0, Inject("EXGUARD_BASE_URL"))
150
165
  ], ExGuardPermissionGuard);
151
- function RequirePermissions(permissions, requireAll = false) {
152
- return function(target, propertyKey, descriptor) {
153
- Reflect.defineMetadata(EXGUARD_PERMISSIONS_KEY, { permissions, requireAll }, descriptor.value);
154
- return descriptor;
155
- };
156
- }
157
- function RequireRoles(roles, requireAll = false) {
158
- return function(target, propertyKey, descriptor) {
159
- Reflect.defineMetadata(EXGUARD_ROLES_KEY, { roles, requireAll }, descriptor.value);
160
- return descriptor;
161
- };
162
- }
163
166
  function clearExGuardCache() {
164
167
  cache.clear();
165
168
  }
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/module.ts","../src/guard.ts"],"sourcesContent":["import { Module, Global, DynamicModule } from '@nestjs/common';\n\nexport interface ExGuardModuleOptions {\n baseUrl: string;\n}\n\n@Global()\n@Module({})\nexport class ExGuardModule {\n static forRoot(options: ExGuardModuleOptions): DynamicModule;\n static forRoot(baseUrl: string): DynamicModule;\n static forRoot(optionsOrUrl: ExGuardModuleOptions | string): DynamicModule {\n const baseUrl = typeof optionsOrUrl === 'string' ? optionsOrUrl : optionsOrUrl.baseUrl;\n \n return {\n module: ExGuardModule,\n providers: [\n {\n provide: 'EXGUARD_BASE_URL',\n useValue: baseUrl,\n },\n ],\n exports: ['EXGUARD_BASE_URL'],\n };\n }\n}\n","import { Injectable, CanActivate, ExecutionContext, UnauthorizedException, ForbiddenException, Inject, Optional } from '@nestjs/common';\nimport { Reflector } from '@nestjs/core';\n\nexport const EXGUARD_PERMISSIONS_KEY = 'exguard_permissions';\nexport const EXGUARD_ROLES_KEY = 'exguard_roles';\n\nconst cache = new Map<string, { data: any; timestamp: number }>();\nconst CACHE_TTL = 5 * 60 * 1000; // 5 minutes\n\nfunction getCached(key: string): any {\n const entry = cache.get(key);\n if (!entry) return null;\n if (Date.now() - entry.timestamp > CACHE_TTL) {\n cache.delete(key);\n return null;\n }\n return entry.data;\n}\n\nfunction setCache(key: string, data: any): void {\n cache.set(key, { data, timestamp: Date.now() });\n}\n\n@Injectable()\nexport class ExGuardPermissionGuard implements CanActivate {\n constructor(\n @Optional() @Inject('EXGUARD_BASE_URL') private baseUrl: string,\n private reflector: Reflector,\n ) {}\n\n async canActivate(context: ExecutionContext): Promise<boolean> {\n const request = context.switchToHttp().getRequest();\n const token = this.extractToken(request);\n\n if (!token) {\n throw new UnauthorizedException('No token provided');\n }\n\n if (!this.baseUrl) {\n console.warn('[ExGuard] Base URL not configured. Access denied.');\n return false;\n }\n\n // Check cache first\n const cacheKey = `auth:${token}`;\n let authResult = getCached(cacheKey);\n\n if (!authResult) {\n try {\n // Verify token\n const verifyResponse = await fetch(`${this.baseUrl}/guard/verify-token`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${token}`,\n },\n body: JSON.stringify({ id_token: token }),\n });\n\n if (!verifyResponse.ok) {\n throw new ForbiddenException('Invalid token');\n }\n\n // Get user access\n const meResponse = await fetch(`${this.baseUrl}/guard/me`, {\n method: 'GET',\n headers: {\n 'Authorization': `Bearer ${token}`,\n },\n });\n\n if (!meResponse.ok) {\n throw new ForbiddenException('Failed to get user access');\n }\n\n const meData = await meResponse.json();\n authResult = { allowed: true, user: meData.data || meData };\n \n // Cache the result\n setCache(cacheKey, authResult);\n } catch (error) {\n console.error('[ExGuard] Auth error:', error);\n throw new ForbiddenException('Authentication failed');\n }\n }\n\n if (!authResult.allowed) {\n throw new ForbiddenException(authResult.error || 'Access denied');\n }\n\n if (!authResult.user) {\n throw new ForbiddenException('User not found');\n }\n\n const handler = context.getHandler();\n const permMeta = this.reflector.get(EXGUARD_PERMISSIONS_KEY, handler);\n\n if (permMeta) {\n const { permissions, requireAll } = permMeta;\n const userPermissions = authResult.user.modules ? authResult.user.modules.flatMap((m: any) => m.permissions) : [];\n\n if (requireAll) {\n if (!permissions.every((p: string) => userPermissions.includes(p))) {\n throw new ForbiddenException('Insufficient permissions');\n }\n } else {\n if (!permissions.some((p: string) => userPermissions.includes(p))) {\n throw new ForbiddenException('Insufficient permissions');\n }\n }\n }\n\n const roleMeta = this.reflector.get(EXGUARD_ROLES_KEY, handler);\n\n if (roleMeta) {\n const { roles, requireAll } = roleMeta;\n const userRoles = authResult.user.roles || [];\n\n if (requireAll) {\n if (!roles.every((r: string) => userRoles.includes(r))) {\n throw new ForbiddenException('Insufficient roles');\n }\n } else {\n if (!roles.some((r: string) => userRoles.includes(r))) {\n throw new ForbiddenException('Insufficient roles');\n }\n }\n }\n\n request.user = authResult.user;\n return true;\n }\n\n private extractToken(request: any): string | null {\n const auth = request.headers?.authorization;\n if (auth?.startsWith('Bearer ')) {\n return auth.substring(7);\n }\n return request.headers?.['x-access-token'] || null;\n }\n}\n\nexport function RequirePermissions(permissions: string[], requireAll = false) {\n return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {\n Reflect.defineMetadata(EXGUARD_PERMISSIONS_KEY, { permissions, requireAll }, descriptor.value);\n return descriptor;\n };\n}\n\nexport function RequireRoles(roles: string[], requireAll = false) {\n return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {\n Reflect.defineMetadata(EXGUARD_ROLES_KEY, { roles, requireAll }, descriptor.value);\n return descriptor;\n };\n}\n\nexport function clearExGuardCache(): void {\n cache.clear();\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/module.ts","../src/guard.ts"],"sourcesContent":["import { Module, Global, DynamicModule } from '@nestjs/common';\n\nexport interface ExGuardModuleOptions {\n baseUrl: string;\n}\n\n@Global()\n@Module({})\nexport class ExGuardModule {\n static forRoot(options: ExGuardModuleOptions): DynamicModule;\n static forRoot(baseUrl: string): DynamicModule;\n static forRoot(optionsOrUrl: ExGuardModuleOptions | string): DynamicModule {\n const baseUrl = typeof optionsOrUrl === 'string' ? optionsOrUrl : optionsOrUrl.baseUrl;\n \n return {\n module: ExGuardModule,\n providers: [\n {\n provide: 'EXGUARD_BASE_URL',\n useValue: baseUrl,\n },\n ],\n exports: ['EXGUARD_BASE_URL'],\n };\n }\n}\n","import { Injectable, CanActivate, ExecutionContext, UnauthorizedException, ForbiddenException, Inject, Optional, SetMetadata } from '@nestjs/common';\nimport { Reflector } from '@nestjs/core';\n\nexport const EXGUARD_PERMISSIONS_KEY = 'exguard_permissions';\nexport const EXGUARD_ROLES_KEY = 'exguard_roles';\nexport const EXGUARD_PERMISSIONS_ALL_KEY = 'exguard_permissions_all';\nexport const EXGUARD_ROLES_ALL_KEY = 'exguard_roles_all';\n\nexport const RequirePermissions = (permissions: string[]) => SetMetadata(EXGUARD_PERMISSIONS_KEY, permissions);\nexport const RequireRoles = (roles: string[]) => SetMetadata(EXGUARD_ROLES_KEY, roles);\nexport const RequirePermissionsAll = () => SetMetadata(EXGUARD_PERMISSIONS_ALL_KEY, true);\nexport const RequireRolesAll = () => SetMetadata(EXGUARD_ROLES_ALL_KEY, true);\n\nconst cache = new Map<string, { data: any; timestamp: number }>();\nconst CACHE_TTL = 5 * 60 * 1000; // 5 minutes\n\nfunction getCached(key: string): any {\n const entry = cache.get(key);\n if (!entry) return null;\n if (Date.now() - entry.timestamp > CACHE_TTL) {\n cache.delete(key);\n return null;\n }\n return entry.data;\n}\n\nfunction setCache(key: string, data: any): void {\n cache.set(key, { data, timestamp: Date.now() });\n}\n\n@Injectable()\nexport class ExGuardPermissionGuard implements CanActivate {\n constructor(\n @Optional() @Inject('EXGUARD_BASE_URL') private baseUrl: string,\n private reflector: Reflector,\n ) {}\n\n async canActivate(context: ExecutionContext): Promise<boolean> {\n const request = context.switchToHttp().getRequest();\n const token = this.extractToken(request);\n\n if (!token) {\n throw new UnauthorizedException('No token provided');\n }\n\n if (!this.baseUrl) {\n console.warn('[ExGuard] Base URL not configured. Access denied.');\n return false;\n }\n\n // Check cache first\n const cacheKey = `auth:${token}`;\n let authResult = getCached(cacheKey);\n\n if (!authResult) {\n try {\n // Verify token\n const verifyResponse = await fetch(`${this.baseUrl}/guard/verify-token`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${token}`,\n },\n body: JSON.stringify({ id_token: token }),\n });\n\n if (!verifyResponse.ok) {\n throw new ForbiddenException('Invalid token');\n }\n\n // Get user access\n const meResponse = await fetch(`${this.baseUrl}/guard/me`, {\n method: 'GET',\n headers: {\n 'Authorization': `Bearer ${token}`,\n },\n });\n\n if (!meResponse.ok) {\n throw new ForbiddenException('Failed to get user access');\n }\n\n const meData = await meResponse.json();\n authResult = { allowed: true, user: meData.data || meData };\n \n console.log('[ExGuard DEBUG] Full user object:', JSON.stringify(authResult.user, null, 2));\n \n // Cache the result\n setCache(cacheKey, authResult);\n } catch (error) {\n console.error('[ExGuard] Auth error:', error);\n throw new ForbiddenException('Authentication failed');\n }\n }\n\n if (!authResult.allowed) {\n throw new ForbiddenException(authResult.error || 'Access denied');\n }\n\n if (!authResult.user) {\n throw new ForbiddenException('User not found');\n }\n\n const handler = context.getHandler();\n const permMeta = this.reflector.get(EXGUARD_PERMISSIONS_KEY, handler);\n\n console.log('[ExGuard DEBUG] permMeta from reflector:', permMeta);\n console.log('[ExGuard DEBUG] handler:', handler);\n console.log('[ExGuard DEBUG] handler name:', handler.name);\n\n if (permMeta) {\n const permissions = Array.isArray(permMeta) ? permMeta : permMeta.permissions;\n const requireAll = permMeta.requireAll ?? this.reflector.get(EXGUARD_PERMISSIONS_ALL_KEY, handler) ?? false;\n const userPermissions = authResult.user.modules ? authResult.user.modules.flatMap((m: any) => m.permissions) : [];\n\n console.log('[ExGuard DEBUG] Required permissions:', permissions);\n console.log('[ExGuard DEBUG] User permissions:', userPermissions);\n console.log('[ExGuard DEBUG] requireAll:', requireAll);\n\n if (requireAll) {\n if (!permissions.every((p: string) => userPermissions.includes(p))) {\n throw new ForbiddenException('Insufficient permissions');\n }\n } else {\n const hasPermission = permissions.some((p: string) => userPermissions.includes(p));\n console.log('[ExGuard DEBUG] Permission check result:', hasPermission);\n if (!hasPermission) {\n throw new ForbiddenException('Insufficient permissions');\n }\n }\n }\n\n const roleMeta = this.reflector.get(EXGUARD_ROLES_KEY, handler);\n\n if (roleMeta) {\n const roles = Array.isArray(roleMeta) ? roleMeta : roleMeta.roles;\n const requireAll = roleMeta.requireAll ?? this.reflector.get(EXGUARD_ROLES_ALL_KEY, handler) ?? false;\n const userRoles = authResult.user.roles || [];\n\n if (requireAll) {\n if (!roles.every((r: string) => userRoles.includes(r))) {\n throw new ForbiddenException('Insufficient roles');\n }\n } else {\n if (!roles.some((r: string) => userRoles.includes(r))) {\n throw new ForbiddenException('Insufficient roles');\n }\n }\n }\n\n request.user = authResult.user;\n return true;\n }\n\n private extractToken(request: any): string | null {\n const auth = request.headers?.authorization;\n if (auth?.startsWith('Bearer ')) {\n return auth.substring(7);\n }\n return request.headers?.['x-access-token'] || null;\n }\n}\n\nexport function clearExGuardCache(): void {\n cache.clear();\n}\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "exguard-endpoint",
3
- "version": "1.0.8",
3
+ "version": "1.0.9",
4
4
  "private": false,
5
5
  "publishConfig": {
6
6
  "access": "public"
package/dist/setup.cjs DELETED
@@ -1,303 +0,0 @@
1
- const fs = require('fs');
2
- const path = require('path');
3
-
4
- const GUARD_CONTENT = `import { Injectable, CanActivate, ExecutionContext, UnauthorizedException, ForbiddenException, Inject, Optional } from '@nestjs/common';
5
- import { Reflector } from '@nestjs/core';
6
-
7
- export const EXGUARD_PERMISSIONS_KEY = 'exguard_permissions';
8
- export const EXGUARD_ROLES_KEY = 'exguard_roles';
9
-
10
- const cache = new Map();
11
- const CACHE_TTL = 5 * 60 * 1000;
12
-
13
- function getCached(key) {
14
- const entry = cache.get(key);
15
- if (!entry) return null;
16
- if (Date.now() - entry.timestamp > CACHE_TTL) {
17
- cache.delete(key);
18
- return null;
19
- }
20
- return entry.data;
21
- }
22
-
23
- function setCache(key, data) {
24
- cache.set(key, { data, timestamp: Date.now() });
25
- }
26
-
27
- @Injectable()
28
- export class ExGuardPermissionGuard {
29
- constructor(
30
- @Optional() @Inject('EXGUARD_BASE_URL') private baseUrl: string,
31
- private reflector: Reflector,
32
- ) {}
33
-
34
- async canActivate(context) {
35
- const request = context.switchToHttp().getRequest();
36
- const token = this.extractToken(request);
37
-
38
- if (!token) {
39
- throw new UnauthorizedException('No token provided');
40
- }
41
-
42
- if (!this.baseUrl) {
43
- console.warn('[ExGuard] Base URL not configured. Access denied.');
44
- return false;
45
- }
46
-
47
- const cacheKey = 'auth:' + token;
48
- let authResult = getCached(cacheKey);
49
-
50
- if (!authResult) {
51
- try {
52
- const verifyResponse = await fetch(this.baseUrl + '/guard/verify-token', {
53
- method: 'POST',
54
- headers: {
55
- 'Content-Type': 'application/json',
56
- 'Authorization': 'Bearer ' + token,
57
- },
58
- body: JSON.stringify({ id_token: token }),
59
- });
60
-
61
- if (!verifyResponse.ok) {
62
- throw new ForbiddenException('Invalid token');
63
- }
64
-
65
- const meResponse = await fetch(this.baseUrl + '/guard/me', {
66
- method: 'GET',
67
- headers: {
68
- 'Authorization': 'Bearer ' + token,
69
- },
70
- });
71
-
72
- if (!meResponse.ok) {
73
- throw new ForbiddenException('Failed to get user access');
74
- }
75
-
76
- const meData = await meResponse.json();
77
- authResult = { allowed: true, user: meData.data || meData };
78
- setCache(cacheKey, authResult);
79
- } catch (error) {
80
- console.error('[ExGuard] Auth error:', error);
81
- throw new ForbiddenException('Authentication failed');
82
- }
83
- }
84
-
85
- if (!authResult.allowed) {
86
- throw new ForbiddenException(authResult.error || 'Access denied');
87
- }
88
-
89
- if (!authResult.user) {
90
- throw new ForbiddenException('User not found');
91
- }
92
-
93
- const handler = context.getHandler();
94
- const permMeta = this.reflector.get(EXGUARD_PERMISSIONS_KEY, handler);
95
-
96
- if (permMeta) {
97
- const { permissions, requireAll } = permMeta;
98
- const userPermissions = authResult.user.modules ? authResult.user.modules.flatMap(m => m.permissions) : [];
99
-
100
- if (requireAll) {
101
- if (!permissions.every(p => userPermissions.includes(p))) {
102
- throw new ForbiddenException('Insufficient permissions');
103
- }
104
- } else {
105
- if (!permissions.some(p => userPermissions.includes(p))) {
106
- throw new ForbiddenException('Insufficient permissions');
107
- }
108
- }
109
- }
110
-
111
- const roleMeta = this.reflector.get(EXGUARD_ROLES_KEY, handler);
112
-
113
- if (roleMeta) {
114
- const { roles, requireAll } = roleMeta;
115
- const userRoles = authResult.user.roles || [];
116
-
117
- if (requireAll) {
118
- if (!roles.every(r => userRoles.includes(r))) {
119
- throw new ForbiddenException('Insufficient roles');
120
- }
121
- } else {
122
- if (!roles.some(r => userRoles.includes(r))) {
123
- throw new ForbiddenException('Insufficient roles');
124
- }
125
- }
126
- }
127
-
128
- request.user = authResult.user;
129
- return true;
130
- }
131
-
132
- extractToken(request) {
133
- const auth = request.headers ? request.headers.authorization : null;
134
- if (auth && auth.startsWith('Bearer ')) {
135
- return auth.substring(7);
136
- }
137
- return request.headers ? request.headers['x-access-token'] : null;
138
- }
139
- }
140
-
141
- export function RequirePermissions(permissions, requireAll = false) {
142
- return function (target, propertyKey, descriptor) {
143
- Reflect.defineMetadata(EXGUARD_PERMISSIONS_KEY, { permissions, requireAll }, descriptor.value);
144
- return descriptor;
145
- };
146
- }
147
-
148
- export function RequireRoles(roles, requireAll = false) {
149
- return function (target, propertyKey, descriptor) {
150
- Reflect.defineMetadata(EXGUARD_ROLES_KEY, { roles, requireAll }, descriptor.value);
151
- return descriptor;
152
- };
153
- }
154
-
155
- export function clearExGuardCache() {
156
- cache.clear();
157
- }
158
- `;
159
-
160
- const MODULE_CONTENT = `import { Module, Global, DynamicModule } from '@nestjs/common';
161
-
162
- @Global()
163
- @Module({})
164
- export class ExGuardModule {
165
- static forRoot(baseUrl: string): DynamicModule {
166
- return {
167
- module: ExGuardModule,
168
- providers: [
169
- {
170
- provide: 'EXGUARD_BASE_URL',
171
- useValue: baseUrl,
172
- },
173
- ],
174
- exports: ['EXGUARD_BASE_URL'],
175
- };
176
- }
177
- }
178
- `;
179
-
180
- function ensureDir(dirPath) {
181
- if (!fs.existsSync(dirPath)) {
182
- fs.mkdirSync(dirPath, { recursive: true });
183
- }
184
- }
185
-
186
- function writeFile(filePath, content) {
187
- const dir = path.dirname(filePath);
188
- ensureDir(dir);
189
- fs.writeFileSync(filePath, content);
190
- console.log('Created: ' + filePath);
191
- }
192
-
193
- function updateAppModule(baseUrl) {
194
- const appModulePath = path.join(process.cwd(), 'src/app.module.ts');
195
-
196
- if (!fs.existsSync(appModulePath)) {
197
- console.log('app.module.ts not found. Skipping auto-configure.');
198
- return;
199
- }
200
-
201
- let content = fs.readFileSync(appModulePath, 'utf8');
202
-
203
- // Check if already configured
204
- if (content.includes('ExGuardModule')) {
205
- console.log('ExGuardModule already configured in app.module.ts');
206
- return;
207
- }
208
-
209
- // Add import
210
- const importStatement = "import { ExGuardModule } from './exguard/exguard.module';";
211
-
212
- if (!content.includes(importStatement)) {
213
- // Find the last import
214
- const importMatch = content.match(/^import .+$/gm);
215
- if (importMatch) {
216
- const lastImport = importMatch[importMatch.length - 1];
217
- content = content.replace(lastImport, lastImport + '\n' + importStatement);
218
- }
219
- }
220
-
221
- // Add to imports array
222
- if (content.includes('imports: [')) {
223
- content = content.replace(
224
- 'imports: [',
225
- "imports: [\n ExGuardModule.forRoot(process.env.EXGUARD_BASE_URL || '" + baseUrl + "'),"
226
- );
227
- }
228
-
229
- fs.writeFileSync(appModulePath, content);
230
- console.log('Updated app.module.ts with ExGuardModule');
231
- }
232
-
233
- function updateEnvFile() {
234
- const envPath = path.join(process.cwd(), '.env');
235
- const envExamplePath = path.join(process.cwd(), '.env.example');
236
-
237
- const envContent = `# ExGuard Configuration
238
- EXGUARD_BASE_URL=http://localhost:3000
239
- `;
240
-
241
- if (!fs.existsSync(envPath) && !fs.existsSync(envExamplePath)) {
242
- fs.writeFileSync(envExamplePath, envContent);
243
- console.log('Created .env.example with EXGUARD_BASE_URL');
244
- }
245
- }
246
-
247
- function setup() {
248
- const srcDir = path.join(process.cwd(), 'src');
249
- const baseUrl = process.env.EXGUARD_BASE_URL || 'http://localhost:3000';
250
-
251
- if (!fs.existsSync(srcDir)) {
252
- console.error('Error: Run this command in your NestJS project root');
253
- process.exit(1);
254
- }
255
-
256
- const exguardDir = path.join(srcDir, 'exguard');
257
- ensureDir(exguardDir);
258
-
259
- console.log('');
260
- console.log('===========================================');
261
- console.log('🔧 Setting up ExGuard...');
262
- console.log('===========================================');
263
-
264
- writeFile(path.join(exguardDir, 'exguard.guard.ts'), GUARD_CONTENT);
265
- writeFile(path.join(exguardDir, 'exguard.module.ts'), MODULE_CONTENT);
266
-
267
- updateEnvFile();
268
- updateAppModule(baseUrl);
269
-
270
- console.log('');
271
- console.log('✅ ExGuard Setup Complete!');
272
- console.log('===========================================');
273
- console.log('');
274
- console.log('Environment:');
275
- console.log(' EXGUARD_BASE_URL=' + baseUrl);
276
- console.log('');
277
- console.log('Usage in controllers:');
278
- console.log('');
279
- console.log(' import { ExGuardPermissionGuard, RequirePermissions } from "./exguard/exguard.guard";');
280
- console.log('');
281
- console.log(' @Controller("items")');
282
- console.log(' @UseGuards(ExGuardPermissionGuard)');
283
- console.log(' export class ItemsController {');
284
- console.log(' @Get() @RequirePermissions(["item:read"]) findAll() {}');
285
- console.log(' }');
286
- console.log('');
287
- console.log('💡 Caching enabled (5 min TTL)');
288
- console.log('===========================================');
289
- }
290
-
291
- const args = process.argv.slice(2);
292
-
293
- if (args[0] === 'setup') {
294
- setup();
295
- } else {
296
- console.log('===========================================');
297
- console.log('ExGuard Endpoint - NestJS RBAC Guard');
298
- console.log('===========================================');
299
- console.log('');
300
- console.log('To setup in your NestJS project:');
301
- console.log(' npx exguard-endpoint setup');
302
- console.log('');
303
- }