@@ -8,6 +8,9 @@ import { ObjectSchema, Field } from '@objectstack/spec/data';
88 * Immutable audit trail for all significant platform events.
99 * Records who did what, when, and the before/after state.
1010 *
11+ * Every field is `readonly: true` — audit logs are written only by
12+ * internal system hooks, never via UI forms. API exposes only `get` + `list`.
13+ *
1114 * @namespace sys
1215 */
1316export const SysAuditLog = ObjectSchema . create ( {
@@ -18,97 +21,133 @@ export const SysAuditLog = ObjectSchema.create({
1821 icon : 'scroll-text' ,
1922 isSystem : true ,
2023 description : 'Immutable audit trail for platform events' ,
21- titleFormat : '{action} on {object_name} by {user_id}' ,
22- compactLayout : [ 'action' , 'object_name' , 'user_id' , 'created_at' ] ,
23-
24+ displayNameField : 'action' ,
25+ titleFormat : '{action} · {object_name}' ,
26+ compactLayout : [ 'created_at' , 'action' , 'object_name' , 'record_id' , 'user_id' ] ,
27+
2428 fields : {
25- id : Field . text ( {
26- label : 'Audit Log ID' ,
27- required : true ,
28- readonly : true ,
29- } ) ,
30-
29+ // ── Event ────────────────────────────────────────────────────
3130 created_at : Field . datetime ( {
3231 label : 'Timestamp' ,
3332 required : true ,
3433 defaultValue : 'NOW()' ,
3534 readonly : true ,
35+ group : 'Event' ,
3636 } ) ,
37-
37+
38+ action : Field . select (
39+ [ 'create' , 'update' , 'delete' , 'restore' , 'login' , 'logout' , 'permission_change' , 'config_change' , 'export' , 'import' ] ,
40+ {
41+ label : 'Action' ,
42+ required : true ,
43+ readonly : true ,
44+ searchable : true ,
45+ description : 'Action type (snake_case)' ,
46+ group : 'Event' ,
47+ } ,
48+ ) ,
49+
3850 user_id : Field . text ( {
39- label : 'User ID ' ,
51+ label : 'Actor ' ,
4052 required : false ,
53+ readonly : true ,
54+ searchable : true ,
4155 description : 'User who performed the action (null for system actions)' ,
56+ group : 'Event' ,
4257 } ) ,
43-
44- action : Field . select ( [ 'create' , 'update' , 'delete' , 'restore' , 'login' , 'logout' , 'permission_change' , 'config_change' , 'export' , 'import' ] , {
45- label : 'Action' ,
46- required : true ,
47- description : 'Action type (snake_case). Values: create, update, delete, restore, login, logout, permission_change, config_change, export, import' ,
48- } ) ,
49-
58+
59+ // ── Target record ────────────────────────────────────────────
5060 object_name : Field . text ( {
51- label : 'Object Name ' ,
61+ label : 'Object' ,
5262 required : false ,
63+ readonly : true ,
64+ searchable : true ,
5365 maxLength : 255 ,
5466 description : 'Target object (e.g. sys_user, project_task)' ,
67+ group : 'Target' ,
5568 } ) ,
56-
69+
5770 record_id : Field . text ( {
5871 label : 'Record ID' ,
5972 required : false ,
73+ readonly : true ,
74+ searchable : true ,
6075 description : 'ID of the affected record' ,
76+ group : 'Target' ,
6177 } ) ,
62-
78+
79+ // ── Change payload ───────────────────────────────────────────
6380 old_value : Field . textarea ( {
6481 label : 'Old Value' ,
6582 required : false ,
83+ readonly : true ,
6684 description : 'JSON-serialized previous state' ,
85+ group : 'Changes' ,
6786 } ) ,
68-
87+
6988 new_value : Field . textarea ( {
7089 label : 'New Value' ,
7190 required : false ,
91+ readonly : true ,
7292 description : 'JSON-serialized new state' ,
93+ group : 'Changes' ,
7394 } ) ,
74-
95+
96+ // ── Client fingerprint ───────────────────────────────────────
7597 ip_address : Field . text ( {
7698 label : 'IP Address' ,
7799 required : false ,
100+ readonly : true ,
78101 maxLength : 45 ,
102+ group : 'Client' ,
79103 } ) ,
80-
104+
81105 user_agent : Field . textarea ( {
82106 label : 'User Agent' ,
83107 required : false ,
108+ readonly : true ,
109+ group : 'Client' ,
84110 } ) ,
85-
111+
112+ // ── Context ──────────────────────────────────────────────────
86113 tenant_id : Field . text ( {
87- label : 'Tenant ID ' ,
114+ label : 'Tenant' ,
88115 required : false ,
116+ readonly : true ,
89117 description : 'Tenant context for multi-tenant isolation' ,
118+ group : 'Context' ,
90119 } ) ,
91-
120+
92121 metadata : Field . textarea ( {
93122 label : 'Metadata' ,
94123 required : false ,
124+ readonly : true ,
95125 description : 'JSON-serialized additional context' ,
126+ group : 'Context' ,
127+ } ) ,
128+
129+ // ── System ───────────────────────────────────────────────────
130+ id : Field . text ( {
131+ label : 'Audit Log ID' ,
132+ required : true ,
133+ readonly : true ,
134+ group : 'System' ,
96135 } ) ,
97136 } ,
98-
137+
99138 indexes : [
100139 { fields : [ 'created_at' ] } ,
101140 { fields : [ 'user_id' ] } ,
102141 { fields : [ 'object_name' , 'record_id' ] } ,
103142 { fields : [ 'action' ] } ,
104143 { fields : [ 'tenant_id' ] } ,
105144 ] ,
106-
145+
107146 enable : {
108147 trackHistory : false , // Audit logs are themselves the audit trail
109148 searchable : true ,
110149 apiEnabled : true ,
111- apiMethods : [ 'get' , 'list' ] , // Read-only — audit logs are immutable; creation happens via internal system hooks only
150+ apiMethods : [ 'get' , 'list' ] , // Read-only — creation happens via internal system hooks only
112151 trash : false , // Never soft-delete audit logs
113152 mru : false ,
114153 clone : false ,
0 commit comments