-
Notifications
You must be signed in to change notification settings - Fork 5
Expand file tree
/
Copy pathsys-approval-token.object.ts
More file actions
101 lines (88 loc) · 2.72 KB
/
Copy pathsys-approval-token.object.ts
File metadata and controls
101 lines (88 loc) · 2.72 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
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
// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.
import { ObjectSchema, Field } from '@objectstack/spec/data';
/**
* sys_approval_token — single-use actionable-link tokens (ADR-0043).
*
* One row per issued approve/reject link. Only the SHA-256 **hash** of the
* raw token is stored — a database leak yields no usable links. A token is
* dead once any of these holds: `consumed_at` set, `expires_at` passed, the
* request left `pending`, or the bound approver no longer holds a slot
* (the last two are re-checked at redemption, not materialized here).
*
* @namespace sys
*/
export const SysApprovalToken = ObjectSchema.create({
name: 'sys_approval_token',
label: 'Approval Action Token',
pluralLabel: 'Approval Action Tokens',
icon: 'key',
isSystem: true,
managedBy: 'engine-owned',
description: 'Single-use tokens behind actionable approval links',
displayNameField: 'id',
nameField: 'id', // [ADR-0079] canonical primary-title pointer (mirrors deprecated displayNameField)
fields: {
id: Field.text({ label: 'Token ID', required: true, readonly: true, group: 'System' }),
organization_id: Field.lookup('sys_organization', {
label: 'Organization',
required: false,
group: 'System',
}),
token_hash: Field.text({
label: 'Token Hash',
required: true,
maxLength: 100,
readonly: true,
description: 'SHA-256 hex of the raw token — the raw value is never stored',
group: 'Token',
}),
request_id: Field.text({
label: 'Request',
required: true,
maxLength: 100,
readonly: true,
group: 'Token',
}),
action: Field.select(['approve', 'reject'], {
label: 'Action',
required: true,
readonly: true,
group: 'Token',
}),
approver_id: Field.text({
label: 'Approver',
required: true,
maxLength: 200,
readonly: true,
description: 'Identity the token is bound to; the decision is audited as this approver',
group: 'Token',
}),
expires_at: Field.datetime({
label: 'Expires At',
required: true,
readonly: true,
group: 'Lifecycle',
}),
consumed_at: Field.datetime({
label: 'Consumed At',
required: false,
group: 'Lifecycle',
}),
created_at: Field.datetime({
label: 'Created At',
required: true,
defaultValue: 'NOW()',
readonly: true,
group: 'System',
}),
},
indexes: [
{ fields: ['token_hash'] },
{ fields: ['request_id'] },
],
enable: {
// [ADR-0103] Engine-owned: one-time email-approval tokens are minted and
// consumed by the approval engine (SYSTEM_CTX), never via the data API.
apiMethods: ['get', 'list'],
},
});