@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.
@@ -9,41 +9,60 @@
9
9
  */
10
10
  import { AuditService } from '../src/audit_service.js';
11
11
  import { AuditErrorLogger } from '../src/error_logger.js';
12
+ import RequestLoggerMiddleware from '../middleware/request_logger.js';
13
+ const defaultConfig = {
14
+ enabled: true,
15
+ requestLog: {
16
+ enabled: true,
17
+ excludeRoutes: [],
18
+ excludeMethods: ['OPTIONS'],
19
+ logBody: false,
20
+ logQuery: true,
21
+ sanitizeFields: ['password', 'token', 'secret'],
22
+ },
23
+ errorLog: {
24
+ enabled: true,
25
+ excludeStatusCodes: [404],
26
+ includeStack: true,
27
+ },
28
+ auditLog: {
29
+ enabled: true,
30
+ },
31
+ retention: {
32
+ requestLogs: 30,
33
+ errorLogs: 90,
34
+ auditLogs: 365,
35
+ },
36
+ };
12
37
  export default class AuditProvider {
13
38
  app;
14
39
  constructor(app) {
15
40
  this.app = app;
16
41
  }
17
42
  register() {
43
+ // Register AuditService
18
44
  this.app.container.singleton('audit', () => {
19
- const config = this.app.config.get('audit', {
20
- enabled: true,
21
- requestLog: {
22
- enabled: true,
23
- excludeRoutes: [],
24
- excludeMethods: ['OPTIONS'],
25
- logBody: false,
26
- logQuery: true,
27
- sanitizeFields: ['password', 'token', 'secret'],
28
- },
29
- errorLog: {
30
- enabled: true,
31
- excludeStatusCodes: [404],
32
- includeStack: true,
33
- },
34
- auditLog: {
35
- enabled: true,
36
- },
37
- retention: {
38
- requestLogs: 30,
39
- errorLogs: 90,
40
- auditLogs: 365,
41
- },
42
- });
45
+ const config = this.app.config.get('audit', defaultConfig);
43
46
  return new AuditService(config);
44
47
  });
48
+ // Register RequestLoggerMiddleware
49
+ this.app.container.bind(RequestLoggerMiddleware, () => {
50
+ const config = this.app.config.get('audit', defaultConfig);
51
+ return new RequestLoggerMiddleware(config);
52
+ });
45
53
  }
46
54
  async boot() {
55
+ // Get the database service from the app container
56
+ const db = (await this.app.container.make('lucid.db'));
57
+ // Import and configure models with the database adapter
58
+ const { default: AuditLog } = await import('../src/models/audit_log.js');
59
+ const { default: RequestLog } = await import('../src/models/request_log.js');
60
+ const { default: ErrorLog } = await import('../src/models/error_log.js');
61
+ // Use the default adapter from Lucid
62
+ const adapter = db.modelAdapter();
63
+ AuditLog.$adapter = adapter;
64
+ RequestLog.$adapter = adapter;
65
+ ErrorLog.$adapter = adapter;
47
66
  const config = this.app.config.get('audit');
48
67
  if (config) {
49
68
  AuditErrorLogger.init(config);
@@ -1,3 +1,27 @@
1
- import { AuditService } from '../src/audit_service.js';
2
- declare let audit: AuditService;
1
+ /**
2
+ * Audit service proxy that provides the same interface as AuditService
3
+ * but lazy-loads from the container
4
+ */
5
+ declare const audit: {
6
+ log(actionType: string, metadata?: Record<string, unknown>): Promise<import("../index.js").AuditLog | null>;
7
+ logAsync(actionType: string, metadata?: Record<string, unknown>): void;
8
+ logChange(actionType: string, options: {
9
+ userId?: number | null;
10
+ targetId?: number | string;
11
+ changes: Record<string, {
12
+ from: unknown;
13
+ to: unknown;
14
+ }>;
15
+ extra?: Record<string, unknown>;
16
+ }): Promise<import("../index.js").AuditLog | null>;
17
+ logChangeAsync(actionType: string, options: {
18
+ userId?: number | null;
19
+ targetId?: number | string;
20
+ changes: Record<string, {
21
+ from: unknown;
22
+ to: unknown;
23
+ }>;
24
+ extra?: Record<string, unknown>;
25
+ }): void;
26
+ };
3
27
  export { audit as default };
@@ -3,13 +3,40 @@
3
3
  | Audit Service Singleton
4
4
  |--------------------------------------------------------------------------
5
5
  |
6
- | This file exports the audit service singleton that can be imported
7
- | anywhere in the application.
6
+ | This file exports a proxy to the audit service that lazy-loads from
7
+ | the container when first accessed.
8
8
  |
9
9
  */
10
10
  import app from '@adonisjs/core/services/app';
11
- let audit;
12
- await app.booted(async () => {
13
- audit = await app.container.make('audit');
14
- });
11
+ let auditService = null;
12
+ async function getAuditService() {
13
+ if (!auditService) {
14
+ auditService = await app.container.make('audit');
15
+ }
16
+ return auditService;
17
+ }
18
+ /**
19
+ * Audit service proxy that provides the same interface as AuditService
20
+ * but lazy-loads from the container
21
+ */
22
+ const audit = {
23
+ async log(actionType, metadata = {}) {
24
+ const service = await getAuditService();
25
+ return service.log(actionType, metadata);
26
+ },
27
+ logAsync(actionType, metadata = {}) {
28
+ getAuditService()
29
+ .then((service) => service.logAsync(actionType, metadata))
30
+ .catch((err) => console.error('[AuditService] Failed to log async:', err));
31
+ },
32
+ async logChange(actionType, options) {
33
+ const service = await getAuditService();
34
+ return service.logChange(actionType, options);
35
+ },
36
+ logChangeAsync(actionType, options) {
37
+ getAuditService()
38
+ .then((service) => service.logChangeAsync(actionType, options))
39
+ .catch((err) => console.error('[AuditService] Failed to log change async:', err));
40
+ },
41
+ };
15
42
  export { audit as default };
@@ -1,40 +1,41 @@
1
- {{{
2
- exports({
3
- to: app.configPath('audit.ts')
4
- })
5
- }}}
6
- import { defineConfig } from '@cepseudo/adonis-audit-log'
7
-
8
- export default defineConfig({
9
- // Enable/disable all logging
10
- enabled: true,
11
-
12
- // Request logging options
13
- requestLog: {
14
- enabled: true,
15
- excludeRoutes: ['/health', '/metrics'],
16
- excludeMethods: ['OPTIONS'],
17
- logBody: false,
18
- logQuery: true,
19
- sanitizeFields: ['password', 'token', 'secret'],
20
- },
21
-
22
- // Error logging options
23
- errorLog: {
24
- enabled: true,
25
- excludeStatusCodes: [404],
26
- includeStack: true,
27
- },
28
-
29
- // Audit log options
30
- auditLog: {
31
- enabled: true,
32
- },
33
-
34
- // Retention (days) - for cleanup job
35
- retention: {
36
- requestLogs: 30,
37
- errorLogs: 90,
38
- auditLogs: 365,
39
- },
40
- })
1
+ {{{
2
+ exports({
3
+ to: app.configPath('audit.ts')
4
+ })
5
+ }}}
6
+ import { defineConfig } from '@cepseudo/adonis-audit-log'
7
+
8
+ export default defineConfig({
9
+ // Enable/disable all logging
10
+ enabled: true,
11
+
12
+ // Request logging options
13
+ requestLog: {
14
+ enabled: true,
15
+ excludeRoutes: ['/health', '/metrics'],
16
+ excludeMethods: ['OPTIONS'],
17
+ logBody: false,
18
+ logQuery: true,
19
+ sanitizeFields: ['password', 'token', 'secret'],
20
+ },
21
+
22
+ // Error logging options
23
+ errorLog: {
24
+ enabled: true,
25
+ excludeStatusCodes: [404],
26
+ includeStack: true,
27
+ },
28
+
29
+ // Audit log options
30
+ auditLog: {
31
+ enabled: true,
32
+ },
33
+
34
+ // Retention in days (0 = unlimited, no cleanup)
35
+ // Run "node ace audit:cleanup" to delete old logs
36
+ retention: {
37
+ requestLogs: 30,
38
+ errorLogs: 90,
39
+ auditLogs: 0, // Keep forever
40
+ },
41
+ })
@@ -1,24 +1,27 @@
1
- {{#var tableName = 'audit_logs'}}
2
- {{#var migrationName = 'create_' + tableName + '_table'}}
3
- import { BaseSchema } from '@adonisjs/lucid/schema'
4
-
5
- export default class extends BaseSchema {
6
- protected tableName = '{{ tableName }}'
7
-
8
- async up() {
9
- this.schema.createTable(this.tableName, (table) => {
10
- table.increments('id')
11
- table.string('action_type').notNullable().index()
12
- table.jsonb('metadata').notNullable().defaultTo('{}')
13
- table.integer('user_id').unsigned().nullable().references('id').inTable('users').onDelete('SET NULL')
14
- table.timestamp('created_at').notNullable().defaultTo(this.now())
15
-
16
- table.index(['created_at'])
17
- table.index(['user_id'])
18
- })
19
- }
20
-
21
- async down() {
22
- this.schema.dropTable(this.tableName)
23
- }
24
- }
1
+ {{{
2
+ exports({
3
+ to: app.makePath(migration.folder, migration.fileName)
4
+ })
5
+ }}}
6
+ import { BaseSchema } from '@adonisjs/lucid/schema'
7
+
8
+ export default class extends BaseSchema {
9
+ protected tableName = 'audit_logs'
10
+
11
+ async up() {
12
+ this.schema.createTable(this.tableName, (table) => {
13
+ table.increments('id')
14
+ table.string('action_type').notNullable().index()
15
+ table.jsonb('metadata').notNullable().defaultTo('{}')
16
+ table.integer('user_id').unsigned().nullable().references('id').inTable('users').onDelete('SET NULL')
17
+ table.timestamp('created_at').notNullable().defaultTo(this.now())
18
+
19
+ table.index(['created_at'])
20
+ table.index(['user_id'])
21
+ })
22
+ }
23
+
24
+ async down() {
25
+ this.schema.dropTable(this.tableName)
26
+ }
27
+ }
@@ -1,31 +1,34 @@
1
- {{#var tableName = 'error_logs'}}
2
- {{#var migrationName = 'create_' + tableName + '_table'}}
3
- import { BaseSchema } from '@adonisjs/lucid/schema'
4
-
5
- export default class extends BaseSchema {
6
- protected tableName = '{{ tableName }}'
7
-
8
- async up() {
9
- this.schema.createTable(this.tableName, (table) => {
10
- table.increments('id')
11
- table.string('error_type').notNullable()
12
- table.text('message').notNullable()
13
- table.text('stack').nullable()
14
- table.string('url', 2048).nullable()
15
- table.string('method', 10).nullable()
16
- table.integer('status_code').nullable()
17
- table.integer('user_id').unsigned().nullable().references('id').inTable('users').onDelete('SET NULL')
18
- table.jsonb('context').nullable()
19
- table.timestamp('created_at').notNullable().defaultTo(this.now())
20
-
21
- table.index(['created_at'])
22
- table.index(['user_id'])
23
- table.index(['error_type'])
24
- table.index(['status_code'])
25
- })
26
- }
27
-
28
- async down() {
29
- this.schema.dropTable(this.tableName)
30
- }
31
- }
1
+ {{{
2
+ exports({
3
+ to: app.makePath(migration.folder, migration.fileName)
4
+ })
5
+ }}}
6
+ import { BaseSchema } from '@adonisjs/lucid/schema'
7
+
8
+ export default class extends BaseSchema {
9
+ protected tableName = 'error_logs'
10
+
11
+ async up() {
12
+ this.schema.createTable(this.tableName, (table) => {
13
+ table.increments('id')
14
+ table.string('error_type').notNullable()
15
+ table.text('message').notNullable()
16
+ table.text('stack').nullable()
17
+ table.string('url', 2048).nullable()
18
+ table.string('method', 10).nullable()
19
+ table.integer('status_code').nullable()
20
+ table.integer('user_id').unsigned().nullable().references('id').inTable('users').onDelete('SET NULL')
21
+ table.jsonb('context').nullable()
22
+ table.timestamp('created_at').notNullable().defaultTo(this.now())
23
+
24
+ table.index(['created_at'])
25
+ table.index(['user_id'])
26
+ table.index(['error_type'])
27
+ table.index(['status_code'])
28
+ })
29
+ }
30
+
31
+ async down() {
32
+ this.schema.dropTable(this.tableName)
33
+ }
34
+ }
@@ -1,33 +1,36 @@
1
- {{#var tableName = 'request_logs'}}
2
- {{#var migrationName = 'create_' + tableName + '_table'}}
3
- import { BaseSchema } from '@adonisjs/lucid/schema'
4
-
5
- export default class extends BaseSchema {
6
- protected tableName = '{{ tableName }}'
7
-
8
- async up() {
9
- this.schema.createTable(this.tableName, (table) => {
10
- table.increments('id')
11
- table.string('method', 10).notNullable()
12
- table.string('url', 2048).notNullable()
13
- table.string('route_name').nullable()
14
- table.integer('status_code').notNullable()
15
- table.integer('response_time_ms').notNullable()
16
- table.string('ip', 45).nullable()
17
- table.string('user_agent', 512).nullable()
18
- table.integer('user_id').unsigned().nullable().references('id').inTable('users').onDelete('SET NULL')
19
- table.jsonb('request_body').nullable()
20
- table.jsonb('request_query').nullable()
21
- table.timestamp('created_at').notNullable().defaultTo(this.now())
22
-
23
- table.index(['created_at'])
24
- table.index(['user_id'])
25
- table.index(['status_code'])
26
- table.index(['method'])
27
- })
28
- }
29
-
30
- async down() {
31
- this.schema.dropTable(this.tableName)
32
- }
33
- }
1
+ {{{
2
+ exports({
3
+ to: app.makePath(migration.folder, migration.fileName)
4
+ })
5
+ }}}
6
+ import { BaseSchema } from '@adonisjs/lucid/schema'
7
+
8
+ export default class extends BaseSchema {
9
+ protected tableName = 'request_logs'
10
+
11
+ async up() {
12
+ this.schema.createTable(this.tableName, (table) => {
13
+ table.increments('id')
14
+ table.string('method', 10).notNullable()
15
+ table.string('url', 2048).notNullable()
16
+ table.string('route_name').nullable()
17
+ table.integer('status_code').notNullable()
18
+ table.integer('response_time_ms').notNullable()
19
+ table.string('ip', 45).nullable()
20
+ table.string('user_agent', 512).nullable()
21
+ table.integer('user_id').unsigned().nullable().references('id').inTable('users').onDelete('SET NULL')
22
+ table.jsonb('request_body').nullable()
23
+ table.jsonb('request_query').nullable()
24
+ table.timestamp('created_at').notNullable().defaultTo(this.now())
25
+
26
+ table.index(['created_at'])
27
+ table.index(['user_id'])
28
+ table.index(['status_code'])
29
+ table.index(['method'])
30
+ })
31
+ }
32
+
33
+ async down() {
34
+ this.schema.dropTable(this.tableName)
35
+ }
36
+ }
package/package.json CHANGED
@@ -1,102 +1,104 @@
1
- {
2
- "name": "@cepseudo/adonis-audit-log",
3
- "description": "Simple audit logging for AdonisJS 6",
4
- "version": "1.0.0",
5
- "engines": {
6
- "node": ">=20.6.0"
7
- },
8
- "type": "module",
9
- "main": "build/index.js",
10
- "types": "build/index.d.ts",
11
- "files": [
12
- "build/src",
13
- "build/providers",
14
- "build/stubs",
15
- "build/services",
16
- "build/middleware",
17
- "build/index.d.ts",
18
- "build/index.js",
19
- "build/configure.d.ts",
20
- "build/configure.js"
21
- ],
22
- "exports": {
23
- ".": "./build/index.js",
24
- "./services/main": "./build/services/main.js",
25
- "./middleware/request_logger": "./build/middleware/request_logger.js",
26
- "./providers/audit_provider": "./build/providers/audit_provider.js",
27
- "./types": "./build/src/types.js"
28
- },
29
- "scripts": {
30
- "clean": "del-cli build",
31
- "copy:templates": "copyfiles \"stubs/**/*.stub\" build",
32
- "typecheck": "tsc --noEmit",
33
- "lint": "eslint .",
34
- "format": "prettier --write .",
35
- "quick:test": "node --import=./tsnode.esm.js --enable-source-maps bin/test.ts",
36
- "pretest": "npm run lint",
37
- "test": "c8 npm run quick:test",
38
- "prebuild": "npm run lint && npm run clean",
39
- "build": "tsc",
40
- "postbuild": "npm run copy:templates",
41
- "release": "np",
42
- "version": "npm run build",
43
- "prepublishOnly": "npm run build"
44
- },
45
- "keywords": [
46
- "adonisjs",
47
- "audit",
48
- "logging",
49
- "audit-log",
50
- "request-logging"
51
- ],
52
- "author": "",
53
- "license": "MIT",
54
- "devDependencies": {
55
- "@adonisjs/assembler": "^7.8.2",
56
- "@adonisjs/core": "^6.12.0",
57
- "@adonisjs/eslint-config": "2.0.0-beta.7",
58
- "@adonisjs/lucid": "^21.0.0",
59
- "@adonisjs/prettier-config": "^1.4.0",
60
- "@adonisjs/tsconfig": "^1.3.0",
61
- "@japa/assert": "^3.0.0",
62
- "@japa/runner": "^3.1.4",
63
- "@swc/core": "^1.6.3",
64
- "@types/luxon": "^3.7.1",
65
- "@types/node": "^20.14.5",
66
- "c8": "^10.1.2",
67
- "copyfiles": "^2.4.1",
68
- "del-cli": "^5.1.0",
69
- "eslint": "^9.15.0",
70
- "np": "^10.0.6",
71
- "prettier": "^3.3.2",
72
- "ts-node-maintained": "^10.9.4",
73
- "typescript": "^5.4.5"
74
- },
75
- "peerDependencies": {
76
- "@adonisjs/core": "^6.2.0",
77
- "@adonisjs/lucid": "^21.0.0"
78
- },
79
- "publishConfig": {
80
- "access": "public",
81
- "tag": "latest"
82
- },
83
- "np": {
84
- "message": "chore(release): %s",
85
- "tag": "latest",
86
- "branch": "main",
87
- "anyBranch": false
88
- },
89
- "c8": {
90
- "reporter": [
91
- "text",
92
- "html"
93
- ],
94
- "exclude": [
95
- "tests/**"
96
- ]
97
- },
98
- "prettier": "@adonisjs/prettier-config",
99
- "dependencies": {
100
- "luxon": "^3.7.2"
101
- }
102
- }
1
+ {
2
+ "name": "@cepseudo/adonis-audit-log",
3
+ "description": "Simple audit logging for AdonisJS 6",
4
+ "version": "1.2.0",
5
+ "engines": {
6
+ "node": ">=20.6.0"
7
+ },
8
+ "type": "module",
9
+ "main": "build/index.js",
10
+ "types": "build/index.d.ts",
11
+ "files": [
12
+ "build/src",
13
+ "build/providers",
14
+ "build/stubs",
15
+ "build/services",
16
+ "build/middleware",
17
+ "build/commands",
18
+ "build/index.d.ts",
19
+ "build/index.js",
20
+ "build/configure.d.ts",
21
+ "build/configure.js"
22
+ ],
23
+ "exports": {
24
+ ".": "./build/index.js",
25
+ "./services/main": "./build/services/main.js",
26
+ "./middleware/request_logger": "./build/middleware/request_logger.js",
27
+ "./providers/audit_provider": "./build/providers/audit_provider.js",
28
+ "./commands": "./build/commands/main.js",
29
+ "./types": "./build/src/types.js"
30
+ },
31
+ "scripts": {
32
+ "clean": "del-cli build",
33
+ "copy:templates": "copyfiles \"stubs/**/*.stub\" \"commands/*.json\" build",
34
+ "typecheck": "tsc --noEmit",
35
+ "lint": "eslint .",
36
+ "format": "prettier --write .",
37
+ "quick:test": "node --import=./tsnode.esm.js --enable-source-maps bin/test.ts",
38
+ "pretest": "npm run lint",
39
+ "test": "c8 npm run quick:test",
40
+ "prebuild": "npm run lint && npm run clean",
41
+ "build": "tsc",
42
+ "postbuild": "npm run copy:templates",
43
+ "release": "np",
44
+ "version": "npm run build",
45
+ "prepublishOnly": "npm run build"
46
+ },
47
+ "keywords": [
48
+ "adonisjs",
49
+ "audit",
50
+ "logging",
51
+ "audit-log",
52
+ "request-logging"
53
+ ],
54
+ "author": "",
55
+ "license": "MIT",
56
+ "devDependencies": {
57
+ "@adonisjs/assembler": "^7.8.2",
58
+ "@adonisjs/core": "^6.12.0",
59
+ "@adonisjs/eslint-config": "2.0.0-beta.7",
60
+ "@adonisjs/lucid": "^21.0.0",
61
+ "@adonisjs/prettier-config": "^1.4.0",
62
+ "@adonisjs/tsconfig": "^1.3.0",
63
+ "@japa/assert": "^3.0.0",
64
+ "@japa/runner": "^3.1.4",
65
+ "@swc/core": "^1.6.3",
66
+ "@types/luxon": "^3.7.1",
67
+ "@types/node": "^20.14.5",
68
+ "c8": "^10.1.2",
69
+ "copyfiles": "^2.4.1",
70
+ "del-cli": "^5.1.0",
71
+ "eslint": "^9.15.0",
72
+ "np": "^10.0.6",
73
+ "prettier": "^3.3.2",
74
+ "ts-node-maintained": "^10.9.4",
75
+ "typescript": "^5.4.5"
76
+ },
77
+ "peerDependencies": {
78
+ "@adonisjs/core": "^6.2.0",
79
+ "@adonisjs/lucid": "^21.0.0"
80
+ },
81
+ "publishConfig": {
82
+ "access": "public",
83
+ "tag": "latest"
84
+ },
85
+ "np": {
86
+ "message": "chore(release): %s",
87
+ "tag": "latest",
88
+ "branch": "main",
89
+ "anyBranch": false
90
+ },
91
+ "c8": {
92
+ "reporter": [
93
+ "text",
94
+ "html"
95
+ ],
96
+ "exclude": [
97
+ "tests/**"
98
+ ]
99
+ },
100
+ "prettier": "@adonisjs/prettier-config",
101
+ "dependencies": {
102
+ "luxon": "^3.7.2"
103
+ }
104
+ }