@mostajs/audit 1.1.0 → 2.0.1
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/api/route.js +2 -9
- package/dist/lib/audit-factory.d.ts +14 -0
- package/dist/lib/audit-factory.js +108 -0
- package/dist/lib/audit-init.d.ts +1 -0
- package/dist/lib/audit-init.js +22 -0
- package/dist/lib/audit.d.ts +1 -1
- package/dist/lib/audit.js +2 -6
- package/dist/lib/data-mode.d.ts +1 -0
- package/dist/lib/data-mode.js +5 -0
- package/dist/lib/handlers/audit-log.js +2 -4
- package/dist/lib/module-info.d.ts +8 -0
- package/dist/lib/module-info.js +16 -0
- package/package.json +6 -4
package/dist/api/route.js
CHANGED
|
@@ -1,14 +1,7 @@
|
|
|
1
1
|
// @mosta/audit — API Route template
|
|
2
2
|
// Author: Dr Hamid MADANI drmdh@msn.com
|
|
3
|
-
//
|
|
4
|
-
// Copy to: src/app/api/admin/audit/route.ts
|
|
5
|
-
// Usage:
|
|
6
|
-
// import { createAuditHandlers } from '@mosta/audit/api/route'
|
|
7
|
-
// import { checkPermission } from '@mosta/auth'
|
|
8
|
-
// export const { GET } = createAuditHandlers('audit:view', checkPermission)
|
|
9
3
|
import { NextResponse } from 'next/server';
|
|
10
|
-
import {
|
|
11
|
-
import { AuditLogRepository } from '../repositories/audit-log.repository';
|
|
4
|
+
import { getAuditRepo } from '../lib/audit-factory.js';
|
|
12
5
|
/**
|
|
13
6
|
* Creates a GET handler for paginated audit log consultation.
|
|
14
7
|
*/
|
|
@@ -28,7 +21,7 @@ export function createAuditHandlers(permission, checkPermission) {
|
|
|
28
21
|
page: parseInt(url.searchParams.get('page') || '1', 10),
|
|
29
22
|
limit: parseInt(url.searchParams.get('limit') || '50', 10),
|
|
30
23
|
};
|
|
31
|
-
const repo =
|
|
24
|
+
const repo = await getAuditRepo();
|
|
32
25
|
const { data, total } = await repo.findPaginated(filters);
|
|
33
26
|
return NextResponse.json({
|
|
34
27
|
data,
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { AuditLogDTO, AuditFilters } from '../types/index.js';
|
|
2
|
+
export interface IAuditLogRepository {
|
|
3
|
+
create(data: Record<string, unknown>): Promise<AuditLogDTO>;
|
|
4
|
+
findPaginated(filters: AuditFilters): Promise<{
|
|
5
|
+
data: AuditLogDTO[];
|
|
6
|
+
total: number;
|
|
7
|
+
}>;
|
|
8
|
+
findByResource(resourceId: string, modules?: string[]): Promise<AuditLogDTO[]>;
|
|
9
|
+
deleteOlderThan(days: number): Promise<number>;
|
|
10
|
+
}
|
|
11
|
+
/** Get audit repository for the current data mode (ORM or NET) */
|
|
12
|
+
export declare function getAuditRepo(): Promise<IAuditLogRepository>;
|
|
13
|
+
/** Reset cache (for tests) */
|
|
14
|
+
export declare function resetAuditRepo(): void;
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
// audit-factory.ts — Centralized repository factory for dual ORM/NET mode
|
|
2
|
+
// Same pattern as @mosta/rbac repos-factory.ts
|
|
3
|
+
// Author: Dr Hamid MADANI drmdh@msn.com
|
|
4
|
+
import { isNetMode } from './data-mode.js';
|
|
5
|
+
import { AuditLogSchema } from '../schemas/audit-log.schema.js';
|
|
6
|
+
// ============================================================
|
|
7
|
+
// Factory
|
|
8
|
+
// ============================================================
|
|
9
|
+
let _cached = null;
|
|
10
|
+
let _schemaReady = false;
|
|
11
|
+
/** Get audit repository for the current data mode (ORM or NET) */
|
|
12
|
+
export async function getAuditRepo() {
|
|
13
|
+
if (_cached)
|
|
14
|
+
return _cached;
|
|
15
|
+
if (isNetMode()) {
|
|
16
|
+
await ensureSchemaNet();
|
|
17
|
+
_cached = createNetRepo();
|
|
18
|
+
}
|
|
19
|
+
else {
|
|
20
|
+
await ensureSchemaOrm();
|
|
21
|
+
_cached = await createOrmRepo();
|
|
22
|
+
}
|
|
23
|
+
return _cached;
|
|
24
|
+
}
|
|
25
|
+
/** Reset cache (for tests) */
|
|
26
|
+
export function resetAuditRepo() { _cached = null; _schemaReady = false; }
|
|
27
|
+
// ============================================================
|
|
28
|
+
// Schema init
|
|
29
|
+
// ============================================================
|
|
30
|
+
async function ensureSchemaOrm() {
|
|
31
|
+
if (_schemaReady)
|
|
32
|
+
return;
|
|
33
|
+
const { registerSchemas } = await import('@mostajs/orm');
|
|
34
|
+
registerSchemas([AuditLogSchema]);
|
|
35
|
+
_schemaReady = true;
|
|
36
|
+
}
|
|
37
|
+
async function ensureSchemaNet() {
|
|
38
|
+
if (_schemaReady)
|
|
39
|
+
return;
|
|
40
|
+
const { NetClient } = await import('@mostajs/net/client');
|
|
41
|
+
const client = new NetClient({ url: process.env.MOSTA_NET_URL });
|
|
42
|
+
const result = await client.compareSchema(AuditLogSchema);
|
|
43
|
+
if (!result.exists || !result.compatible) {
|
|
44
|
+
await client.applySchema([AuditLogSchema]);
|
|
45
|
+
}
|
|
46
|
+
_schemaReady = true;
|
|
47
|
+
}
|
|
48
|
+
// ============================================================
|
|
49
|
+
// ORM mode
|
|
50
|
+
// ============================================================
|
|
51
|
+
async function createOrmRepo() {
|
|
52
|
+
const { getDialect } = await import('@mostajs/orm');
|
|
53
|
+
const { AuditLogRepository } = await import('../repositories/audit-log.repository.js');
|
|
54
|
+
const dialect = await getDialect();
|
|
55
|
+
return new AuditLogRepository(dialect);
|
|
56
|
+
}
|
|
57
|
+
// ============================================================
|
|
58
|
+
// NET mode
|
|
59
|
+
// ============================================================
|
|
60
|
+
function createNetRepo() {
|
|
61
|
+
const clientPromise = import('@mostajs/net/client').then(m => new m.NetClient({ url: process.env.MOSTA_NET_URL }));
|
|
62
|
+
return {
|
|
63
|
+
async create(data) {
|
|
64
|
+
const c = await clientPromise;
|
|
65
|
+
return c.create('auditlogs', data);
|
|
66
|
+
},
|
|
67
|
+
async findPaginated(filters) {
|
|
68
|
+
const c = await clientPromise;
|
|
69
|
+
const netFilter = {};
|
|
70
|
+
if (filters.module)
|
|
71
|
+
netFilter.module = filters.module;
|
|
72
|
+
if (filters.action)
|
|
73
|
+
netFilter.action = { $regex: filters.action, $regexFlags: 'i' };
|
|
74
|
+
if (filters.userId)
|
|
75
|
+
netFilter.userId = filters.userId;
|
|
76
|
+
if (filters.status)
|
|
77
|
+
netFilter.status = filters.status;
|
|
78
|
+
if (filters.from || filters.to) {
|
|
79
|
+
const ts = {};
|
|
80
|
+
if (filters.from)
|
|
81
|
+
ts.$gte = new Date(filters.from).toISOString();
|
|
82
|
+
if (filters.to)
|
|
83
|
+
ts.$lte = new Date(filters.to).toISOString();
|
|
84
|
+
netFilter.timestamp = ts;
|
|
85
|
+
}
|
|
86
|
+
const page = filters.page || 1;
|
|
87
|
+
const limit = filters.limit || 50;
|
|
88
|
+
const options = { sort: { timestamp: -1 }, skip: (page - 1) * limit, limit };
|
|
89
|
+
const [data, total] = await Promise.all([
|
|
90
|
+
c.findAll('auditlogs', netFilter, options),
|
|
91
|
+
c.count('auditlogs', netFilter),
|
|
92
|
+
]);
|
|
93
|
+
return { data, total };
|
|
94
|
+
},
|
|
95
|
+
async findByResource(resourceId, modules) {
|
|
96
|
+
const c = await clientPromise;
|
|
97
|
+
const filter = { resourceId };
|
|
98
|
+
if (modules?.length)
|
|
99
|
+
filter.module = { $in: modules };
|
|
100
|
+
return c.findAll('auditlogs', filter, { sort: { timestamp: -1 } });
|
|
101
|
+
},
|
|
102
|
+
async deleteOlderThan(days) {
|
|
103
|
+
const c = await clientPromise;
|
|
104
|
+
const cutoff = new Date(Date.now() - days * 86400000).toISOString();
|
|
105
|
+
return c.deleteMany('auditlogs', { timestamp: { $lt: cutoff } });
|
|
106
|
+
},
|
|
107
|
+
};
|
|
108
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function ensureAuditSchema(): Promise<void>;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// audit-init.ts — Ensure AuditLog schema is ready (ORM or NET)
|
|
2
|
+
// Author: Dr Hamid MADANI drmdh@msn.com
|
|
3
|
+
import { isNetMode } from './data-mode.js';
|
|
4
|
+
import { AuditLogSchema } from '../schemas/audit-log.schema.js';
|
|
5
|
+
let initialized = false;
|
|
6
|
+
export async function ensureAuditSchema() {
|
|
7
|
+
if (initialized)
|
|
8
|
+
return;
|
|
9
|
+
if (isNetMode()) {
|
|
10
|
+
const { NetClient } = await import('@mostajs/net/client');
|
|
11
|
+
const client = new NetClient({ url: process.env.MOSTA_NET_URL });
|
|
12
|
+
const result = await client.compareSchema(AuditLogSchema);
|
|
13
|
+
if (!result.exists || !result.compatible) {
|
|
14
|
+
await client.applySchema([AuditLogSchema]);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
else {
|
|
18
|
+
const { registerSchemas } = await import('@mostajs/orm');
|
|
19
|
+
registerSchemas([AuditLogSchema]);
|
|
20
|
+
}
|
|
21
|
+
initialized = true;
|
|
22
|
+
}
|
package/dist/lib/audit.d.ts
CHANGED
package/dist/lib/audit.js
CHANGED
|
@@ -1,16 +1,12 @@
|
|
|
1
1
|
// @mosta/audit — Core audit functions
|
|
2
2
|
// Author: Dr Hamid MADANI drmdh@msn.com
|
|
3
|
-
import {
|
|
4
|
-
import { AuditLogRepository } from '../repositories/audit-log.repository';
|
|
5
|
-
import { AuditLogSchema } from '../schemas/audit-log.schema';
|
|
6
|
-
// Auto-register audit schema into ORM registry (idempotent)
|
|
7
|
-
registerSchemas([AuditLogSchema]);
|
|
3
|
+
import { getAuditRepo } from './audit-factory.js';
|
|
8
4
|
/**
|
|
9
5
|
* Log an audit entry. Fire-and-forget — never throws, never blocks.
|
|
10
6
|
*/
|
|
11
7
|
export async function logAudit(params) {
|
|
12
8
|
try {
|
|
13
|
-
const repo =
|
|
9
|
+
const repo = await getAuditRepo();
|
|
14
10
|
await repo.create({
|
|
15
11
|
userId: params.userId,
|
|
16
12
|
userName: params.userName,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function isNetMode(): boolean;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// @mostajs/audit — Route handler for audit-log
|
|
2
2
|
// Author: Dr Hamid MADANI drmdh@msn.com
|
|
3
3
|
// Bare handler — NO auth. The socle catch-all does it via permission.
|
|
4
|
-
import {
|
|
4
|
+
import { getAuditRepo } from '../audit-factory.js';
|
|
5
5
|
export async function GET(req) {
|
|
6
6
|
const url = new URL(req.url);
|
|
7
7
|
const page = parseInt(url.searchParams.get('page') || '1', 10);
|
|
@@ -12,9 +12,7 @@ export async function GET(req) {
|
|
|
12
12
|
const status = url.searchParams.get('status') || undefined;
|
|
13
13
|
const dateFrom = url.searchParams.get('dateFrom') || url.searchParams.get('from') || null;
|
|
14
14
|
const dateTo = url.searchParams.get('dateTo') || url.searchParams.get('to') || null;
|
|
15
|
-
const
|
|
16
|
-
const dialect = await getDialect();
|
|
17
|
-
const repo = new AuditLogRepository(dialect);
|
|
15
|
+
const repo = await getAuditRepo();
|
|
18
16
|
const { data: logs, total } = await repo.findPaginated({
|
|
19
17
|
module, action, userId, status,
|
|
20
18
|
from: dateFrom ? new Date(dateFrom) : undefined,
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// @mostajs/audit — Module info (schemas, seeds, metadata)
|
|
2
|
+
// Author: Dr Hamid MADANI drmdh@msn.com
|
|
3
|
+
import { AuditLogSchema } from '../schemas/audit-log.schema.js';
|
|
4
|
+
export function getSchemas() {
|
|
5
|
+
return [AuditLogSchema];
|
|
6
|
+
}
|
|
7
|
+
export const moduleInfo = {
|
|
8
|
+
name: 'audit',
|
|
9
|
+
label: 'Audit & Logs',
|
|
10
|
+
description: 'Audit logging with paginated consultation',
|
|
11
|
+
schemas: getSchemas,
|
|
12
|
+
seed: async () => {
|
|
13
|
+
// Audit has no seed data
|
|
14
|
+
return {};
|
|
15
|
+
},
|
|
16
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mostajs/audit",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.1",
|
|
4
4
|
"description": "Reusable audit logging module — fire-and-forget logAudit() with paginated consultation",
|
|
5
5
|
"author": "Dr Hamid MADANI <drmdh@msn.com>",
|
|
6
6
|
"license": "MIT",
|
|
@@ -76,9 +76,6 @@
|
|
|
76
76
|
"build": "tsc",
|
|
77
77
|
"prepublishOnly": "npm run build"
|
|
78
78
|
},
|
|
79
|
-
"dependencies": {
|
|
80
|
-
"@mostajs/orm": "^1.0.0"
|
|
81
|
-
},
|
|
82
79
|
"peerDependencies": {
|
|
83
80
|
"@mostajs/menu": ">=1.0.2",
|
|
84
81
|
"@mostajs/socle": ">=2.0.0",
|
|
@@ -108,10 +105,15 @@
|
|
|
108
105
|
"@mostajs/menu": "^1.0.2",
|
|
109
106
|
"@mostajs/socle": "^2.0.0",
|
|
110
107
|
"@mostajs/ui": "^1.0.3",
|
|
108
|
+
"@types/node": "^25.5.0",
|
|
111
109
|
"@types/react": "^19.0.0",
|
|
112
110
|
"lucide-react": "^0.575.0",
|
|
113
111
|
"next": "^16.1.6",
|
|
114
112
|
"react": "^19.0.0",
|
|
115
113
|
"typescript": "^5.6.0"
|
|
114
|
+
},
|
|
115
|
+
"dependencies": {
|
|
116
|
+
"@mostajs/net": "^2.0.0",
|
|
117
|
+
"@mostajs/orm": "^1.7.0"
|
|
116
118
|
}
|
|
117
119
|
}
|