@groundbrick/audit-service 1.1.3 → 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/README.md +127 -0
- package/dist/providers/DatabaseAuditProvider.js +3 -3
- package/dist/types.d.ts +2 -2
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
package/README.md
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
# @groundbrick/audit-service
|
|
2
|
+
|
|
3
|
+
Pluggable audit service for asynchronously recording audit events. Designed for multi-tenant applications with a provider-based architecture that allows swapping storage backends.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @groundbrick/audit-service
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Quick Start
|
|
12
|
+
|
|
13
|
+
```typescript
|
|
14
|
+
import { AuditService, DatabaseAuditProvider } from '@groundbrick/audit-service';
|
|
15
|
+
|
|
16
|
+
// 1. Create a provider with your database query executor
|
|
17
|
+
const provider = new DatabaseAuditProvider(async (query, params) => {
|
|
18
|
+
await db.query(query, params);
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
// 2. Get the singleton instance
|
|
22
|
+
const auditService = AuditService.getInstance(provider);
|
|
23
|
+
|
|
24
|
+
// 3. Log events (fire-and-forget, non-blocking)
|
|
25
|
+
auditService.log({
|
|
26
|
+
actorId: '42',
|
|
27
|
+
tenantId: '1',
|
|
28
|
+
action: 'CREATE',
|
|
29
|
+
entity: 'USER',
|
|
30
|
+
entityId: '100',
|
|
31
|
+
newValue: { name: 'John Doe', email: 'john@example.com' },
|
|
32
|
+
});
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Database Schema
|
|
36
|
+
|
|
37
|
+
The `DatabaseAuditProvider` expects an `audit_logs` table with the following structure:
|
|
38
|
+
|
|
39
|
+
```sql
|
|
40
|
+
CREATE TABLE audit_logs (
|
|
41
|
+
id SERIAL PRIMARY KEY,
|
|
42
|
+
actor_id VARCHAR NOT NULL,
|
|
43
|
+
tenant_id VARCHAR NOT NULL,
|
|
44
|
+
action VARCHAR NOT NULL,
|
|
45
|
+
entity VARCHAR NOT NULL,
|
|
46
|
+
entity_id VARCHAR NOT NULL,
|
|
47
|
+
old_value JSONB,
|
|
48
|
+
new_value JSONB,
|
|
49
|
+
metadata JSONB,
|
|
50
|
+
"timestamp" TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP
|
|
51
|
+
);
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## API
|
|
55
|
+
|
|
56
|
+
### `AuditService`
|
|
57
|
+
|
|
58
|
+
Singleton service that delegates event persistence to a provider.
|
|
59
|
+
|
|
60
|
+
| Method | Description |
|
|
61
|
+
|---|---|
|
|
62
|
+
| `AuditService.getInstance(provider)` | Returns the singleton instance, initializing it with the given provider on first call. |
|
|
63
|
+
| `auditService.log(event)` | Logs an audit event asynchronously (fire-and-forget). Errors are caught and logged to `console.error`. |
|
|
64
|
+
|
|
65
|
+
### `AuditEvent`
|
|
66
|
+
|
|
67
|
+
```typescript
|
|
68
|
+
interface AuditEvent {
|
|
69
|
+
actorId: string; // Who performed the action (user ID or 'system')
|
|
70
|
+
tenantId: string; // Tenant/organization the action belongs to
|
|
71
|
+
action: AuditAction; // What happened
|
|
72
|
+
entity: string; // Affected entity type (e.g. 'USER', 'ORDER')
|
|
73
|
+
entityId: string; // ID of the affected entity
|
|
74
|
+
oldValue?: any | null; // Previous state (UPDATE, DELETE)
|
|
75
|
+
newValue?: any | null; // New state (CREATE, UPDATE)
|
|
76
|
+
timestamp?: Date; // Auto-generated if not provided
|
|
77
|
+
metadata?: Record<string, any>; // Additional context (IP, user-agent, etc.)
|
|
78
|
+
}
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### `AuditAction`
|
|
82
|
+
|
|
83
|
+
```typescript
|
|
84
|
+
type AuditAction = 'CREATE' | 'UPDATE' | 'DELETE' | 'LOGIN' | 'LOGOUT';
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### `IAuditProvider`
|
|
88
|
+
|
|
89
|
+
Interface for implementing custom storage backends.
|
|
90
|
+
|
|
91
|
+
```typescript
|
|
92
|
+
interface IAuditProvider {
|
|
93
|
+
save(event: AuditEvent): Promise<void>;
|
|
94
|
+
}
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### `DatabaseAuditProvider`
|
|
98
|
+
|
|
99
|
+
Built-in provider that persists events to a SQL database via a query executor function.
|
|
100
|
+
|
|
101
|
+
```typescript
|
|
102
|
+
const provider = new DatabaseAuditProvider(
|
|
103
|
+
async (query: string, params: any[]) => {
|
|
104
|
+
await pool.query(query, params);
|
|
105
|
+
}
|
|
106
|
+
);
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
## Custom Provider
|
|
110
|
+
|
|
111
|
+
Implement `IAuditProvider` to use any storage backend:
|
|
112
|
+
|
|
113
|
+
```typescript
|
|
114
|
+
import { IAuditProvider, AuditEvent } from '@groundbrick/audit-service';
|
|
115
|
+
|
|
116
|
+
class RedisAuditProvider implements IAuditProvider {
|
|
117
|
+
constructor(private redis: RedisClient) {}
|
|
118
|
+
|
|
119
|
+
async save(event: AuditEvent): Promise<void> {
|
|
120
|
+
await this.redis.lpush('audit:logs', JSON.stringify(event));
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
## License
|
|
126
|
+
|
|
127
|
+
MIT
|
|
@@ -4,14 +4,14 @@ export class DatabaseAuditProvider {
|
|
|
4
4
|
this.executeQuery = executeQuery;
|
|
5
5
|
}
|
|
6
6
|
async save(event) {
|
|
7
|
-
const { actorId,
|
|
7
|
+
const { actorId, tenantId, action, entity, entityId, oldValue, newValue, metadata, } = event;
|
|
8
8
|
const query = `
|
|
9
|
-
INSERT INTO audit_logs (actor_id,
|
|
9
|
+
INSERT INTO audit_logs (actor_id, tenant_id, action, entity, entity_id, old_value, new_value, metadata, "timestamp")
|
|
10
10
|
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)
|
|
11
11
|
`;
|
|
12
12
|
const params = [
|
|
13
13
|
actorId,
|
|
14
|
-
|
|
14
|
+
tenantId,
|
|
15
15
|
action,
|
|
16
16
|
entity,
|
|
17
17
|
entityId,
|
package/dist/types.d.ts
CHANGED
|
@@ -2,8 +2,8 @@ export type AuditAction = 'CREATE' | 'UPDATE' | 'DELETE' | 'LOGIN' | 'LOGOUT';
|
|
|
2
2
|
export interface AuditEvent {
|
|
3
3
|
/** ID do usuário ou sistema que realizou a ação. */
|
|
4
4
|
actorId: string;
|
|
5
|
-
/** ID do
|
|
6
|
-
|
|
5
|
+
/** ID do tenant (organização/empresa) ao qual a ação está associada. */
|
|
6
|
+
tenantId: string;
|
|
7
7
|
/** A ação que foi realizada. */
|
|
8
8
|
action: AuditAction;
|
|
9
9
|
/** A entidade que foi afetada. Ex: 'PET', 'APPOINTMENT', 'USER'. */
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,WAAW,GAAG,QAAQ,GAAG,QAAQ,GAAG,QAAQ,GAAG,OAAO,GAAG,QAAQ,CAAC;AAE9E,MAAM,WAAW,UAAU;IACzB,oDAAoD;IACpD,OAAO,EAAE,MAAM,CAAC;IAChB,
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,WAAW,GAAG,QAAQ,GAAG,QAAQ,GAAG,QAAQ,GAAG,OAAO,GAAG,QAAQ,CAAC;AAE9E,MAAM,WAAW,UAAU;IACzB,oDAAoD;IACpD,OAAO,EAAE,MAAM,CAAC;IAChB,wEAAwE;IACxE,QAAQ,EAAE,MAAM,CAAC;IACjB,gCAAgC;IAChC,MAAM,EAAE,WAAW,CAAC;IACpB,oEAAoE;IACpE,MAAM,EAAE,MAAM,CAAC;IACf,wCAAwC;IACxC,QAAQ,EAAE,MAAM,CAAC;IACjB,0DAA0D;IAC1D,QAAQ,CAAC,EAAE,GAAG,GAAG,IAAI,CAAC;IACtB,sDAAsD;IACtD,QAAQ,CAAC,EAAE,GAAG,GAAG,IAAI,CAAC;IACtB,wEAAwE;IACxE,SAAS,CAAC,EAAE,IAAI,CAAC;IACjB,8DAA8D;IAC9D,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CAChC"}
|