exguard-backend 1.0.3 ā 1.0.5
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/README.md +67 -5
- package/dist/index.d.cts +4 -21
- package/dist/index.d.ts +4 -21
- package/package.json +8 -2
- package/scripts/create-example.js +201 -0
- package/scripts/setup-nestjs.js +535 -0
package/README.md
CHANGED
|
@@ -25,13 +25,40 @@ pnpm add exguard-backend
|
|
|
25
25
|
|
|
26
26
|
## š Quick Start - NestJS Integration
|
|
27
27
|
|
|
28
|
-
###
|
|
28
|
+
### šÆ Option 1: Automatic Setup (Recommended)
|
|
29
|
+
|
|
30
|
+
**One-command setup for NestJS projects:**
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
# Install exguard-backend
|
|
34
|
+
npm install exguard-backend
|
|
35
|
+
|
|
36
|
+
# Run automatic setup
|
|
37
|
+
npx exguard-backend setup-nestjs
|
|
38
|
+
# OR
|
|
39
|
+
npm run setup-nestjs
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
**What the automatic setup creates:**
|
|
43
|
+
|
|
44
|
+
ā
**ExGuard Module** - Global module with Guard provider
|
|
45
|
+
ā
**Custom Guards** - ExGuardNestGuard, PermissionGuard, RoleGuard
|
|
46
|
+
ā
**Decorators** - @RequirePermissions, @RequireRoles, @RequireModules
|
|
47
|
+
ā
**Example Controller** - Complete usage examples
|
|
48
|
+
ā
**Environment Variables** - .env configuration
|
|
49
|
+
ā
**Package Scripts** - Helper scripts for development
|
|
50
|
+
|
|
51
|
+
### š Option 2: Manual Setup
|
|
52
|
+
|
|
53
|
+
If you prefer manual setup, follow these steps:
|
|
54
|
+
|
|
55
|
+
#### Step 1: Install Dependencies
|
|
29
56
|
|
|
30
57
|
```bash
|
|
31
58
|
npm install exguard-backend @nestjs/core @nestjs/common @nestjs/platform-express
|
|
32
59
|
```
|
|
33
60
|
|
|
34
|
-
|
|
61
|
+
#### Step 2: Create ExGuard Module
|
|
35
62
|
|
|
36
63
|
```typescript
|
|
37
64
|
// src/exguard/exguard.module.ts
|
|
@@ -54,7 +81,7 @@ import { Guard } from 'exguard-backend';
|
|
|
54
81
|
export class ExGuardModule {}
|
|
55
82
|
```
|
|
56
83
|
|
|
57
|
-
|
|
84
|
+
#### Step 3: Create Custom Guards
|
|
58
85
|
|
|
59
86
|
```typescript
|
|
60
87
|
// src/exguard/exguard.guard.ts
|
|
@@ -104,7 +131,7 @@ export function createPermissionGuard(permissions: string[], requireAll = false)
|
|
|
104
131
|
}
|
|
105
132
|
```
|
|
106
133
|
|
|
107
|
-
|
|
134
|
+
#### Step 4: Protect Your Controllers
|
|
108
135
|
|
|
109
136
|
```typescript
|
|
110
137
|
// src/events/events.controller.ts
|
|
@@ -134,7 +161,7 @@ export class EventsController {
|
|
|
134
161
|
}
|
|
135
162
|
```
|
|
136
163
|
|
|
137
|
-
|
|
164
|
+
#### Step 5: Update App Module
|
|
138
165
|
|
|
139
166
|
```typescript
|
|
140
167
|
// src/app.module.ts
|
|
@@ -149,6 +176,41 @@ import { EventsController } from './events/events.controller';
|
|
|
149
176
|
export class AppModule {}
|
|
150
177
|
```
|
|
151
178
|
|
|
179
|
+
### šÆ Automatic Setup Features
|
|
180
|
+
|
|
181
|
+
The automatic setup creates a complete NestJS integration:
|
|
182
|
+
|
|
183
|
+
#### **Generated Files:**
|
|
184
|
+
```
|
|
185
|
+
src/
|
|
186
|
+
āāā exguard/
|
|
187
|
+
ā āāā exguard.module.ts # Global ExGuard module
|
|
188
|
+
ā āāā exguard.guard.ts # Custom guards
|
|
189
|
+
ā āāā exguard.decorators.ts # Permission decorators
|
|
190
|
+
ā āāā guards/ # Additional guard files
|
|
191
|
+
āāā events/
|
|
192
|
+
ā āāā events.controller.ts # Example controller
|
|
193
|
+
āāā .env # Environment configuration
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
#### **Generated Decorators:**
|
|
197
|
+
```typescript
|
|
198
|
+
@RequirePermissions(['events:read'])
|
|
199
|
+
@RequireRoles(['Admin'])
|
|
200
|
+
@RequireModules(['reporting'])
|
|
201
|
+
@RequireFieldOffices(['FO-MANILA'])
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
#### **Generated Package Scripts:**
|
|
205
|
+
```json
|
|
206
|
+
{
|
|
207
|
+
"scripts": {
|
|
208
|
+
"exguard:setup": "node node_modules/exguard-backend/scripts/setup-nestjs.js",
|
|
209
|
+
"exguard:example": "node node_modules/exguard-backend/scripts/create-example.js"
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
```
|
|
213
|
+
|
|
152
214
|
## šÆ Advanced NestJS Examples
|
|
153
215
|
|
|
154
216
|
### Role-Based Protection
|
package/dist/index.d.cts
CHANGED
|
@@ -51,17 +51,17 @@ interface ExGuardEnhancedConfig$1 extends ExGuardConfig {
|
|
|
51
51
|
cache?: ExGuardCacheConfig;
|
|
52
52
|
realtime?: ExGuardRealtimeConfig;
|
|
53
53
|
}
|
|
54
|
-
interface GuardContext
|
|
54
|
+
interface GuardContext {
|
|
55
55
|
token: string;
|
|
56
56
|
request?: any;
|
|
57
57
|
}
|
|
58
|
-
interface GuardResult
|
|
58
|
+
interface GuardResult {
|
|
59
59
|
allowed: boolean;
|
|
60
60
|
user?: UserAccessResponse;
|
|
61
61
|
error?: string;
|
|
62
62
|
statusCode?: number;
|
|
63
63
|
}
|
|
64
|
-
interface GuardOptions
|
|
64
|
+
interface GuardOptions {
|
|
65
65
|
permissions?: string[];
|
|
66
66
|
roles?: string[];
|
|
67
67
|
modules?: string[];
|
|
@@ -292,23 +292,6 @@ declare const realtime: ExGuardRealtime;
|
|
|
292
292
|
* Framework-agnostic guards for protecting backend endpoints
|
|
293
293
|
*/
|
|
294
294
|
|
|
295
|
-
interface GuardContext {
|
|
296
|
-
token: string;
|
|
297
|
-
request?: any;
|
|
298
|
-
}
|
|
299
|
-
interface GuardResult {
|
|
300
|
-
allowed: boolean;
|
|
301
|
-
user?: UserAccessResponse;
|
|
302
|
-
error?: string;
|
|
303
|
-
statusCode?: number;
|
|
304
|
-
}
|
|
305
|
-
interface GuardOptions {
|
|
306
|
-
permissions?: string[];
|
|
307
|
-
roles?: string[];
|
|
308
|
-
modules?: string[];
|
|
309
|
-
fieldOffices?: string[];
|
|
310
|
-
requireAll?: boolean;
|
|
311
|
-
}
|
|
312
295
|
/**
|
|
313
296
|
* Framework-agnostic guard class for endpoint protection
|
|
314
297
|
*/
|
|
@@ -480,4 +463,4 @@ declare function createExGuardFastify(config: any): {
|
|
|
480
463
|
getGuard(): ExGuardBackend;
|
|
481
464
|
};
|
|
482
465
|
|
|
483
|
-
export { type ApiResponse, ExGuardBackend$1 as ExGuardBackend, ExGuardBackendEnhanced, ExGuardCache, type ExGuardCacheConfig, type ExGuardConfig, type ExGuardEnhancedConfig$1 as ExGuardEnhancedConfig, ExGuardRealtime, type ExGuardRealtimeConfig, ExGuardBackend as Guard, type GuardContext
|
|
466
|
+
export { type ApiResponse, ExGuardBackend$1 as ExGuardBackend, ExGuardBackendEnhanced, ExGuardCache, type ExGuardCacheConfig, type ExGuardConfig, type ExGuardEnhancedConfig$1 as ExGuardEnhancedConfig, ExGuardRealtime, type ExGuardRealtimeConfig, ExGuardBackend as Guard, type GuardContext, type GuardOptions, type GuardResult, type UserAccessResponse, cache, createExGuardExpress, createExGuardFastify, realtime };
|
package/dist/index.d.ts
CHANGED
|
@@ -51,17 +51,17 @@ interface ExGuardEnhancedConfig$1 extends ExGuardConfig {
|
|
|
51
51
|
cache?: ExGuardCacheConfig;
|
|
52
52
|
realtime?: ExGuardRealtimeConfig;
|
|
53
53
|
}
|
|
54
|
-
interface GuardContext
|
|
54
|
+
interface GuardContext {
|
|
55
55
|
token: string;
|
|
56
56
|
request?: any;
|
|
57
57
|
}
|
|
58
|
-
interface GuardResult
|
|
58
|
+
interface GuardResult {
|
|
59
59
|
allowed: boolean;
|
|
60
60
|
user?: UserAccessResponse;
|
|
61
61
|
error?: string;
|
|
62
62
|
statusCode?: number;
|
|
63
63
|
}
|
|
64
|
-
interface GuardOptions
|
|
64
|
+
interface GuardOptions {
|
|
65
65
|
permissions?: string[];
|
|
66
66
|
roles?: string[];
|
|
67
67
|
modules?: string[];
|
|
@@ -292,23 +292,6 @@ declare const realtime: ExGuardRealtime;
|
|
|
292
292
|
* Framework-agnostic guards for protecting backend endpoints
|
|
293
293
|
*/
|
|
294
294
|
|
|
295
|
-
interface GuardContext {
|
|
296
|
-
token: string;
|
|
297
|
-
request?: any;
|
|
298
|
-
}
|
|
299
|
-
interface GuardResult {
|
|
300
|
-
allowed: boolean;
|
|
301
|
-
user?: UserAccessResponse;
|
|
302
|
-
error?: string;
|
|
303
|
-
statusCode?: number;
|
|
304
|
-
}
|
|
305
|
-
interface GuardOptions {
|
|
306
|
-
permissions?: string[];
|
|
307
|
-
roles?: string[];
|
|
308
|
-
modules?: string[];
|
|
309
|
-
fieldOffices?: string[];
|
|
310
|
-
requireAll?: boolean;
|
|
311
|
-
}
|
|
312
295
|
/**
|
|
313
296
|
* Framework-agnostic guard class for endpoint protection
|
|
314
297
|
*/
|
|
@@ -480,4 +463,4 @@ declare function createExGuardFastify(config: any): {
|
|
|
480
463
|
getGuard(): ExGuardBackend;
|
|
481
464
|
};
|
|
482
465
|
|
|
483
|
-
export { type ApiResponse, ExGuardBackend$1 as ExGuardBackend, ExGuardBackendEnhanced, ExGuardCache, type ExGuardCacheConfig, type ExGuardConfig, type ExGuardEnhancedConfig$1 as ExGuardEnhancedConfig, ExGuardRealtime, type ExGuardRealtimeConfig, ExGuardBackend as Guard, type GuardContext
|
|
466
|
+
export { type ApiResponse, ExGuardBackend$1 as ExGuardBackend, ExGuardBackendEnhanced, ExGuardCache, type ExGuardCacheConfig, type ExGuardConfig, type ExGuardEnhancedConfig$1 as ExGuardEnhancedConfig, ExGuardRealtime, type ExGuardRealtimeConfig, ExGuardBackend as Guard, type GuardContext, type GuardOptions, type GuardResult, type UserAccessResponse, cache, createExGuardExpress, createExGuardFastify, realtime };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "exguard-backend",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.5",
|
|
4
4
|
"private": false,
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -19,8 +19,12 @@
|
|
|
19
19
|
},
|
|
20
20
|
"files": [
|
|
21
21
|
"dist",
|
|
22
|
+
"scripts",
|
|
22
23
|
"README.md"
|
|
23
24
|
],
|
|
25
|
+
"bin": {
|
|
26
|
+
"exguard-backend": "scripts/setup-nestjs.js"
|
|
27
|
+
},
|
|
24
28
|
"keywords": [
|
|
25
29
|
"exguard",
|
|
26
30
|
"rbac",
|
|
@@ -46,6 +50,8 @@
|
|
|
46
50
|
"test": "node --test",
|
|
47
51
|
"test:watch": "node --test --watch",
|
|
48
52
|
"clean": "node -e \"fs.rmSync('dist', { recursive: true, force: true })\"",
|
|
49
|
-
"prebuild": "npm run clean"
|
|
53
|
+
"prebuild": "npm run clean",
|
|
54
|
+
"setup-nestjs": "node scripts/setup-nestjs.js",
|
|
55
|
+
"create-example": "node scripts/create-example.js"
|
|
50
56
|
}
|
|
51
57
|
}
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Create Example Controller for ExGuard
|
|
5
|
+
*
|
|
6
|
+
* Usage:
|
|
7
|
+
* npx exguard-backend create-example
|
|
8
|
+
* OR
|
|
9
|
+
* node node_modules/exguard-backend/scripts/create-example.js
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
const fs = require('fs');
|
|
13
|
+
const path = require('path');
|
|
14
|
+
|
|
15
|
+
// ANSI colors
|
|
16
|
+
const colors = {
|
|
17
|
+
reset: '\x1b[0m',
|
|
18
|
+
green: '\x1b[32m',
|
|
19
|
+
yellow: '\x1b[33m',
|
|
20
|
+
cyan: '\x1b[36m'
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
function log(message, color = 'reset') {
|
|
24
|
+
console.log(`${colors[color]}${message}${colors.reset}`);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function logSuccess(message) {
|
|
28
|
+
log(`ā
${message}`, 'green');
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function logInfo(message) {
|
|
32
|
+
log(`ā¹ļø ${message}`, 'cyan');
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Create example controller
|
|
36
|
+
function createExampleController() {
|
|
37
|
+
const controllerContent = `import {
|
|
38
|
+
Controller,
|
|
39
|
+
Get,
|
|
40
|
+
Post,
|
|
41
|
+
Put,
|
|
42
|
+
Delete,
|
|
43
|
+
Body,
|
|
44
|
+
Param,
|
|
45
|
+
UseGuards,
|
|
46
|
+
Request
|
|
47
|
+
} from '@nestjs/common';
|
|
48
|
+
import {
|
|
49
|
+
RequirePermissions,
|
|
50
|
+
RequireRoles,
|
|
51
|
+
RequireModules
|
|
52
|
+
} from '../exguard/exguard.decorators';
|
|
53
|
+
import {
|
|
54
|
+
ExGuardPermissionGuard,
|
|
55
|
+
createPermissionGuard,
|
|
56
|
+
createRoleGuard,
|
|
57
|
+
createModuleGuard
|
|
58
|
+
} from '../exguard/exguard.guard';
|
|
59
|
+
|
|
60
|
+
@Controller('api/example')
|
|
61
|
+
export class ExampleController {
|
|
62
|
+
@Get()
|
|
63
|
+
@UseGuards(ExGuardPermissionGuard) // Requires 'read' permission
|
|
64
|
+
async getItems(@Request() req) {
|
|
65
|
+
return {
|
|
66
|
+
success: true,
|
|
67
|
+
data: [],
|
|
68
|
+
user: req.user,
|
|
69
|
+
message: 'Items retrieved successfully'
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
@Post()
|
|
74
|
+
@RequirePermissions(['items:create']) // Using decorator
|
|
75
|
+
@UseGuards(createPermissionGuard(['items:create']))
|
|
76
|
+
async createItem(@Body() createDto: any, @Request() req) {
|
|
77
|
+
return {
|
|
78
|
+
success: true,
|
|
79
|
+
data: createDto,
|
|
80
|
+
user: req.user,
|
|
81
|
+
message: 'Item created successfully'
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
@Put(':id')
|
|
86
|
+
@UseGuards(createPermissionGuard(['items:update'], true)) // Require both permissions
|
|
87
|
+
async updateItem(@Param('id') id: string, @Body() updateDto: any, @Request() req) {
|
|
88
|
+
return {
|
|
89
|
+
success: true,
|
|
90
|
+
data: { id, ...updateDto },
|
|
91
|
+
user: req.user,
|
|
92
|
+
message: 'Item updated successfully'
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
@Delete(':id')
|
|
97
|
+
@RequireRoles(['Admin', 'Manager']) // Using decorator
|
|
98
|
+
@UseGuards(createRoleGuard(['Admin', 'Manager']))
|
|
99
|
+
async deleteItem(@Param('id') id: string, @Request() req) {
|
|
100
|
+
return {
|
|
101
|
+
success: true,
|
|
102
|
+
message: \`Item \${id} deleted successfully\`,
|
|
103
|
+
user: req.user
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
@Get('reports')
|
|
108
|
+
@RequireModules(['reporting']) // Using decorator
|
|
109
|
+
@UseGuards(createModuleGuard(['reporting']))
|
|
110
|
+
async getReports(@Request() req) {
|
|
111
|
+
return {
|
|
112
|
+
success: true,
|
|
113
|
+
data: { reports: [], total: 0 },
|
|
114
|
+
user: req.user,
|
|
115
|
+
message: 'Reports retrieved successfully'
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
@Get('admin/dashboard')
|
|
120
|
+
@UseGuards(createRoleGuard(['Admin']))
|
|
121
|
+
async getAdminDashboard(@Request() req) {
|
|
122
|
+
return {
|
|
123
|
+
success: true,
|
|
124
|
+
data: {
|
|
125
|
+
totalUsers: 100,
|
|
126
|
+
activeUsers: 85,
|
|
127
|
+
totalItems: 500
|
|
128
|
+
},
|
|
129
|
+
user: req.user,
|
|
130
|
+
message: 'Admin dashboard data retrieved'
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
@Get('complex')
|
|
135
|
+
@UseGuards(new (class extends ExGuardPermissionGuard {
|
|
136
|
+
protected async checkPermissions(context: any) {
|
|
137
|
+
return this.exGuard.require(context, {
|
|
138
|
+
permissions: ['complex:access'],
|
|
139
|
+
roles: ['Manager'],
|
|
140
|
+
modules: ['analytics'],
|
|
141
|
+
requireAll: true // Must satisfy ALL conditions
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
})(this.exGuard))
|
|
145
|
+
async getComplexData(@Request() req) {
|
|
146
|
+
return {
|
|
147
|
+
success: true,
|
|
148
|
+
data: { complexData: '...' },
|
|
149
|
+
user: req.user,
|
|
150
|
+
message: 'Complex data accessed successfully'
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
`;
|
|
155
|
+
|
|
156
|
+
const controllerPath = path.join(process.cwd(), 'src/api/example/example.controller.ts');
|
|
157
|
+
|
|
158
|
+
// Create directories if they don't exist
|
|
159
|
+
const apiDir = path.join(process.cwd(), 'src/api');
|
|
160
|
+
const exampleDir = path.join(process.cwd(), 'src/api/example');
|
|
161
|
+
|
|
162
|
+
if (!fs.existsSync(apiDir)) {
|
|
163
|
+
fs.mkdirSync(apiDir, { recursive: true });
|
|
164
|
+
logSuccess('Created api directory');
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
if (!fs.existsSync(exampleDir)) {
|
|
168
|
+
fs.mkdirSync(exampleDir, { recursive: true });
|
|
169
|
+
logSuccess('Created example directory');
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
if (fs.existsSync(controllerPath)) {
|
|
173
|
+
log('ā ļø Example controller already exists. Skipping creation.', 'yellow');
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
fs.writeFileSync(controllerPath, controllerContent);
|
|
178
|
+
logSuccess('Created example controller at src/api/example/example.controller.ts');
|
|
179
|
+
|
|
180
|
+
logInfo('\nš Example controller features:');
|
|
181
|
+
logInfo('⢠Basic CRUD operations with permission protection');
|
|
182
|
+
logInfo('⢠Role-based protection for admin operations');
|
|
183
|
+
logInfo('⢠Module-based protection for reports');
|
|
184
|
+
logInfo('⢠Complex multi-requirement protection');
|
|
185
|
+
logInfo('⢠Decorator usage examples');
|
|
186
|
+
logInfo('⢠Dynamic guard factory usage');
|
|
187
|
+
|
|
188
|
+
logInfo('\nšÆ Next steps:');
|
|
189
|
+
logInfo('1. Update your app.module.ts to import ExampleController');
|
|
190
|
+
logInfo('2. Start your application: npm run start:dev');
|
|
191
|
+
logInfo('3. Test the endpoints with different user permissions');
|
|
192
|
+
logInfo('4. Check the user data attached to requests');
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// Run if called directly
|
|
196
|
+
if (require.main === module) {
|
|
197
|
+
log('š Creating ExGuard Example Controller', 'cyan');
|
|
198
|
+
createExampleController();
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
module.exports = { createExampleController };
|
|
@@ -0,0 +1,535 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Automatic ExGuard Module Setup for NestJS
|
|
5
|
+
*
|
|
6
|
+
* Usage:
|
|
7
|
+
* npx exguard-backend
|
|
8
|
+
* npx exguard-backend setup-nestjs
|
|
9
|
+
* OR
|
|
10
|
+
* node node_modules/exguard-backend/scripts/setup-nestjs.js
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
const fs = require('fs');
|
|
14
|
+
const path = require('path');
|
|
15
|
+
const { execSync } = require('child_process');
|
|
16
|
+
|
|
17
|
+
// ANSI colors for better output
|
|
18
|
+
const colors = {
|
|
19
|
+
reset: '\x1b[0m',
|
|
20
|
+
bright: '\x1b[1m',
|
|
21
|
+
red: '\x1b[31m',
|
|
22
|
+
green: '\x1b[32m',
|
|
23
|
+
yellow: '\x1b[33m',
|
|
24
|
+
blue: '\x1b[34m',
|
|
25
|
+
magenta: '\x1b[35m',
|
|
26
|
+
cyan: '\x1b[36m'
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
function log(message, color = 'reset') {
|
|
30
|
+
console.log(`${colors[color]}${message}${colors.reset}`);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function logStep(step, message) {
|
|
34
|
+
log(`\nš§ Step ${step}: ${message}`, 'cyan');
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function logSuccess(message) {
|
|
38
|
+
log(`ā
${message}`, 'green');
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function logWarning(message) {
|
|
42
|
+
log(`ā ļø ${message}`, 'yellow');
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function logError(message) {
|
|
46
|
+
log(`ā ${message}`, 'red');
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function logInfo(message) {
|
|
50
|
+
log(`ā¹ļø ${message}`, 'blue');
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Check if we're in a NestJS project
|
|
54
|
+
function checkNestJSProject() {
|
|
55
|
+
const packageJsonPath = path.join(process.cwd(), 'package.json');
|
|
56
|
+
|
|
57
|
+
if (!fs.existsSync(packageJsonPath)) {
|
|
58
|
+
logError('package.json not found. Please run this command in a NestJS project root.');
|
|
59
|
+
process.exit(1);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
|
|
63
|
+
const dependencies = { ...packageJson.dependencies, ...packageJson.devDependencies };
|
|
64
|
+
|
|
65
|
+
const nestPackages = Object.keys(dependencies).filter(dep =>
|
|
66
|
+
dep.startsWith('@nestjs/') || dep === 'nestjs'
|
|
67
|
+
);
|
|
68
|
+
|
|
69
|
+
if (nestPackages.length === 0) {
|
|
70
|
+
logError('No NestJS packages found. Please run this command in a NestJS project.');
|
|
71
|
+
process.exit(1);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
logSuccess(`NestJS project detected with packages: ${nestPackages.join(', ')}`);
|
|
75
|
+
return true;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Create exguard directory structure
|
|
79
|
+
function createDirectoryStructure() {
|
|
80
|
+
logStep(1, 'Creating ExGuard directory structure');
|
|
81
|
+
|
|
82
|
+
const directories = [
|
|
83
|
+
'src/exguard',
|
|
84
|
+
'src/exguard/guards',
|
|
85
|
+
'src/exguard/decorators',
|
|
86
|
+
'src/exguard/interceptors'
|
|
87
|
+
];
|
|
88
|
+
|
|
89
|
+
directories.forEach(dir => {
|
|
90
|
+
const fullPath = path.join(process.cwd(), dir);
|
|
91
|
+
if (!fs.existsSync(fullPath)) {
|
|
92
|
+
fs.mkdirSync(fullPath, { recursive: true });
|
|
93
|
+
logSuccess(`Created directory: ${dir}`);
|
|
94
|
+
} else {
|
|
95
|
+
logWarning(`Directory already exists: ${dir}`);
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Create ExGuard Module
|
|
101
|
+
function createExGuardModule() {
|
|
102
|
+
logStep(2, 'Creating ExGuard Module');
|
|
103
|
+
|
|
104
|
+
const moduleContent = `import { Module, Global } from '@nestjs/common';
|
|
105
|
+
import { Guard } from 'exguard-backend';
|
|
106
|
+
|
|
107
|
+
@Global()
|
|
108
|
+
@Module({
|
|
109
|
+
providers: [
|
|
110
|
+
{
|
|
111
|
+
provide: Guard,
|
|
112
|
+
useFactory: () => new Guard({
|
|
113
|
+
apiUrl: process.env.EXGUARD_API_URL || 'http://localhost:3000',
|
|
114
|
+
cache: {
|
|
115
|
+
enabled: process.env.EXGUARD_CACHE_ENABLED !== 'false',
|
|
116
|
+
ttl: parseInt(process.env.EXGUARD_CACHE_TTL) || 300000, // 5 minutes
|
|
117
|
+
},
|
|
118
|
+
realtime: {
|
|
119
|
+
enabled: process.env.EXGUARD_REALTIME_ENABLED === 'true',
|
|
120
|
+
url: process.env.EXGUARD_REALTIME_URL,
|
|
121
|
+
token: process.env.EXGUARD_SERVICE_TOKEN,
|
|
122
|
+
},
|
|
123
|
+
}),
|
|
124
|
+
},
|
|
125
|
+
],
|
|
126
|
+
exports: [Guard],
|
|
127
|
+
})
|
|
128
|
+
export class ExGuardModule {}
|
|
129
|
+
`;
|
|
130
|
+
|
|
131
|
+
const modulePath = path.join(process.cwd(), 'src/exguard/exguard.module.ts');
|
|
132
|
+
|
|
133
|
+
if (fs.existsSync(modulePath)) {
|
|
134
|
+
logWarning('ExGuardModule already exists. Skipping creation.');
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
fs.writeFileSync(modulePath, moduleContent);
|
|
139
|
+
logSuccess('Created ExGuardModule');
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// Create ExGuard Guards
|
|
143
|
+
function createExGuardGuards() {
|
|
144
|
+
logStep(3, 'Creating ExGuard Guards');
|
|
145
|
+
|
|
146
|
+
const guardContent = `import { Injectable, CanActivate, ExecutionContext, ForbiddenException, UnauthorizedException } from '@nestjs/common';
|
|
147
|
+
import { Guard, GuardContext } from 'exguard-backend';
|
|
148
|
+
|
|
149
|
+
@Injectable()
|
|
150
|
+
export class ExGuardNestGuard implements CanActivate {
|
|
151
|
+
constructor(protected exGuard: Guard) {}
|
|
152
|
+
|
|
153
|
+
async canActivate(context: ExecutionContext): Promise<boolean> {
|
|
154
|
+
const request = context.switchToHttp().getRequest();
|
|
155
|
+
const token = this.extractToken(request);
|
|
156
|
+
|
|
157
|
+
if (!token) {
|
|
158
|
+
throw new UnauthorizedException('No authentication token provided');
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
const guardContext: GuardContext = {
|
|
162
|
+
token,
|
|
163
|
+
request,
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
const result = await this.checkPermissions(guardContext);
|
|
167
|
+
|
|
168
|
+
if (!result.allowed) {
|
|
169
|
+
throw new ForbiddenException(result.error || 'Access denied');
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
request.user = result.user;
|
|
173
|
+
return true;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
protected async checkPermissions(context: GuardContext) {
|
|
177
|
+
return this.exGuard.authenticate(context);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
protected extractToken(request: any): string | null {
|
|
181
|
+
const authHeader = request.headers?.authorization;
|
|
182
|
+
if (authHeader?.startsWith('Bearer ')) {
|
|
183
|
+
return authHeader.substring(7);
|
|
184
|
+
}
|
|
185
|
+
return null;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
@Injectable()
|
|
190
|
+
export class ExGuardPermissionGuard extends ExGuardNestGuard {
|
|
191
|
+
protected async checkPermissions(context: GuardContext) {
|
|
192
|
+
return this.exGuard.requirePermissions(context, ['read']);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
@Injectable()
|
|
197
|
+
export class ExGuardRoleGuard extends ExGuardNestGuard {
|
|
198
|
+
protected async checkPermissions(context: GuardContext) {
|
|
199
|
+
return this.exGuard.requireRoles(context, ['Admin']);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// Factory functions for dynamic guards
|
|
204
|
+
export function createPermissionGuard(permissions: string[], requireAll = false) {
|
|
205
|
+
return class extends ExGuardNestGuard {
|
|
206
|
+
protected async checkPermissions(context: GuardContext) {
|
|
207
|
+
return this.exGuard.requirePermissions(context, permissions, { requireAll });
|
|
208
|
+
}
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
export function createRoleGuard(roles: string[], requireAll = false) {
|
|
213
|
+
return class extends ExGuardNestGuard {
|
|
214
|
+
protected async checkPermissions(context: GuardContext) {
|
|
215
|
+
return this.exGuard.requireRoles(context, roles, { requireAll });
|
|
216
|
+
}
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
export function createModuleGuard(modules: string[], requireAll = false) {
|
|
221
|
+
return class extends ExGuardNestGuard {
|
|
222
|
+
protected async checkPermissions(context: GuardContext) {
|
|
223
|
+
return this.exGuard.requireModules(context, modules, { requireAll });
|
|
224
|
+
}
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
`;
|
|
228
|
+
|
|
229
|
+
const guardPath = path.join(process.cwd(), 'src/exguard/exguard.guard.ts');
|
|
230
|
+
|
|
231
|
+
if (fs.existsSync(guardPath)) {
|
|
232
|
+
logWarning('ExGuard guards already exist. Skipping creation.');
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
fs.writeFileSync(guardPath, guardContent);
|
|
237
|
+
logSuccess('Created ExGuard guards');
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// Create ExGuard Decorators
|
|
241
|
+
function createExGuardDecorators() {
|
|
242
|
+
logStep(4, 'Creating ExGuard Decorators');
|
|
243
|
+
|
|
244
|
+
const decoratorContent = `import { SetMetadata } from '@nestjs/common';
|
|
245
|
+
import { createPermissionGuard, createRoleGuard, createModuleGuard } from './exguard.guard';
|
|
246
|
+
|
|
247
|
+
// Metadata keys
|
|
248
|
+
export const EXGUARD_PERMISSIONS_KEY = 'exguard_permissions';
|
|
249
|
+
export const EXGUARD_ROLES_KEY = 'exguard_roles';
|
|
250
|
+
export const EXGUARD_MODULES_KEY = 'exguard_modules';
|
|
251
|
+
export const EXGUARD_FIELD_OFFICES_KEY = 'exguard_field_offices';
|
|
252
|
+
|
|
253
|
+
// Permission decorator
|
|
254
|
+
export const RequirePermissions = (permissions: string[], requireAll = false) => {
|
|
255
|
+
return SetMetadata(EXGUARD_PERMISSIONS_KEY, { permissions, requireAll });
|
|
256
|
+
};
|
|
257
|
+
|
|
258
|
+
// Role decorator
|
|
259
|
+
export const RequireRoles = (roles: string[], requireAll = false) => {
|
|
260
|
+
return SetMetadata(EXGUARD_ROLES_KEY, { roles, requireAll });
|
|
261
|
+
};
|
|
262
|
+
|
|
263
|
+
// Module decorator
|
|
264
|
+
export const RequireModules = (modules: string[], requireAll = false) => {
|
|
265
|
+
return SetMetadata(EXGUARD_MODULES_KEY, { modules, requireAll });
|
|
266
|
+
};
|
|
267
|
+
|
|
268
|
+
// Field office decorator
|
|
269
|
+
export const RequireFieldOffices = (fieldOffices: string[], requireAll = false) => {
|
|
270
|
+
return SetMetadata(EXGUARD_FIELD_OFFICES_KEY, { fieldOffices, requireAll });
|
|
271
|
+
};
|
|
272
|
+
|
|
273
|
+
// Combined decorator for complex requirements
|
|
274
|
+
export const Require = (requirements: {
|
|
275
|
+
permissions?: string[];
|
|
276
|
+
roles?: string[];
|
|
277
|
+
modules?: string[];
|
|
278
|
+
fieldOffices?: string[];
|
|
279
|
+
requireAll?: boolean;
|
|
280
|
+
}) => {
|
|
281
|
+
return (target: any) => {
|
|
282
|
+
if (requirements.permissions) {
|
|
283
|
+
SetMetadata(EXGUARD_PERMISSIONS_KEY, {
|
|
284
|
+
permissions: requirements.permissions,
|
|
285
|
+
requireAll: requirements.requireAll || false
|
|
286
|
+
})(target);
|
|
287
|
+
}
|
|
288
|
+
if (requirements.roles) {
|
|
289
|
+
SetMetadata(EXGUARD_ROLES_KEY, {
|
|
290
|
+
roles: requirements.roles,
|
|
291
|
+
requireAll: requirements.requireAll || false
|
|
292
|
+
})(target);
|
|
293
|
+
}
|
|
294
|
+
if (requirements.modules) {
|
|
295
|
+
SetMetadata(EXGUARD_MODULES_KEY, {
|
|
296
|
+
modules: requirements.modules,
|
|
297
|
+
requireAll: requirements.requireAll || false
|
|
298
|
+
})(target);
|
|
299
|
+
}
|
|
300
|
+
if (requirements.fieldOffices) {
|
|
301
|
+
SetMetadata(EXGUARD_FIELD_OFFICES_KEY, {
|
|
302
|
+
fieldOffices: requirements.fieldOffices,
|
|
303
|
+
requireAll: requirements.requireAll || false
|
|
304
|
+
})(target);
|
|
305
|
+
}
|
|
306
|
+
};
|
|
307
|
+
};
|
|
308
|
+
`;
|
|
309
|
+
|
|
310
|
+
const decoratorPath = path.join(process.cwd(), 'src/exguard/exguard.decorators.ts');
|
|
311
|
+
|
|
312
|
+
if (fs.existsSync(decoratorPath)) {
|
|
313
|
+
logWarning('ExGuard decorators already exist. Skipping creation.');
|
|
314
|
+
return;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
fs.writeFileSync(decoratorPath, decoratorContent);
|
|
318
|
+
logSuccess('Created ExGuard decorators');
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
// Create example controller
|
|
322
|
+
function createExampleController() {
|
|
323
|
+
logStep(5, 'Creating Example Controller');
|
|
324
|
+
|
|
325
|
+
const controllerContent = `import {
|
|
326
|
+
Controller,
|
|
327
|
+
Get,
|
|
328
|
+
Post,
|
|
329
|
+
Body,
|
|
330
|
+
UseGuards,
|
|
331
|
+
Request
|
|
332
|
+
} from '@nestjs/common';
|
|
333
|
+
import {
|
|
334
|
+
RequirePermissions,
|
|
335
|
+
RequireRoles,
|
|
336
|
+
RequireModules
|
|
337
|
+
} from '../exguard/exguard.decorators';
|
|
338
|
+
import {
|
|
339
|
+
ExGuardPermissionGuard,
|
|
340
|
+
createPermissionGuard
|
|
341
|
+
} from '../exguard/exguard.guard';
|
|
342
|
+
|
|
343
|
+
@Controller('events')
|
|
344
|
+
@UseGuards(ExGuardPermissionGuard) // Requires 'read' permission
|
|
345
|
+
export class EventsController {
|
|
346
|
+
@Get()
|
|
347
|
+
async getEvents(@Request() req) {
|
|
348
|
+
console.log('User accessing events:', req.user);
|
|
349
|
+
return { success: true, data: [] };
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
@Post()
|
|
353
|
+
@UseGuards(createPermissionGuard(['events:create']))
|
|
354
|
+
async createEvent(@Body() createEventDto: any, @Request() req) {
|
|
355
|
+
console.log('User creating event:', req.user);
|
|
356
|
+
return { success: true, data: createEventDto };
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
@Get('admin')
|
|
360
|
+
@RequireRoles(['Admin']) // Using decorator
|
|
361
|
+
@UseGuards(createRoleGuard(['Admin']))
|
|
362
|
+
async getAdminEvents(@Request() req) {
|
|
363
|
+
return { success: true, data: [], message: 'Admin events' };
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
@Get('reports')
|
|
367
|
+
@RequireModules(['reporting']) // Using decorator
|
|
368
|
+
@UseGuards(createModuleGuard(['reporting']))
|
|
369
|
+
async getReports(@Request() req) {
|
|
370
|
+
return { success: true, data: [], message: 'Reports data' };
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
`;
|
|
374
|
+
|
|
375
|
+
const controllerPath = path.join(process.cwd(), 'src/events/events.controller.ts');
|
|
376
|
+
|
|
377
|
+
// Check if events directory exists
|
|
378
|
+
const eventsDir = path.join(process.cwd(), 'src/events');
|
|
379
|
+
if (!fs.existsSync(eventsDir)) {
|
|
380
|
+
fs.mkdirSync(eventsDir, { recursive: true });
|
|
381
|
+
logSuccess('Created events directory');
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
if (fs.existsSync(controllerPath)) {
|
|
385
|
+
logWarning('Example controller already exists. Skipping creation.');
|
|
386
|
+
return;
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
fs.writeFileSync(controllerPath, controllerContent);
|
|
390
|
+
logSuccess('Created example controller');
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
// Update app module
|
|
394
|
+
function updateAppModule() {
|
|
395
|
+
logStep(6, 'Updating App Module');
|
|
396
|
+
|
|
397
|
+
const appModulePath = path.join(process.cwd(), 'src/app.module.ts');
|
|
398
|
+
|
|
399
|
+
if (!fs.existsSync(appModulePath)) {
|
|
400
|
+
logWarning('app.module.ts not found. Please manually import ExGuardModule.');
|
|
401
|
+
return;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
let appModuleContent = fs.readFileSync(appModulePath, 'utf8');
|
|
405
|
+
|
|
406
|
+
// Check if ExGuardModule is already imported
|
|
407
|
+
if (appModuleContent.includes('ExGuardModule')) {
|
|
408
|
+
logWarning('ExGuardModule already imported in app.module.ts');
|
|
409
|
+
return;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
// Add import
|
|
413
|
+
const importMatch = appModuleContent.match(/import\s*\{[^}]*\}\s*from\s*['"][^'"]*['"];?\s*/m);
|
|
414
|
+
if (importMatch) {
|
|
415
|
+
const lastImport = importMatch[0];
|
|
416
|
+
const newImport = lastImport.replace(/;?\s*$/, '') + ',\\n ExGuardModule from \\'./exguard/exguard.module\\';\\n';
|
|
417
|
+
appModuleContent = appModuleContent.replace(lastImport, newImport);
|
|
418
|
+
} else {
|
|
419
|
+
// Add import at the top
|
|
420
|
+
appModuleContent = `import { ExGuardModule } from './exguard/exguard.module';\\n\\n${appModuleContent}`;
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
// Add to imports array
|
|
424
|
+
const importsMatch = appModuleContent.match(/@Module\s*\(\s*\{\s*imports:\s*\[([^\]]*)\]/m);
|
|
425
|
+
if (importsMatch) {
|
|
426
|
+
const importsContent = importsMatch[1];
|
|
427
|
+
const newImportsContent = importsContent.trim() + ',\\n ExGuardModule';
|
|
428
|
+
appModuleContent = appModuleContent.replace(importsMatch[0], `@Module({\\n imports: [${newImportsContent}]`);
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
fs.writeFileSync(appModulePath, appModuleContent);
|
|
432
|
+
logSuccess('Updated app.module.ts with ExGuardModule');
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
// Create environment variables
|
|
436
|
+
function createEnvironmentFile() {
|
|
437
|
+
logStep(7, 'Creating Environment Variables');
|
|
438
|
+
|
|
439
|
+
const envContent = `# ExGuard Configuration
|
|
440
|
+
EXGUARD_API_URL=http://localhost:3000
|
|
441
|
+
EXGUARD_CACHE_ENABLED=true
|
|
442
|
+
EXGUARD_CACHE_TTL=300000
|
|
443
|
+
|
|
444
|
+
# Optional Realtime Configuration
|
|
445
|
+
EXGUARD_REALTIME_ENABLED=false
|
|
446
|
+
EXGUARD_REALTIME_URL=ws://localhost:3000/realtime
|
|
447
|
+
EXGUARD_SERVICE_TOKEN=your-service-jwt-token
|
|
448
|
+
`;
|
|
449
|
+
|
|
450
|
+
const envPath = path.join(process.cwd(), '.env');
|
|
451
|
+
|
|
452
|
+
if (fs.existsSync(envPath)) {
|
|
453
|
+
// Append if not already present
|
|
454
|
+
const existingEnv = fs.readFileSync(envPath, 'utf8');
|
|
455
|
+
if (!existingEnv.includes('EXGUARD_API_URL')) {
|
|
456
|
+
fs.appendFileSync(envPath, '\\n\\n# ExGuard Configuration\\n' + envContent);
|
|
457
|
+
logSuccess('Appended ExGuard configuration to .env');
|
|
458
|
+
} else {
|
|
459
|
+
logWarning('ExGuard configuration already exists in .env');
|
|
460
|
+
}
|
|
461
|
+
} else {
|
|
462
|
+
fs.writeFileSync(envPath, envContent);
|
|
463
|
+
logSuccess('Created .env with ExGuard configuration');
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
// Update package.json scripts
|
|
468
|
+
function updatePackageScripts() {
|
|
469
|
+
logStep(8, 'Updating Package Scripts');
|
|
470
|
+
|
|
471
|
+
const packageJsonPath = path.join(process.cwd(), 'package.json');
|
|
472
|
+
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
|
|
473
|
+
|
|
474
|
+
const scriptsToAdd = {
|
|
475
|
+
'exguard:setup': 'node node_modules/exguard-backend/scripts/setup-nestjs.js',
|
|
476
|
+
'exguard:example': 'node node_modules/exguard-backend/scripts/create-example.js'
|
|
477
|
+
};
|
|
478
|
+
|
|
479
|
+
if (!packageJson.scripts) {
|
|
480
|
+
packageJson.scripts = {};
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
Object.entries(scriptsToAdd).forEach(([script, command]) => {
|
|
484
|
+
if (!packageJson.scripts[script]) {
|
|
485
|
+
packageJson.scripts[script] = command;
|
|
486
|
+
logSuccess(`Added script: ${script}`);
|
|
487
|
+
} else {
|
|
488
|
+
logWarning(`Script already exists: ${script}`);
|
|
489
|
+
}
|
|
490
|
+
});
|
|
491
|
+
|
|
492
|
+
fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2));
|
|
493
|
+
logSuccess('Updated package.json scripts');
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
// Main setup function
|
|
497
|
+
async function setupExGuard() {
|
|
498
|
+
log('š ExGuard NestJS Automatic Setup', 'bright');
|
|
499
|
+
log('This script will automatically set up ExGuard in your NestJS project.\\n');
|
|
500
|
+
|
|
501
|
+
try {
|
|
502
|
+
checkNestJSProject();
|
|
503
|
+
createDirectoryStructure();
|
|
504
|
+
createExGuardModule();
|
|
505
|
+
createExGuardGuards();
|
|
506
|
+
createExGuardDecorators();
|
|
507
|
+
createExampleController();
|
|
508
|
+
updateAppModule();
|
|
509
|
+
createEnvironmentFile();
|
|
510
|
+
updatePackageScripts();
|
|
511
|
+
|
|
512
|
+
log('\\nš ExGuard setup completed successfully!', 'green');
|
|
513
|
+
log('\\nš Next steps:', 'cyan');
|
|
514
|
+
log('1. Review the generated files in src/exguard/', 'blue');
|
|
515
|
+
log('2. Check the example controller in src/events/', 'blue');
|
|
516
|
+
log('3. Update your .env file with your ExGuard API URL', 'blue');
|
|
517
|
+
log('4. Start your application: npm run start:dev', 'blue');
|
|
518
|
+
log('5. Test the protected endpoints', 'blue');
|
|
519
|
+
|
|
520
|
+
log('\\nš Documentation:', 'cyan');
|
|
521
|
+
log('- Main README: node_modules/exguard-backend/README.md', 'blue');
|
|
522
|
+
log('- NestJS Guide: node_modules/exguard-backend/examples/NESTJS-SETUP.md', 'blue');
|
|
523
|
+
|
|
524
|
+
} catch (error) {
|
|
525
|
+
logError(`Setup failed: ${error.message}`);
|
|
526
|
+
process.exit(1);
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
// Run setup if called directly
|
|
531
|
+
if (require.main === module) {
|
|
532
|
+
setupExGuard();
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
module.exports = { setupExGuard };
|