@cepseudo/adonis-audit-log 1.0.0 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.md +9 -9
- package/README.md +201 -190
- package/build/commands/audit_cleanup.d.ts +8 -0
- package/build/commands/audit_cleanup.js +64 -0
- package/build/commands/commands.json +18 -0
- package/build/commands/main.d.ts +9 -0
- package/build/commands/main.js +37 -0
- package/build/configure.js +7 -22
- package/build/middleware/request_logger.d.ts +3 -0
- package/build/middleware/request_logger.js +6 -13
- package/build/providers/audit_provider.js +43 -24
- package/build/services/main.d.ts +26 -2
- package/build/services/main.js +33 -6
- package/build/stubs/config.stub +41 -40
- package/build/stubs/migrations/create_audit_logs_table.stub +27 -24
- package/build/stubs/migrations/create_error_logs_table.stub +34 -31
- package/build/stubs/migrations/create_request_logs_table.stub +36 -33
- package/package.json +104 -102
package/LICENSE.md
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
# The MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2023
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
6
|
-
|
|
7
|
-
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
8
|
-
|
|
9
|
-
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
1
|
+
# The MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2023
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
6
|
+
|
|
7
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
8
|
+
|
|
9
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,190 +1,201 @@
|
|
|
1
|
-
# @cepseudo/adonis-audit-log
|
|
2
|
-
|
|
3
|
-
Simple audit logging package for AdonisJS 6.
|
|
4
|
-
|
|
5
|
-
## Features
|
|
6
|
-
|
|
7
|
-
- Custom audit logs for tracking user actions and business events
|
|
8
|
-
- Automatic HTTP request logging via middleware
|
|
9
|
-
- Error logging with exception handler integration
|
|
10
|
-
- Configurable field sanitization for sensitive data
|
|
11
|
-
- Retention settings for log cleanup
|
|
12
|
-
|
|
13
|
-
## Installation
|
|
14
|
-
|
|
15
|
-
```bash
|
|
16
|
-
npm install @cepseudo/adonis-audit-log
|
|
17
|
-
```
|
|
18
|
-
|
|
19
|
-
## Configuration
|
|
20
|
-
|
|
21
|
-
```bash
|
|
22
|
-
node ace configure @cepseudo/adonis-audit-log
|
|
23
|
-
```
|
|
24
|
-
|
|
25
|
-
This will:
|
|
26
|
-
|
|
27
|
-
1. Publish migrations to `database/migrations/`
|
|
28
|
-
2. Publish config to `config/audit.ts`
|
|
29
|
-
3. Register the provider in `adonisrc.ts`
|
|
30
|
-
|
|
31
|
-
Then run the migrations:
|
|
32
|
-
|
|
33
|
-
```bash
|
|
34
|
-
node ace migration:run
|
|
35
|
-
```
|
|
36
|
-
|
|
37
|
-
## Configuration Options
|
|
38
|
-
|
|
39
|
-
```typescript
|
|
40
|
-
// config/audit.ts
|
|
41
|
-
import { defineConfig } from '@cepseudo/adonis-audit-log'
|
|
42
|
-
|
|
43
|
-
export default defineConfig({
|
|
44
|
-
enabled: true,
|
|
45
|
-
|
|
46
|
-
requestLog: {
|
|
47
|
-
enabled: true,
|
|
48
|
-
excludeRoutes: ['/health', '/metrics'],
|
|
49
|
-
excludeMethods: ['OPTIONS'],
|
|
50
|
-
logBody: false,
|
|
51
|
-
logQuery: true,
|
|
52
|
-
sanitizeFields: ['password', 'token', 'secret'],
|
|
53
|
-
},
|
|
54
|
-
|
|
55
|
-
errorLog: {
|
|
56
|
-
enabled: true,
|
|
57
|
-
excludeStatusCodes: [404],
|
|
58
|
-
includeStack: true,
|
|
59
|
-
},
|
|
60
|
-
|
|
61
|
-
auditLog: {
|
|
62
|
-
enabled: true,
|
|
63
|
-
},
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
- `
|
|
141
|
-
- `
|
|
142
|
-
- `
|
|
143
|
-
- `
|
|
144
|
-
- `
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
- `
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
- `
|
|
153
|
-
- `
|
|
154
|
-
- `
|
|
155
|
-
- `
|
|
156
|
-
- `
|
|
157
|
-
- `
|
|
158
|
-
- `
|
|
159
|
-
- `
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
1
|
+
# @cepseudo/adonis-audit-log
|
|
2
|
+
|
|
3
|
+
Simple audit logging package for AdonisJS 6.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- Custom audit logs for tracking user actions and business events
|
|
8
|
+
- Automatic HTTP request logging via middleware
|
|
9
|
+
- Error logging with exception handler integration
|
|
10
|
+
- Configurable field sanitization for sensitive data
|
|
11
|
+
- Retention settings for log cleanup
|
|
12
|
+
|
|
13
|
+
## Installation
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npm install @cepseudo/adonis-audit-log
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Configuration
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
node ace configure @cepseudo/adonis-audit-log
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
This will:
|
|
26
|
+
|
|
27
|
+
1. Publish migrations to `database/migrations/`
|
|
28
|
+
2. Publish config to `config/audit.ts`
|
|
29
|
+
3. Register the provider and command in `adonisrc.ts`
|
|
30
|
+
|
|
31
|
+
Then run the migrations:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
node ace migration:run
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Configuration Options
|
|
38
|
+
|
|
39
|
+
```typescript
|
|
40
|
+
// config/audit.ts
|
|
41
|
+
import { defineConfig } from '@cepseudo/adonis-audit-log'
|
|
42
|
+
|
|
43
|
+
export default defineConfig({
|
|
44
|
+
enabled: true,
|
|
45
|
+
|
|
46
|
+
requestLog: {
|
|
47
|
+
enabled: true,
|
|
48
|
+
excludeRoutes: ['/health', '/metrics'],
|
|
49
|
+
excludeMethods: ['OPTIONS'],
|
|
50
|
+
logBody: false,
|
|
51
|
+
logQuery: true,
|
|
52
|
+
sanitizeFields: ['password', 'token', 'secret'],
|
|
53
|
+
},
|
|
54
|
+
|
|
55
|
+
errorLog: {
|
|
56
|
+
enabled: true,
|
|
57
|
+
excludeStatusCodes: [404],
|
|
58
|
+
includeStack: true,
|
|
59
|
+
},
|
|
60
|
+
|
|
61
|
+
auditLog: {
|
|
62
|
+
enabled: true,
|
|
63
|
+
},
|
|
64
|
+
|
|
65
|
+
// Retention in days (0 = unlimited)
|
|
66
|
+
retention: {
|
|
67
|
+
requestLogs: 30,
|
|
68
|
+
errorLogs: 90,
|
|
69
|
+
auditLogs: 0, // Keep forever
|
|
70
|
+
},
|
|
71
|
+
})
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## Usage
|
|
75
|
+
|
|
76
|
+
### Custom Audit Logs
|
|
77
|
+
|
|
78
|
+
```typescript
|
|
79
|
+
import audit from '@cepseudo/adonis-audit-log/services/main'
|
|
80
|
+
|
|
81
|
+
// Simple log
|
|
82
|
+
await audit.log('user.login', { user_id: 1 })
|
|
83
|
+
|
|
84
|
+
// With metadata
|
|
85
|
+
await audit.log('submission.create', {
|
|
86
|
+
user_id: auth.user.id,
|
|
87
|
+
submission_id: submission.id,
|
|
88
|
+
project_acronym: submission.projectAcronym,
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
// Track changes
|
|
92
|
+
await audit.logChange('user.update', {
|
|
93
|
+
userId: auth.user.id,
|
|
94
|
+
targetId: targetUser.id,
|
|
95
|
+
changes: {
|
|
96
|
+
email: { from: 'old@example.com', to: 'new@example.com' },
|
|
97
|
+
},
|
|
98
|
+
})
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### Request Logging (Middleware)
|
|
102
|
+
|
|
103
|
+
Add the middleware to your router in `start/kernel.ts`:
|
|
104
|
+
|
|
105
|
+
```typescript
|
|
106
|
+
router.use([() => import('@cepseudo/adonis-audit-log/middleware/request_logger')])
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
### Error Logging (Exception Handler)
|
|
110
|
+
|
|
111
|
+
Integrate with your exception handler in `app/exceptions/handler.ts`:
|
|
112
|
+
|
|
113
|
+
```typescript
|
|
114
|
+
import { AuditErrorLogger } from '@cepseudo/adonis-audit-log'
|
|
115
|
+
|
|
116
|
+
export default class HttpExceptionHandler extends ExceptionHandler {
|
|
117
|
+
async report(error: unknown, ctx: HttpContext) {
|
|
118
|
+
await AuditErrorLogger.log(error, ctx)
|
|
119
|
+
return super.report(error, ctx)
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### Log Cleanup
|
|
125
|
+
|
|
126
|
+
Delete old logs based on retention configuration:
|
|
127
|
+
|
|
128
|
+
```bash
|
|
129
|
+
node ace audit:cleanup
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
Set retention to `0` for unlimited retention (logs won't be deleted).
|
|
133
|
+
|
|
134
|
+
## Database Schema
|
|
135
|
+
|
|
136
|
+
The package creates three tables:
|
|
137
|
+
|
|
138
|
+
### audit_logs
|
|
139
|
+
|
|
140
|
+
- `id` - Primary key
|
|
141
|
+
- `action_type` - Event type (e.g., 'user.login', 'submission.create')
|
|
142
|
+
- `metadata` - JSON object with additional data
|
|
143
|
+
- `user_id` - Optional reference to users table
|
|
144
|
+
- `created_at` - Timestamp
|
|
145
|
+
|
|
146
|
+
### request_logs
|
|
147
|
+
|
|
148
|
+
- `id` - Primary key
|
|
149
|
+
- `method` - HTTP method
|
|
150
|
+
- `url` - Request URL
|
|
151
|
+
- `route_name` - Named route if available
|
|
152
|
+
- `status_code` - HTTP response status
|
|
153
|
+
- `response_time_ms` - Response time in milliseconds
|
|
154
|
+
- `ip` - Client IP address
|
|
155
|
+
- `user_agent` - Browser/client user agent
|
|
156
|
+
- `user_id` - Optional reference to users table
|
|
157
|
+
- `request_body` - Sanitized request body (optional)
|
|
158
|
+
- `request_query` - Query parameters
|
|
159
|
+
- `created_at` - Timestamp
|
|
160
|
+
|
|
161
|
+
### error_logs
|
|
162
|
+
|
|
163
|
+
- `id` - Primary key
|
|
164
|
+
- `error_type` - Error class name
|
|
165
|
+
- `message` - Error message
|
|
166
|
+
- `stack` - Stack trace (optional)
|
|
167
|
+
- `url` - URL where error occurred
|
|
168
|
+
- `method` - HTTP method
|
|
169
|
+
- `status_code` - HTTP status returned
|
|
170
|
+
- `user_id` - Optional reference to users table
|
|
171
|
+
- `context` - Additional context (params, query)
|
|
172
|
+
- `created_at` - Timestamp
|
|
173
|
+
|
|
174
|
+
## Development
|
|
175
|
+
|
|
176
|
+
```bash
|
|
177
|
+
# Install dependencies
|
|
178
|
+
npm install
|
|
179
|
+
|
|
180
|
+
# Run tests
|
|
181
|
+
npm run test
|
|
182
|
+
|
|
183
|
+
# Run tests without linting
|
|
184
|
+
npm run quick:test
|
|
185
|
+
|
|
186
|
+
# Build
|
|
187
|
+
npm run build
|
|
188
|
+
|
|
189
|
+
# Type check
|
|
190
|
+
npm run typecheck
|
|
191
|
+
|
|
192
|
+
# Lint
|
|
193
|
+
npm run lint
|
|
194
|
+
|
|
195
|
+
# Format
|
|
196
|
+
npm run format
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
## License
|
|
200
|
+
|
|
201
|
+
MIT
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { BaseCommand } from '@adonisjs/core/ace';
|
|
2
|
+
import { CommandOptions } from '@adonisjs/core/types/ace';
|
|
3
|
+
export default class AuditCleanup extends BaseCommand {
|
|
4
|
+
static commandName: string;
|
|
5
|
+
static description: string;
|
|
6
|
+
static options: CommandOptions;
|
|
7
|
+
run(): Promise<void>;
|
|
8
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/*
|
|
2
|
+
|--------------------------------------------------------------------------
|
|
3
|
+
| Audit Cleanup Command
|
|
4
|
+
|--------------------------------------------------------------------------
|
|
5
|
+
|
|
|
6
|
+
| Command to delete old audit logs based on retention configuration.
|
|
7
|
+
| Run with: node ace audit:cleanup
|
|
8
|
+
|
|
|
9
|
+
*/
|
|
10
|
+
import { BaseCommand } from '@adonisjs/core/ace';
|
|
11
|
+
import { DateTime } from 'luxon';
|
|
12
|
+
export default class AuditCleanup extends BaseCommand {
|
|
13
|
+
static commandName = 'audit:cleanup';
|
|
14
|
+
static description = 'Delete old audit logs based on retention configuration';
|
|
15
|
+
static options = {
|
|
16
|
+
startApp: true,
|
|
17
|
+
};
|
|
18
|
+
async run() {
|
|
19
|
+
const { default: AuditLog } = await import('../src/models/audit_log.js');
|
|
20
|
+
const { default: RequestLog } = await import('../src/models/request_log.js');
|
|
21
|
+
const { default: ErrorLog } = await import('../src/models/error_log.js');
|
|
22
|
+
const config = this.app.config.get('audit');
|
|
23
|
+
if (!config) {
|
|
24
|
+
this.logger.error('Audit config not found. Make sure config/audit.ts exists.');
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
const retention = config.retention;
|
|
28
|
+
let totalDeleted = 0;
|
|
29
|
+
// Cleanup audit logs
|
|
30
|
+
if (retention.auditLogs > 0) {
|
|
31
|
+
const cutoff = DateTime.now().minus({ days: retention.auditLogs });
|
|
32
|
+
const deleted = await AuditLog.query().where('createdAt', '<', cutoff.toJSDate()).delete();
|
|
33
|
+
const count = Array.isArray(deleted) ? deleted.length : deleted;
|
|
34
|
+
this.logger.info(`Deleted ${count} audit logs older than ${retention.auditLogs} days`);
|
|
35
|
+
totalDeleted += count;
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
this.logger.info('Audit logs: retention unlimited (skipped)');
|
|
39
|
+
}
|
|
40
|
+
// Cleanup request logs
|
|
41
|
+
if (retention.requestLogs > 0) {
|
|
42
|
+
const cutoff = DateTime.now().minus({ days: retention.requestLogs });
|
|
43
|
+
const deleted = await RequestLog.query().where('createdAt', '<', cutoff.toJSDate()).delete();
|
|
44
|
+
const count = Array.isArray(deleted) ? deleted.length : deleted;
|
|
45
|
+
this.logger.info(`Deleted ${count} request logs older than ${retention.requestLogs} days`);
|
|
46
|
+
totalDeleted += count;
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
this.logger.info('Request logs: retention unlimited (skipped)');
|
|
50
|
+
}
|
|
51
|
+
// Cleanup error logs
|
|
52
|
+
if (retention.errorLogs > 0) {
|
|
53
|
+
const cutoff = DateTime.now().minus({ days: retention.errorLogs });
|
|
54
|
+
const deleted = await ErrorLog.query().where('createdAt', '<', cutoff.toJSDate()).delete();
|
|
55
|
+
const count = Array.isArray(deleted) ? deleted.length : deleted;
|
|
56
|
+
this.logger.info(`Deleted ${count} error logs older than ${retention.errorLogs} days`);
|
|
57
|
+
totalDeleted += count;
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
this.logger.info('Error logs: retention unlimited (skipped)');
|
|
61
|
+
}
|
|
62
|
+
this.logger.success(`Cleanup complete. Total deleted: ${totalDeleted}`);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"commands": [
|
|
3
|
+
{
|
|
4
|
+
"commandName": "audit:cleanup",
|
|
5
|
+
"description": "Delete old audit logs based on retention configuration",
|
|
6
|
+
"help": "",
|
|
7
|
+
"namespace": "audit",
|
|
8
|
+
"aliases": [],
|
|
9
|
+
"flags": [],
|
|
10
|
+
"args": [],
|
|
11
|
+
"options": {
|
|
12
|
+
"startApp": true
|
|
13
|
+
},
|
|
14
|
+
"filePath": "./audit_cleanup.js"
|
|
15
|
+
}
|
|
16
|
+
],
|
|
17
|
+
"version": 1
|
|
18
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { CommandMetaData } from '@adonisjs/core/types/ace';
|
|
2
|
+
/**
|
|
3
|
+
* Reads the commands from the "./commands.json" file.
|
|
4
|
+
*/
|
|
5
|
+
export declare function getMetaData(): Promise<CommandMetaData[]>;
|
|
6
|
+
/**
|
|
7
|
+
* Imports the command by looking up its path from the commands metadata
|
|
8
|
+
*/
|
|
9
|
+
export declare function getCommand(metaData: CommandMetaData): Promise<typeof import('./audit_cleanup.js').default | null>;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/*
|
|
2
|
+
|--------------------------------------------------------------------------
|
|
3
|
+
| Commands Loader
|
|
4
|
+
|--------------------------------------------------------------------------
|
|
5
|
+
|
|
|
6
|
+
| This file exports the getMetaData and getCommand functions required
|
|
7
|
+
| by AdonisJS to load commands from packages.
|
|
8
|
+
|
|
|
9
|
+
*/
|
|
10
|
+
import { readFile } from 'node:fs/promises';
|
|
11
|
+
/**
|
|
12
|
+
* In-memory cache of commands after they have been loaded
|
|
13
|
+
*/
|
|
14
|
+
let commandsMetaData;
|
|
15
|
+
/**
|
|
16
|
+
* Reads the commands from the "./commands.json" file.
|
|
17
|
+
*/
|
|
18
|
+
export async function getMetaData() {
|
|
19
|
+
if (commandsMetaData) {
|
|
20
|
+
return commandsMetaData;
|
|
21
|
+
}
|
|
22
|
+
const commandsIndex = await readFile(new URL('./commands.json', import.meta.url), 'utf-8');
|
|
23
|
+
commandsMetaData = JSON.parse(commandsIndex).commands;
|
|
24
|
+
return commandsMetaData;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Imports the command by looking up its path from the commands metadata
|
|
28
|
+
*/
|
|
29
|
+
export async function getCommand(metaData) {
|
|
30
|
+
const commands = await getMetaData();
|
|
31
|
+
const command = commands.find(({ commandName }) => metaData.commandName === commandName);
|
|
32
|
+
if (!command) {
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
const { default: commandConstructor } = await import(new URL(command.filePath, import.meta.url).href);
|
|
36
|
+
return commandConstructor;
|
|
37
|
+
}
|
package/build/configure.js
CHANGED
|
@@ -12,47 +12,32 @@ export async function configure(command) {
|
|
|
12
12
|
const codemods = await command.createCodemods();
|
|
13
13
|
// Publish config file
|
|
14
14
|
await codemods.makeUsingStub(stubsRoot, 'config.stub', {});
|
|
15
|
-
// Publish migrations
|
|
16
|
-
const
|
|
17
|
-
const timestamp = now
|
|
18
|
-
.toISOString()
|
|
19
|
-
.replace(/[-:]/g, '')
|
|
20
|
-
.replace('T', '_')
|
|
21
|
-
.replace(/\.\d{3}Z$/, '');
|
|
15
|
+
// Publish migrations using Unix timestamp in milliseconds (AdonisJS standard format)
|
|
16
|
+
const timestamp = Date.now();
|
|
22
17
|
await codemods.makeUsingStub(stubsRoot, 'migrations/create_audit_logs_table.stub', {
|
|
23
18
|
migration: {
|
|
24
19
|
folder: 'database/migrations',
|
|
25
20
|
fileName: `${timestamp}_create_audit_logs_table.ts`,
|
|
26
21
|
},
|
|
27
22
|
});
|
|
28
|
-
// Add 1 second to timestamp for ordering
|
|
29
|
-
const timestamp2 = new Date(now.getTime() + 1000)
|
|
30
|
-
.toISOString()
|
|
31
|
-
.replace(/[-:]/g, '')
|
|
32
|
-
.replace('T', '_')
|
|
33
|
-
.replace(/\.\d{3}Z$/, '');
|
|
34
23
|
await codemods.makeUsingStub(stubsRoot, 'migrations/create_request_logs_table.stub', {
|
|
35
24
|
migration: {
|
|
36
25
|
folder: 'database/migrations',
|
|
37
|
-
fileName: `${
|
|
26
|
+
fileName: `${timestamp + 1}_create_request_logs_table.ts`,
|
|
38
27
|
},
|
|
39
28
|
});
|
|
40
|
-
// Add 2 seconds to timestamp for ordering
|
|
41
|
-
const timestamp3 = new Date(now.getTime() + 2000)
|
|
42
|
-
.toISOString()
|
|
43
|
-
.replace(/[-:]/g, '')
|
|
44
|
-
.replace('T', '_')
|
|
45
|
-
.replace(/\.\d{3}Z$/, '');
|
|
46
29
|
await codemods.makeUsingStub(stubsRoot, 'migrations/create_error_logs_table.stub', {
|
|
47
30
|
migration: {
|
|
48
31
|
folder: 'database/migrations',
|
|
49
|
-
fileName: `${
|
|
32
|
+
fileName: `${timestamp + 2}_create_error_logs_table.ts`,
|
|
50
33
|
},
|
|
51
34
|
});
|
|
52
|
-
// Register provider in adonisrc.ts
|
|
35
|
+
// Register provider and command in adonisrc.ts
|
|
53
36
|
await codemods.updateRcFile((rcFile) => {
|
|
54
37
|
rcFile.addProvider('@cepseudo/adonis-audit-log/providers/audit_provider');
|
|
38
|
+
rcFile.addCommand('@cepseudo/adonis-audit-log/commands');
|
|
55
39
|
});
|
|
56
40
|
command.logger.success('Audit log package configured successfully!');
|
|
57
41
|
command.logger.info('Run "node ace migration:run" to create the audit tables.');
|
|
42
|
+
command.logger.info('Use "node ace audit:cleanup" to delete old logs based on retention config.');
|
|
58
43
|
}
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import type { HttpContext } from '@adonisjs/core/http';
|
|
2
2
|
import type { NextFn } from '@adonisjs/core/types/http';
|
|
3
|
+
import type { AuditConfig } from '../src/types.js';
|
|
3
4
|
export default class RequestLoggerMiddleware {
|
|
5
|
+
#private;
|
|
6
|
+
constructor(config: AuditConfig);
|
|
4
7
|
handle(ctx: HttpContext, next: NextFn): Promise<void>;
|
|
5
8
|
}
|
|
@@ -7,27 +7,20 @@
|
|
|
7
7
|
| middleware stack in start/kernel.ts.
|
|
8
8
|
|
|
|
9
9
|
*/
|
|
10
|
-
import app from '@adonisjs/core/services/app';
|
|
11
10
|
import { RequestLogger } from '../src/request_logger.js';
|
|
12
|
-
let requestLogger = null;
|
|
13
11
|
export default class RequestLoggerMiddleware {
|
|
12
|
+
#requestLogger;
|
|
13
|
+
constructor(config) {
|
|
14
|
+
this.#requestLogger = new RequestLogger(config);
|
|
15
|
+
}
|
|
14
16
|
async handle(ctx, next) {
|
|
15
17
|
const startTime = performance.now();
|
|
16
18
|
// Execute the request
|
|
17
19
|
await next();
|
|
18
20
|
// Log the request after response (non-blocking)
|
|
19
21
|
try {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
if (config) {
|
|
23
|
-
requestLogger = new RequestLogger(config);
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
if (requestLogger) {
|
|
27
|
-
const responseTimeMs = Math.round(performance.now() - startTime);
|
|
28
|
-
// Use async version to not block the response
|
|
29
|
-
requestLogger.logAsync(ctx, responseTimeMs);
|
|
30
|
-
}
|
|
22
|
+
const responseTimeMs = Math.round(performance.now() - startTime);
|
|
23
|
+
this.#requestLogger.logAsync(ctx, responseTimeMs);
|
|
31
24
|
}
|
|
32
25
|
catch (error) {
|
|
33
26
|
console.error('[RequestLoggerMiddleware] Error:', error);
|