@adminiumjs/meta 0.1.0-rc.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/LICENSE +661 -0
- package/dist/bootstrap.d.ts +103 -0
- package/dist/bootstrap.d.ts.map +1 -0
- package/dist/bootstrap.js +225 -0
- package/dist/bootstrap.js.map +1 -0
- package/dist/columns.d.ts +44 -0
- package/dist/columns.d.ts.map +1 -0
- package/dist/columns.js +57 -0
- package/dist/columns.js.map +1 -0
- package/dist/connect.d.ts +38 -0
- package/dist/connect.d.ts.map +1 -0
- package/dist/connect.js +57 -0
- package/dist/connect.js.map +1 -0
- package/dist/ids.d.ts +79 -0
- package/dist/ids.d.ts.map +1 -0
- package/dist/ids.js +165 -0
- package/dist/ids.js.map +1 -0
- package/dist/index.d.ts +21 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +21 -0
- package/dist/index.js.map +1 -0
- package/dist/migrations/0001_core_auth.d.ts +9 -0
- package/dist/migrations/0001_core_auth.d.ts.map +1 -0
- package/dist/migrations/0001_core_auth.js +102 -0
- package/dist/migrations/0001_core_auth.js.map +1 -0
- package/dist/migrations/0002_rbac.d.ts +8 -0
- package/dist/migrations/0002_rbac.d.ts.map +1 -0
- package/dist/migrations/0002_rbac.js +75 -0
- package/dist/migrations/0002_rbac.js.map +1 -0
- package/dist/migrations/0003_connections_schema.d.ts +9 -0
- package/dist/migrations/0003_connections_schema.d.ts.map +1 -0
- package/dist/migrations/0003_connections_schema.js +100 -0
- package/dist/migrations/0003_connections_schema.js.map +1 -0
- package/dist/migrations/0004_pages_views.d.ts +8 -0
- package/dist/migrations/0004_pages_views.d.ts.map +1 -0
- package/dist/migrations/0004_pages_views.js +59 -0
- package/dist/migrations/0004_pages_views.js.map +1 -0
- package/dist/migrations/0005_ops.d.ts +9 -0
- package/dist/migrations/0005_ops.d.ts.map +1 -0
- package/dist/migrations/0005_ops.js +104 -0
- package/dist/migrations/0005_ops.js.map +1 -0
- package/dist/migrations/0006_platform.d.ts +11 -0
- package/dist/migrations/0006_platform.d.ts.map +1 -0
- package/dist/migrations/0006_platform.js +259 -0
- package/dist/migrations/0006_platform.js.map +1 -0
- package/dist/migrations/0007_llm_runs.d.ts +33 -0
- package/dist/migrations/0007_llm_runs.d.ts.map +1 -0
- package/dist/migrations/0007_llm_runs.js +62 -0
- package/dist/migrations/0007_llm_runs.js.map +1 -0
- package/dist/migrations/0008_llm_overrides.d.ts +18 -0
- package/dist/migrations/0008_llm_overrides.d.ts.map +1 -0
- package/dist/migrations/0008_llm_overrides.js +24 -0
- package/dist/migrations/0008_llm_overrides.js.map +1 -0
- package/dist/migrations/0009_views_kind.d.ts +20 -0
- package/dist/migrations/0009_views_kind.d.ts.map +1 -0
- package/dist/migrations/0009_views_kind.js +24 -0
- package/dist/migrations/0009_views_kind.js.map +1 -0
- package/dist/migrations/0010_llm_prompt_version_width.d.ts +19 -0
- package/dist/migrations/0010_llm_prompt_version_width.d.ts.map +1 -0
- package/dist/migrations/0010_llm_prompt_version_width.js +28 -0
- package/dist/migrations/0010_llm_prompt_version_width.js.map +1 -0
- package/dist/migrations/index.d.ts +29 -0
- package/dist/migrations/index.d.ts.map +1 -0
- package/dist/migrations/index.js +43 -0
- package/dist/migrations/index.js.map +1 -0
- package/dist/migrator.d.ts +83 -0
- package/dist/migrator.d.ts.map +1 -0
- package/dist/migrator.js +173 -0
- package/dist/migrator.js.map +1 -0
- package/dist/prefix.d.ts +19 -0
- package/dist/prefix.d.ts.map +1 -0
- package/dist/prefix.js +33 -0
- package/dist/prefix.js.map +1 -0
- package/dist/repos/api-keys.d.ts +31 -0
- package/dist/repos/api-keys.d.ts.map +1 -0
- package/dist/repos/api-keys.js +62 -0
- package/dist/repos/api-keys.js.map +1 -0
- package/dist/repos/audit.d.ts +51 -0
- package/dist/repos/audit.d.ts.map +1 -0
- package/dist/repos/audit.js +78 -0
- package/dist/repos/audit.js.map +1 -0
- package/dist/repos/connections.d.ts +90 -0
- package/dist/repos/connections.d.ts.map +1 -0
- package/dist/repos/connections.js +191 -0
- package/dist/repos/connections.js.map +1 -0
- package/dist/repos/email-templates.d.ts +47 -0
- package/dist/repos/email-templates.d.ts.map +1 -0
- package/dist/repos/email-templates.js +93 -0
- package/dist/repos/email-templates.js.map +1 -0
- package/dist/repos/exports.d.ts +67 -0
- package/dist/repos/exports.d.ts.map +1 -0
- package/dist/repos/exports.js +132 -0
- package/dist/repos/exports.js.map +1 -0
- package/dist/repos/files.d.ts +52 -0
- package/dist/repos/files.d.ts.map +1 -0
- package/dist/repos/files.js +86 -0
- package/dist/repos/files.js.map +1 -0
- package/dist/repos/imports.d.ts +71 -0
- package/dist/repos/imports.d.ts.map +1 -0
- package/dist/repos/imports.js +111 -0
- package/dist/repos/imports.js.map +1 -0
- package/dist/repos/index.d.ts +26 -0
- package/dist/repos/index.d.ts.map +1 -0
- package/dist/repos/index.js +26 -0
- package/dist/repos/index.js.map +1 -0
- package/dist/repos/jobs.d.ts +71 -0
- package/dist/repos/jobs.d.ts.map +1 -0
- package/dist/repos/jobs.js +187 -0
- package/dist/repos/jobs.js.map +1 -0
- package/dist/repos/llm-overrides.d.ts +89 -0
- package/dist/repos/llm-overrides.d.ts.map +1 -0
- package/dist/repos/llm-overrides.js +205 -0
- package/dist/repos/llm-overrides.js.map +1 -0
- package/dist/repos/llm-runs.d.ts +114 -0
- package/dist/repos/llm-runs.d.ts.map +1 -0
- package/dist/repos/llm-runs.js +214 -0
- package/dist/repos/llm-runs.js.map +1 -0
- package/dist/repos/notification-prefs.d.ts +34 -0
- package/dist/repos/notification-prefs.d.ts.map +1 -0
- package/dist/repos/notification-prefs.js +80 -0
- package/dist/repos/notification-prefs.js.map +1 -0
- package/dist/repos/notifications.d.ts +60 -0
- package/dist/repos/notifications.d.ts.map +1 -0
- package/dist/repos/notifications.js +107 -0
- package/dist/repos/notifications.js.map +1 -0
- package/dist/repos/overrides.d.ts +59 -0
- package/dist/repos/overrides.d.ts.map +1 -0
- package/dist/repos/overrides.js +143 -0
- package/dist/repos/overrides.js.map +1 -0
- package/dist/repos/pages.d.ts +192 -0
- package/dist/repos/pages.d.ts.map +1 -0
- package/dist/repos/pages.js +374 -0
- package/dist/repos/pages.js.map +1 -0
- package/dist/repos/password-resets.d.ts +32 -0
- package/dist/repos/password-resets.d.ts.map +1 -0
- package/dist/repos/password-resets.js +56 -0
- package/dist/repos/password-resets.js.map +1 -0
- package/dist/repos/permissions.d.ts +28 -0
- package/dist/repos/permissions.d.ts.map +1 -0
- package/dist/repos/permissions.js +100 -0
- package/dist/repos/permissions.js.map +1 -0
- package/dist/repos/roles.d.ts +33 -0
- package/dist/repos/roles.d.ts.map +1 -0
- package/dist/repos/roles.js +89 -0
- package/dist/repos/roles.js.map +1 -0
- package/dist/repos/scheduled-reports.d.ts +73 -0
- package/dist/repos/scheduled-reports.d.ts.map +1 -0
- package/dist/repos/scheduled-reports.js +132 -0
- package/dist/repos/scheduled-reports.js.map +1 -0
- package/dist/repos/sessions.d.ts +33 -0
- package/dist/repos/sessions.d.ts.map +1 -0
- package/dist/repos/sessions.js +79 -0
- package/dist/repos/sessions.js.map +1 -0
- package/dist/repos/settings.d.ts +25 -0
- package/dist/repos/settings.d.ts.map +1 -0
- package/dist/repos/settings.js +73 -0
- package/dist/repos/settings.js.map +1 -0
- package/dist/repos/snapshots.d.ts +79 -0
- package/dist/repos/snapshots.d.ts.map +1 -0
- package/dist/repos/snapshots.js +191 -0
- package/dist/repos/snapshots.js.map +1 -0
- package/dist/repos/user-prefs.d.ts +56 -0
- package/dist/repos/user-prefs.d.ts.map +1 -0
- package/dist/repos/user-prefs.js +147 -0
- package/dist/repos/user-prefs.js.map +1 -0
- package/dist/repos/users.d.ts +38 -0
- package/dist/repos/users.d.ts.map +1 -0
- package/dist/repos/users.js +103 -0
- package/dist/repos/users.js.map +1 -0
- package/dist/repos/util.d.ts +45 -0
- package/dist/repos/util.d.ts.map +1 -0
- package/dist/repos/util.js +81 -0
- package/dist/repos/util.js.map +1 -0
- package/dist/repos/views.d.ts +75 -0
- package/dist/repos/views.d.ts.map +1 -0
- package/dist/repos/views.js +180 -0
- package/dist/repos/views.js.map +1 -0
- package/dist/schema/index.d.ts +4 -0
- package/dist/schema/index.d.ts.map +1 -0
- package/dist/schema/index.js +4 -0
- package/dist/schema/index.js.map +1 -0
- package/dist/schema/json-payloads.d.ts +479 -0
- package/dist/schema/json-payloads.d.ts.map +1 -0
- package/dist/schema/json-payloads.js +340 -0
- package/dist/schema/json-payloads.js.map +1 -0
- package/dist/schema/settings-registry.d.ts +187 -0
- package/dist/schema/settings-registry.d.ts.map +1 -0
- package/dist/schema/settings-registry.js +183 -0
- package/dist/schema/settings-registry.js.map +1 -0
- package/dist/schema/tables.d.ts +568 -0
- package/dist/schema/tables.d.ts.map +1 -0
- package/dist/schema/tables.js +51 -0
- package/dist/schema/tables.js.map +1 -0
- package/package.json +33 -0
|
@@ -0,0 +1,374 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* pagesRepo — adminium_pages (07-meta-store.md §3.16).
|
|
3
|
+
*
|
|
4
|
+
* Every navigable page of the Generated App, dashboards included. The
|
|
5
|
+
* `config` column is the opaque, already-validated envelope JSON
|
|
6
|
+
* (01-architecture.md §6.1) — `@adminium/meta` never imports the engine
|
|
7
|
+
* schemas; the server is the single write-time validator.
|
|
8
|
+
*
|
|
9
|
+
* Generator contract (M4-T08): `upsertGenerated` is idempotent — stable
|
|
10
|
+
* `page_<slug>` ids, unchanged documents are not rewritten, changed ones
|
|
11
|
+
* bump `revision`, rows the generator no longer emits are pruned (unless
|
|
12
|
+
* human-edited — see below), and rows whose `origin` is not `generated`
|
|
13
|
+
* (user/manifest/system pages) are never touched or pruned. The M5
|
|
14
|
+
* regeneration safety net keys on the `config.generatedHash` the engine
|
|
15
|
+
* embeds in each generated envelope (04-widget-registry.md §6.3 note: user
|
|
16
|
+
* delta wins): when the caller supplies the hash function, a stored document
|
|
17
|
+
* whose embedded hash no longer matches (a human edited it — `setLayout`
|
|
18
|
+
* deliberately leaves the hash stale) is skipped, not overwritten, and
|
|
19
|
+
* reported in `skippedEdited` — the full diff proposal UI is 04-T15. The
|
|
20
|
+
* same guard extends to deletion: an edited row missing from the new set is
|
|
21
|
+
* kept, not pruned, and reported in `keptEdited`; only unedited orphans
|
|
22
|
+
* (byte-identically regenerable) are deleted. Without `hashEnvelope` the
|
|
23
|
+
* legacy full prune applies.
|
|
24
|
+
*/
|
|
25
|
+
import { newId } from '../ids.js';
|
|
26
|
+
import { MetaValidationError, jsonEquals, packJson, readBool, readJson, writeBool, } from './util.js';
|
|
27
|
+
export const PAGE_ORIGINS = ['generated', 'user', 'manifest', 'system', 'llm'];
|
|
28
|
+
/**
|
|
29
|
+
* True when a stored generated-origin envelope no longer matches its embedded
|
|
30
|
+
* `config.generatedHash` — the edited-page signal (human writes update the
|
|
31
|
+
* document but never the hash). Rows without a string hash are treated as
|
|
32
|
+
* untouched: only the generator embeds one, and regenerating restores it.
|
|
33
|
+
*/
|
|
34
|
+
function isEditedEnvelope(stored, hashEnvelope) {
|
|
35
|
+
if (typeof stored !== 'object' || stored === null)
|
|
36
|
+
return false;
|
|
37
|
+
const envelope = stored;
|
|
38
|
+
const config = envelope['config'];
|
|
39
|
+
if (typeof config !== 'object' || config === null)
|
|
40
|
+
return false;
|
|
41
|
+
const embedded = config['generatedHash'];
|
|
42
|
+
if (typeof embedded !== 'string')
|
|
43
|
+
return false;
|
|
44
|
+
return hashEnvelope(envelope) !== embedded;
|
|
45
|
+
}
|
|
46
|
+
function decode(row) {
|
|
47
|
+
return {
|
|
48
|
+
id: row.id,
|
|
49
|
+
connectionId: row.connectionId,
|
|
50
|
+
slug: row.slug,
|
|
51
|
+
type: row.type,
|
|
52
|
+
title: row.title,
|
|
53
|
+
icon: row.icon,
|
|
54
|
+
navGroup: row.navGroup,
|
|
55
|
+
navOrder: row.navOrder,
|
|
56
|
+
config: readJson(row.config),
|
|
57
|
+
origin: row.origin,
|
|
58
|
+
manifestId: row.manifestId,
|
|
59
|
+
generatedFromSnapshotId: row.generatedFromSnapshotId,
|
|
60
|
+
revision: row.revision,
|
|
61
|
+
isEnabled: readBool(row.isEnabled),
|
|
62
|
+
createdBy: row.createdBy,
|
|
63
|
+
createdAt: row.createdAt,
|
|
64
|
+
updatedAt: row.updatedAt,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
export function pagesRepo(meta) {
|
|
68
|
+
const { db } = meta;
|
|
69
|
+
return {
|
|
70
|
+
/**
|
|
71
|
+
* Idempotent bulk upsert of one generation run for a connection.
|
|
72
|
+
* Insert new rows, update changed ones (revision + 1), skip unchanged,
|
|
73
|
+
* prune generated rows missing from the new set. Non-generated rows are
|
|
74
|
+
* reported in `preserved` and never modified.
|
|
75
|
+
*/
|
|
76
|
+
async upsertGenerated(connectionId, pages, opts = {}) {
|
|
77
|
+
const at = opts.at ?? Date.now();
|
|
78
|
+
const prune = opts.prune ?? true;
|
|
79
|
+
const ids = new Set(pages.map((p) => p.id));
|
|
80
|
+
if (ids.size !== pages.length) {
|
|
81
|
+
throw new MetaValidationError('duplicate page ids in one generation run');
|
|
82
|
+
}
|
|
83
|
+
for (const input of pages) {
|
|
84
|
+
if (input.id.length > 36) {
|
|
85
|
+
throw new MetaValidationError(`page id exceeds char(36): ${input.id}`);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
return db.transaction().execute(async (trx) => {
|
|
89
|
+
const existing = await trx
|
|
90
|
+
.selectFrom('adminium_pages')
|
|
91
|
+
.selectAll()
|
|
92
|
+
.where('connectionId', '=', connectionId)
|
|
93
|
+
.execute();
|
|
94
|
+
const byId = new Map(existing.map((row) => [row.id, row]));
|
|
95
|
+
const result = {
|
|
96
|
+
created: 0,
|
|
97
|
+
updated: 0,
|
|
98
|
+
unchanged: 0,
|
|
99
|
+
pruned: 0,
|
|
100
|
+
preserved: [],
|
|
101
|
+
skippedEdited: [],
|
|
102
|
+
keptEdited: [],
|
|
103
|
+
};
|
|
104
|
+
for (const input of pages) {
|
|
105
|
+
const row = byId.get(input.id);
|
|
106
|
+
const packed = packJson(input.config);
|
|
107
|
+
if (row === undefined) {
|
|
108
|
+
await trx
|
|
109
|
+
.insertInto('adminium_pages')
|
|
110
|
+
.values({
|
|
111
|
+
id: input.id,
|
|
112
|
+
connectionId,
|
|
113
|
+
slug: input.slug,
|
|
114
|
+
type: input.type,
|
|
115
|
+
title: input.title,
|
|
116
|
+
icon: input.icon ?? null,
|
|
117
|
+
navGroup: input.navGroup ?? null,
|
|
118
|
+
navOrder: input.navOrder ?? 0,
|
|
119
|
+
config: packed,
|
|
120
|
+
origin: 'generated',
|
|
121
|
+
manifestId: null,
|
|
122
|
+
generatedFromSnapshotId: opts.snapshotId ?? null,
|
|
123
|
+
revision: 1,
|
|
124
|
+
isEnabled: writeBool(meta, true),
|
|
125
|
+
createdBy: opts.createdBy ?? null,
|
|
126
|
+
createdAt: at,
|
|
127
|
+
updatedAt: at,
|
|
128
|
+
})
|
|
129
|
+
.execute();
|
|
130
|
+
result.created += 1;
|
|
131
|
+
continue;
|
|
132
|
+
}
|
|
133
|
+
if (row.origin !== 'generated') {
|
|
134
|
+
// A user/manifest page claimed this id — regeneration never
|
|
135
|
+
// overwrites it (user delta wins, 04 §6.3).
|
|
136
|
+
result.preserved.push(row.id);
|
|
137
|
+
continue;
|
|
138
|
+
}
|
|
139
|
+
// Structural, not serialized-string, equality: pg jsonb / mysql json
|
|
140
|
+
// do not preserve key order, so the round-tripped document rarely
|
|
141
|
+
// re-serializes byte-identical to `packed` (util.ts jsonEquals).
|
|
142
|
+
const unchanged = jsonEquals(readJson(row.config), input.config) &&
|
|
143
|
+
row.slug === input.slug &&
|
|
144
|
+
row.type === input.type &&
|
|
145
|
+
row.title === input.title &&
|
|
146
|
+
(row.icon ?? null) === (input.icon ?? null) &&
|
|
147
|
+
(row.navGroup ?? null) === (input.navGroup ?? null) &&
|
|
148
|
+
row.navOrder === (input.navOrder ?? 0);
|
|
149
|
+
if (unchanged) {
|
|
150
|
+
result.unchanged += 1;
|
|
151
|
+
continue;
|
|
152
|
+
}
|
|
153
|
+
if (opts.hashEnvelope !== undefined &&
|
|
154
|
+
isEditedEnvelope(readJson(row.config), opts.hashEnvelope)) {
|
|
155
|
+
// User delta wins (04 §6.3): the stored document was edited after
|
|
156
|
+
// generation, so this run must not clobber it. The row keeps its
|
|
157
|
+
// snapshot lineage too — it no longer descends from this run.
|
|
158
|
+
result.skippedEdited.push(row.id);
|
|
159
|
+
continue;
|
|
160
|
+
}
|
|
161
|
+
await trx
|
|
162
|
+
.updateTable('adminium_pages')
|
|
163
|
+
.set({
|
|
164
|
+
slug: input.slug,
|
|
165
|
+
type: input.type,
|
|
166
|
+
title: input.title,
|
|
167
|
+
icon: input.icon ?? null,
|
|
168
|
+
navGroup: input.navGroup ?? null,
|
|
169
|
+
navOrder: input.navOrder ?? 0,
|
|
170
|
+
config: packed,
|
|
171
|
+
generatedFromSnapshotId: opts.snapshotId ?? null,
|
|
172
|
+
revision: row.revision + 1,
|
|
173
|
+
updatedAt: at,
|
|
174
|
+
})
|
|
175
|
+
.where('id', '=', row.id)
|
|
176
|
+
.execute();
|
|
177
|
+
result.updated += 1;
|
|
178
|
+
}
|
|
179
|
+
if (prune) {
|
|
180
|
+
for (const row of existing) {
|
|
181
|
+
if (row.origin !== 'generated' || ids.has(row.id))
|
|
182
|
+
continue;
|
|
183
|
+
if (opts.hashEnvelope !== undefined &&
|
|
184
|
+
isEditedEnvelope(readJson(row.config), opts.hashEnvelope)) {
|
|
185
|
+
// User delta wins extends to deletion (04 §6.3): the generator
|
|
186
|
+
// dropped this page (table removed/hidden between runs), but a
|
|
187
|
+
// human customized the stored document, so pruning would
|
|
188
|
+
// destroy work that cannot be regenerated. Keep the row exactly
|
|
189
|
+
// as-is — origin stays 'generated', it stays enabled, and its
|
|
190
|
+
// snapshot lineage is untouched. Unedited orphans are
|
|
191
|
+
// byte-identically regenerable and still delete below.
|
|
192
|
+
result.keptEdited.push(row.id);
|
|
193
|
+
continue;
|
|
194
|
+
}
|
|
195
|
+
await trx.deleteFrom('adminium_pages').where('id', '=', row.id).execute();
|
|
196
|
+
result.pruned += 1;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
return result;
|
|
200
|
+
});
|
|
201
|
+
},
|
|
202
|
+
/** Create a non-generated page (user/manifest/system flows, seeds). */
|
|
203
|
+
async create(input, at = Date.now()) {
|
|
204
|
+
const origin = input.origin ?? 'user';
|
|
205
|
+
if (!PAGE_ORIGINS.includes(origin)) {
|
|
206
|
+
throw new MetaValidationError(`invalid page origin ${JSON.stringify(origin)}`);
|
|
207
|
+
}
|
|
208
|
+
const row = {
|
|
209
|
+
id: newId('page'),
|
|
210
|
+
connectionId: input.connectionId ?? null,
|
|
211
|
+
slug: input.slug,
|
|
212
|
+
type: input.type,
|
|
213
|
+
title: input.title,
|
|
214
|
+
icon: input.icon ?? null,
|
|
215
|
+
navGroup: input.navGroup ?? null,
|
|
216
|
+
navOrder: input.navOrder ?? 0,
|
|
217
|
+
config: packJson(input.config),
|
|
218
|
+
origin,
|
|
219
|
+
manifestId: null,
|
|
220
|
+
generatedFromSnapshotId: null,
|
|
221
|
+
revision: 1,
|
|
222
|
+
isEnabled: writeBool(meta, true),
|
|
223
|
+
createdBy: input.createdBy ?? null,
|
|
224
|
+
createdAt: at,
|
|
225
|
+
updatedAt: at,
|
|
226
|
+
};
|
|
227
|
+
await db.insertInto('adminium_pages').values(row).execute();
|
|
228
|
+
return decode(row);
|
|
229
|
+
},
|
|
230
|
+
async findById(id) {
|
|
231
|
+
const row = await db
|
|
232
|
+
.selectFrom('adminium_pages')
|
|
233
|
+
.selectAll()
|
|
234
|
+
.where('id', '=', id)
|
|
235
|
+
.executeTakeFirst();
|
|
236
|
+
return row === undefined ? null : decode(row);
|
|
237
|
+
},
|
|
238
|
+
/**
|
|
239
|
+
* Write the shared default dashboard layout into the envelope's
|
|
240
|
+
* `config.layout` slot (04-widget-registry.md §6.3; the renderer and engine
|
|
241
|
+
* envelope validator both read `envelope.config.layout`). Merges over the
|
|
242
|
+
* stored envelope so the rest of the document (title/source/nav and the
|
|
243
|
+
* other template config keys) is untouched, and bumps `updatedAt` so the
|
|
244
|
+
* bootstrap `configVersion` advances and clients re-fetch. `revision`
|
|
245
|
+
* bumps too — the counter tracks every changed document, human or machine
|
|
246
|
+
* (override-staleness and future H5 diff/telemetry read it) — while the
|
|
247
|
+
* embedded `config.generatedHash` deliberately stays stale: that mismatch
|
|
248
|
+
* IS the edited-page signal `upsertGenerated` keys on. The `layout` value
|
|
249
|
+
* is validated by the server route before it reaches here. Returns the
|
|
250
|
+
* reloaded page, or null if the id does not exist.
|
|
251
|
+
*/
|
|
252
|
+
async setLayout(pageId, layout, at = Date.now()) {
|
|
253
|
+
const page = await this.findById(pageId);
|
|
254
|
+
if (page === null)
|
|
255
|
+
return null;
|
|
256
|
+
const envelope = typeof page.config === 'object' && page.config !== null
|
|
257
|
+
? page.config
|
|
258
|
+
: {};
|
|
259
|
+
const templateConfig = typeof envelope['config'] === 'object' && envelope['config'] !== null
|
|
260
|
+
? envelope['config']
|
|
261
|
+
: {};
|
|
262
|
+
const nextEnvelope = { ...envelope, config: { ...templateConfig, layout } };
|
|
263
|
+
await db
|
|
264
|
+
.updateTable('adminium_pages')
|
|
265
|
+
.set({ config: packJson(nextEnvelope), revision: page.revision + 1, updatedAt: at })
|
|
266
|
+
.where('id', '=', pageId)
|
|
267
|
+
.execute();
|
|
268
|
+
return this.findById(pageId);
|
|
269
|
+
},
|
|
270
|
+
/**
|
|
271
|
+
* Replace a page's whole config document — the 06-llm-assist.md §8.3
|
|
272
|
+
* materialization write: the apply executor seeds `origin: 'llm'` rows with
|
|
273
|
+
* a minimal `{source, llmRunId}` config and the regeneration hook expands
|
|
274
|
+
* it into the full validated envelope from the active snapshot. Bumps
|
|
275
|
+
* `revision` + `updatedAt` (configVersion advances → clients re-fetch);
|
|
276
|
+
* `icon` is back-filled only when the row has none. `title`/`navGroup`,
|
|
277
|
+
* when provided, sync the row's nav projection from the envelope — the
|
|
278
|
+
* bootstrap tree reads the ROW columns, so without this an expanded llm
|
|
279
|
+
* page would stay outside the nav (and `/p/$slug` resolves from the nav
|
|
280
|
+
* tree, leaving the page unreachable). Returns the reloaded page, or null
|
|
281
|
+
* if the id does not exist.
|
|
282
|
+
*/
|
|
283
|
+
async replaceConfig(pageId, config, opts = {}) {
|
|
284
|
+
const page = await this.findById(pageId);
|
|
285
|
+
if (page === null)
|
|
286
|
+
return null;
|
|
287
|
+
const at = opts.at ?? Date.now();
|
|
288
|
+
const icon = opts.icon ?? null;
|
|
289
|
+
await db
|
|
290
|
+
.updateTable('adminium_pages')
|
|
291
|
+
.set({
|
|
292
|
+
config: packJson(config),
|
|
293
|
+
...(page.icon === null && icon !== null ? { icon } : {}),
|
|
294
|
+
...(opts.title === undefined ? {} : { title: opts.title }),
|
|
295
|
+
...(opts.navGroup === undefined ? {} : { navGroup: opts.navGroup }),
|
|
296
|
+
revision: page.revision + 1,
|
|
297
|
+
updatedAt: at,
|
|
298
|
+
})
|
|
299
|
+
.where('id', '=', pageId)
|
|
300
|
+
.execute();
|
|
301
|
+
return this.findById(pageId);
|
|
302
|
+
},
|
|
303
|
+
/**
|
|
304
|
+
* Toggle a page's visibility (nav + GET both key on `isEnabled`). Bumps
|
|
305
|
+
* `updatedAt` only — the document itself is unchanged, so `revision`
|
|
306
|
+
* stays. Used by the §8.3 materialization pass to park an llm seed whose
|
|
307
|
+
* template cannot compose yet, instead of serving an invalid-config card.
|
|
308
|
+
*/
|
|
309
|
+
async setEnabled(pageId, isEnabled, at = Date.now()) {
|
|
310
|
+
await db
|
|
311
|
+
.updateTable('adminium_pages')
|
|
312
|
+
.set({ isEnabled: writeBool(meta, isEnabled), updatedAt: at })
|
|
313
|
+
.where('id', '=', pageId)
|
|
314
|
+
.execute();
|
|
315
|
+
},
|
|
316
|
+
/** Slugs are unique per connection (uq_adminium_pages_conn_slug). */
|
|
317
|
+
async findBySlug(connectionId, slug) {
|
|
318
|
+
let q = db.selectFrom('adminium_pages').selectAll().where('slug', '=', slug);
|
|
319
|
+
q =
|
|
320
|
+
connectionId === null
|
|
321
|
+
? q.where('connectionId', 'is', null)
|
|
322
|
+
: q.where('connectionId', '=', connectionId);
|
|
323
|
+
const row = await q.executeTakeFirst();
|
|
324
|
+
return row === undefined ? null : decode(row);
|
|
325
|
+
},
|
|
326
|
+
async listForConnection(connectionId) {
|
|
327
|
+
const rows = await db
|
|
328
|
+
.selectFrom('adminium_pages')
|
|
329
|
+
.selectAll()
|
|
330
|
+
.where('connectionId', '=', connectionId)
|
|
331
|
+
.orderBy('navOrder', 'asc')
|
|
332
|
+
.orderBy('slug', 'asc')
|
|
333
|
+
.execute();
|
|
334
|
+
return rows.map(decode);
|
|
335
|
+
},
|
|
336
|
+
/**
|
|
337
|
+
* The bootstrap nav projection — every page row, nav fields only
|
|
338
|
+
* (09-generated-app.md §2.2; the route buckets these into the five fixed
|
|
339
|
+
* groups and derives `configVersion` from max updatedAt).
|
|
340
|
+
*/
|
|
341
|
+
async navRows() {
|
|
342
|
+
return db
|
|
343
|
+
.selectFrom('adminium_pages')
|
|
344
|
+
.select(['id', 'connectionId', 'slug', 'title', 'icon', 'navGroup', 'navOrder', 'isEnabled', 'updatedAt'])
|
|
345
|
+
.execute();
|
|
346
|
+
},
|
|
347
|
+
/** Generated-page counts per connection (connections-hub cards, 09 §8.1). */
|
|
348
|
+
async countGeneratedByConnection() {
|
|
349
|
+
const rows = await db
|
|
350
|
+
.selectFrom('adminium_pages')
|
|
351
|
+
.select(['connectionId'])
|
|
352
|
+
.select((eb) => eb.fn.countAll().as('pages'))
|
|
353
|
+
.where('origin', '=', 'generated')
|
|
354
|
+
.where('connectionId', 'is not', null)
|
|
355
|
+
.groupBy('connectionId')
|
|
356
|
+
.execute();
|
|
357
|
+
const out = {};
|
|
358
|
+
for (const row of rows) {
|
|
359
|
+
if (row.connectionId !== null)
|
|
360
|
+
out[row.connectionId] = Number(row.pages);
|
|
361
|
+
}
|
|
362
|
+
return out;
|
|
363
|
+
},
|
|
364
|
+
/** Max updatedAt over all pages — the bootstrap `configVersion` stamp. */
|
|
365
|
+
async configVersion() {
|
|
366
|
+
const row = await db
|
|
367
|
+
.selectFrom('adminium_pages')
|
|
368
|
+
.select((eb) => eb.fn.max('updatedAt').as('max'))
|
|
369
|
+
.executeTakeFirst();
|
|
370
|
+
return Number(row?.max ?? 0);
|
|
371
|
+
},
|
|
372
|
+
};
|
|
373
|
+
}
|
|
374
|
+
//# sourceMappingURL=pages.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pages.js","sourceRoot":"","sources":["../../src/repos/pages.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* passwordResetsRepo — adminium_password_resets (07-meta-store.md §3.6).
|
|
3
|
+
* Carries both reset and invite-activation tokens; single-use.
|
|
4
|
+
*/
|
|
5
|
+
import type { Selectable } from 'kysely';
|
|
6
|
+
import { z } from 'zod';
|
|
7
|
+
import type { MetaDb } from '../connect.js';
|
|
8
|
+
import type { AdminiumPasswordResetsTable } from '../schema/tables.js';
|
|
9
|
+
export declare const resetKindSchema: z.ZodEnum<{
|
|
10
|
+
reset: "reset";
|
|
11
|
+
invite: "invite";
|
|
12
|
+
}>;
|
|
13
|
+
export type ResetKind = z.infer<typeof resetKindSchema>;
|
|
14
|
+
export type PasswordReset = Selectable<AdminiumPasswordResetsTable>;
|
|
15
|
+
export interface CreatePasswordResetInput {
|
|
16
|
+
userId: string;
|
|
17
|
+
kind: ResetKind;
|
|
18
|
+
tokenHash: string;
|
|
19
|
+
/** reset: +2 h; invite: +7 days — computed by the server (§3.6). */
|
|
20
|
+
expiresAt: number;
|
|
21
|
+
}
|
|
22
|
+
export declare function passwordResetsRepo(meta: MetaDb): {
|
|
23
|
+
create(input: CreatePasswordResetInput, at?: number): Promise<PasswordReset>;
|
|
24
|
+
/** Unused and unexpired at `at`. */
|
|
25
|
+
findValidByTokenHash(tokenHash: string, at?: number): Promise<PasswordReset | null>;
|
|
26
|
+
/** Single-use guard: only the first consume wins. */
|
|
27
|
+
consume(id: string, at?: number): Promise<boolean>;
|
|
28
|
+
/** Retention §8 `password-resets` policy: used, or expired > 24 h ago. */
|
|
29
|
+
gc(at?: number): Promise<number>;
|
|
30
|
+
};
|
|
31
|
+
export type PasswordResetsRepo = ReturnType<typeof passwordResetsRepo>;
|
|
32
|
+
//# sourceMappingURL=password-resets.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"password-resets.d.ts","sourceRoot":"","sources":["../../src/repos/password-resets.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AACzC,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAE5C,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,qBAAqB,CAAC;AAGvE,eAAO,MAAM,eAAe;;;EAA8B,CAAC;AAC3D,MAAM,MAAM,SAAS,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,eAAe,CAAC,CAAC;AAExD,MAAM,MAAM,aAAa,GAAG,UAAU,CAAC,2BAA2B,CAAC,CAAC;AAEpE,MAAM,WAAW,wBAAwB;IACvC,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,SAAS,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,oEAAoE;IACpE,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM;kBAGvB,wBAAwB,OAAM,MAAM,GAAgB,OAAO,CAAC,aAAa,CAAC;IAc9F,oCAAoC;oCACE,MAAM,OAAM,MAAM,GAAgB,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC;IAWrG,qDAAqD;gBACnC,MAAM,OAAM,MAAM,GAAgB,OAAO,CAAC,OAAO,CAAC;IAUpE,0EAA0E;YAC7D,MAAM,GAAgB,OAAO,CAAC,MAAM,CAAC;EAQrD;AAED,MAAM,MAAM,kBAAkB,GAAG,UAAU,CAAC,OAAO,kBAAkB,CAAC,CAAC"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* passwordResetsRepo — adminium_password_resets (07-meta-store.md §3.6).
|
|
3
|
+
* Carries both reset and invite-activation tokens; single-use.
|
|
4
|
+
*/
|
|
5
|
+
import { z } from 'zod';
|
|
6
|
+
import { newId } from '../ids.js';
|
|
7
|
+
import { DAY_MS } from './util.js';
|
|
8
|
+
export const resetKindSchema = z.enum(['reset', 'invite']);
|
|
9
|
+
export function passwordResetsRepo(meta) {
|
|
10
|
+
const { db } = meta;
|
|
11
|
+
return {
|
|
12
|
+
async create(input, at = Date.now()) {
|
|
13
|
+
const row = {
|
|
14
|
+
id: newId('rst'),
|
|
15
|
+
userId: input.userId,
|
|
16
|
+
kind: resetKindSchema.parse(input.kind),
|
|
17
|
+
tokenHash: input.tokenHash,
|
|
18
|
+
expiresAt: input.expiresAt,
|
|
19
|
+
usedAt: null,
|
|
20
|
+
createdAt: at,
|
|
21
|
+
};
|
|
22
|
+
await db.insertInto('adminium_password_resets').values(row).execute();
|
|
23
|
+
return row;
|
|
24
|
+
},
|
|
25
|
+
/** Unused and unexpired at `at`. */
|
|
26
|
+
async findValidByTokenHash(tokenHash, at = Date.now()) {
|
|
27
|
+
const row = await db
|
|
28
|
+
.selectFrom('adminium_password_resets')
|
|
29
|
+
.selectAll()
|
|
30
|
+
.where('tokenHash', '=', tokenHash)
|
|
31
|
+
.where('usedAt', 'is', null)
|
|
32
|
+
.where('expiresAt', '>', at)
|
|
33
|
+
.executeTakeFirst();
|
|
34
|
+
return row ?? null;
|
|
35
|
+
},
|
|
36
|
+
/** Single-use guard: only the first consume wins. */
|
|
37
|
+
async consume(id, at = Date.now()) {
|
|
38
|
+
const res = await db
|
|
39
|
+
.updateTable('adminium_password_resets')
|
|
40
|
+
.set({ usedAt: at })
|
|
41
|
+
.where('id', '=', id)
|
|
42
|
+
.where('usedAt', 'is', null)
|
|
43
|
+
.executeTakeFirst();
|
|
44
|
+
return Number(res.numUpdatedRows) === 1;
|
|
45
|
+
},
|
|
46
|
+
/** Retention §8 `password-resets` policy: used, or expired > 24 h ago. */
|
|
47
|
+
async gc(at = Date.now()) {
|
|
48
|
+
const res = await db
|
|
49
|
+
.deleteFrom('adminium_password_resets')
|
|
50
|
+
.where((eb) => eb.or([eb('usedAt', 'is not', null), eb('expiresAt', '<', at - DAY_MS)]))
|
|
51
|
+
.executeTakeFirst();
|
|
52
|
+
return Number(res.numDeletedRows);
|
|
53
|
+
},
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=password-resets.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"password-resets.js","sourceRoot":"","sources":["../../src/repos/password-resets.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,OAAO,EAAE,KAAK,EAAE,MAAM,WAAW,CAAC;AAElC,OAAO,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;AAEnC,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC,CAAC;AAa3D,MAAM,UAAU,kBAAkB,CAAC,IAAY;IAC7C,MAAM,EAAE,EAAE,EAAE,GAAG,IAAI,CAAC;IACpB,OAAO;QACL,KAAK,CAAC,MAAM,CAAC,KAA+B,EAAE,KAAa,IAAI,CAAC,GAAG,EAAE;YACnE,MAAM,GAAG,GAAkB;gBACzB,EAAE,EAAE,KAAK,CAAC,KAAK,CAAC;gBAChB,MAAM,EAAE,KAAK,CAAC,MAAM;gBACpB,IAAI,EAAE,eAAe,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC;gBACvC,SAAS,EAAE,KAAK,CAAC,SAAS;gBAC1B,SAAS,EAAE,KAAK,CAAC,SAAS;gBAC1B,MAAM,EAAE,IAAI;gBACZ,SAAS,EAAE,EAAE;aACd,CAAC;YACF,MAAM,EAAE,CAAC,UAAU,CAAC,0BAA0B,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,CAAC;YACtE,OAAO,GAAG,CAAC;QACb,CAAC;QAED,oCAAoC;QACpC,KAAK,CAAC,oBAAoB,CAAC,SAAiB,EAAE,KAAa,IAAI,CAAC,GAAG,EAAE;YACnE,MAAM,GAAG,GAAG,MAAM,EAAE;iBACjB,UAAU,CAAC,0BAA0B,CAAC;iBACtC,SAAS,EAAE;iBACX,KAAK,CAAC,WAAW,EAAE,GAAG,EAAE,SAAS,CAAC;iBAClC,KAAK,CAAC,QAAQ,EAAE,IAAI,EAAE,IAAI,CAAC;iBAC3B,KAAK,CAAC,WAAW,EAAE,GAAG,EAAE,EAAE,CAAC;iBAC3B,gBAAgB,EAAE,CAAC;YACtB,OAAO,GAAG,IAAI,IAAI,CAAC;QACrB,CAAC;QAED,qDAAqD;QACrD,KAAK,CAAC,OAAO,CAAC,EAAU,EAAE,KAAa,IAAI,CAAC,GAAG,EAAE;YAC/C,MAAM,GAAG,GAAG,MAAM,EAAE;iBACjB,WAAW,CAAC,0BAA0B,CAAC;iBACvC,GAAG,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;iBACnB,KAAK,CAAC,IAAI,EAAE,GAAG,EAAE,EAAE,CAAC;iBACpB,KAAK,CAAC,QAAQ,EAAE,IAAI,EAAE,IAAI,CAAC;iBAC3B,gBAAgB,EAAE,CAAC;YACtB,OAAO,MAAM,CAAC,GAAG,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC;QAC1C,CAAC;QAED,0EAA0E;QAC1E,KAAK,CAAC,EAAE,CAAC,KAAa,IAAI,CAAC,GAAG,EAAE;YAC9B,MAAM,GAAG,GAAG,MAAM,EAAE;iBACjB,UAAU,CAAC,0BAA0B,CAAC;iBACtC,KAAK,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,QAAQ,EAAE,QAAQ,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,WAAW,EAAE,GAAG,EAAE,EAAE,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;iBACvF,gBAAgB,EAAE,CAAC;YACtB,OAAO,MAAM,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;QACpC,CAAC;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* permissionsRepo — adminium_role_permissions (07-meta-store.md §3.9).
|
|
3
|
+
* The `actions` JSON is Zod-validated per resource kind and never queried in
|
|
4
|
+
* SQL (the server layers an in-memory cache on top).
|
|
5
|
+
*/
|
|
6
|
+
import type { MetaDb } from '../connect.js';
|
|
7
|
+
import { type PermissionActions, type ResourceKind } from '../schema/json-payloads.js';
|
|
8
|
+
export interface RolePermission {
|
|
9
|
+
id: string;
|
|
10
|
+
roleId: string;
|
|
11
|
+
resourceKind: ResourceKind;
|
|
12
|
+
resourceRef: string;
|
|
13
|
+
actions: PermissionActions;
|
|
14
|
+
}
|
|
15
|
+
export declare function permissionsRepo(meta: MetaDb): {
|
|
16
|
+
find: (roleId: string, resourceKind: ResourceKind, resourceRef: string) => Promise<RolePermission | null>;
|
|
17
|
+
/** Upsert one matrix row keyed by (role, kind, ref). */
|
|
18
|
+
grant(roleId: string, resourceKind: ResourceKind, resourceRef: string, actions: PermissionActions): Promise<RolePermission>;
|
|
19
|
+
revoke(roleId: string, resourceKind: ResourceKind, resourceRef: string): Promise<boolean>;
|
|
20
|
+
listForRole(roleId: string): Promise<RolePermission[]>;
|
|
21
|
+
/**
|
|
22
|
+
* Single matrix-cell check: `action` is `read`/`update`/… for tables,
|
|
23
|
+
* `view`/`edit` for pages, and ignored (the ref IS the action) for system.
|
|
24
|
+
*/
|
|
25
|
+
isAllowed(roleId: string, resourceKind: ResourceKind, resourceRef: string, action?: string): Promise<boolean>;
|
|
26
|
+
};
|
|
27
|
+
export type PermissionsRepo = ReturnType<typeof permissionsRepo>;
|
|
28
|
+
//# sourceMappingURL=permissions.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"permissions.d.ts","sourceRoot":"","sources":["../../src/repos/permissions.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAE5C,OAAO,EAGL,KAAK,iBAAiB,EACtB,KAAK,YAAY,EAClB,MAAM,4BAA4B,CAAC;AAGpC,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,EAAE,YAAY,CAAC;IAC3B,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,iBAAiB,CAAC;CAC5B;AAED,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM;mBAGd,MAAM,gBAAgB,YAAY,eAAe,MAAM,KAAG,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC;IAsBlH,wDAAwD;kBAE9C,MAAM,gBACA,YAAY,eACb,MAAM,WACV,iBAAiB,GACzB,OAAO,CAAC,cAAc,CAAC;mBAsBL,MAAM,gBAAgB,YAAY,eAAe,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;wBAUrE,MAAM,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC;IAoB5D;;;OAGG;sBACqB,MAAM,gBAAgB,YAAY,eAAe,MAAM,WAAW,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;EAStH;AAED,MAAM,MAAM,eAAe,GAAG,UAAU,CAAC,OAAO,eAAe,CAAC,CAAC"}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* permissionsRepo — adminium_role_permissions (07-meta-store.md §3.9).
|
|
3
|
+
* The `actions` JSON is Zod-validated per resource kind and never queried in
|
|
4
|
+
* SQL (the server layers an in-memory cache on top).
|
|
5
|
+
*/
|
|
6
|
+
import { newId } from '../ids.js';
|
|
7
|
+
import { permissionActionsSchemaFor, resourceKindSchema, } from '../schema/json-payloads.js';
|
|
8
|
+
import { packJson, readJson } from './util.js';
|
|
9
|
+
export function permissionsRepo(meta) {
|
|
10
|
+
const { db } = meta;
|
|
11
|
+
async function find(roleId, resourceKind, resourceRef) {
|
|
12
|
+
const row = await db
|
|
13
|
+
.selectFrom('adminium_role_permissions')
|
|
14
|
+
.selectAll()
|
|
15
|
+
.where('roleId', '=', roleId)
|
|
16
|
+
.where('resourceKind', '=', resourceKind)
|
|
17
|
+
.where('resourceRef', '=', resourceRef)
|
|
18
|
+
.executeTakeFirst();
|
|
19
|
+
if (!row)
|
|
20
|
+
return null;
|
|
21
|
+
const kind = resourceKindSchema.parse(row.resourceKind);
|
|
22
|
+
return {
|
|
23
|
+
id: row.id,
|
|
24
|
+
roleId: row.roleId,
|
|
25
|
+
resourceKind: kind,
|
|
26
|
+
resourceRef: row.resourceRef,
|
|
27
|
+
actions: permissionActionsSchemaFor(kind).parse(readJson(row.actions)),
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
return {
|
|
31
|
+
find,
|
|
32
|
+
/** Upsert one matrix row keyed by (role, kind, ref). */
|
|
33
|
+
async grant(roleId, resourceKind, resourceRef, actions) {
|
|
34
|
+
const kind = resourceKindSchema.parse(resourceKind);
|
|
35
|
+
const validated = permissionActionsSchemaFor(kind).parse(actions);
|
|
36
|
+
const value = packJson(validated);
|
|
37
|
+
const res = await db
|
|
38
|
+
.updateTable('adminium_role_permissions')
|
|
39
|
+
.set({ actions: value })
|
|
40
|
+
.where('roleId', '=', roleId)
|
|
41
|
+
.where('resourceKind', '=', kind)
|
|
42
|
+
.where('resourceRef', '=', resourceRef)
|
|
43
|
+
.executeTakeFirst();
|
|
44
|
+
if (Number(res.numUpdatedRows) === 0) {
|
|
45
|
+
await db
|
|
46
|
+
.insertInto('adminium_role_permissions')
|
|
47
|
+
.values({ id: newId('perm'), roleId, resourceKind: kind, resourceRef, actions: value })
|
|
48
|
+
.execute();
|
|
49
|
+
}
|
|
50
|
+
const row = await find(roleId, kind, resourceRef);
|
|
51
|
+
if (!row)
|
|
52
|
+
throw new Error('permission row disappeared during grant');
|
|
53
|
+
return row;
|
|
54
|
+
},
|
|
55
|
+
async revoke(roleId, resourceKind, resourceRef) {
|
|
56
|
+
const res = await db
|
|
57
|
+
.deleteFrom('adminium_role_permissions')
|
|
58
|
+
.where('roleId', '=', roleId)
|
|
59
|
+
.where('resourceKind', '=', resourceKind)
|
|
60
|
+
.where('resourceRef', '=', resourceRef)
|
|
61
|
+
.executeTakeFirst();
|
|
62
|
+
return Number(res.numDeletedRows) === 1;
|
|
63
|
+
},
|
|
64
|
+
async listForRole(roleId) {
|
|
65
|
+
const rows = await db
|
|
66
|
+
.selectFrom('adminium_role_permissions')
|
|
67
|
+
.selectAll()
|
|
68
|
+
.where('roleId', '=', roleId)
|
|
69
|
+
.orderBy('resourceKind', 'asc')
|
|
70
|
+
.orderBy('resourceRef', 'asc')
|
|
71
|
+
.execute();
|
|
72
|
+
return rows.map((row) => {
|
|
73
|
+
const kind = resourceKindSchema.parse(row.resourceKind);
|
|
74
|
+
return {
|
|
75
|
+
id: row.id,
|
|
76
|
+
roleId: row.roleId,
|
|
77
|
+
resourceKind: kind,
|
|
78
|
+
resourceRef: row.resourceRef,
|
|
79
|
+
actions: permissionActionsSchemaFor(kind).parse(readJson(row.actions)),
|
|
80
|
+
};
|
|
81
|
+
});
|
|
82
|
+
},
|
|
83
|
+
/**
|
|
84
|
+
* Single matrix-cell check: `action` is `read`/`update`/… for tables,
|
|
85
|
+
* `view`/`edit` for pages, and ignored (the ref IS the action) for system.
|
|
86
|
+
*/
|
|
87
|
+
async isAllowed(roleId, resourceKind, resourceRef, action) {
|
|
88
|
+
const row = await find(roleId, resourceKind, resourceRef);
|
|
89
|
+
if (!row)
|
|
90
|
+
return false;
|
|
91
|
+
if (resourceKind === 'system')
|
|
92
|
+
return row.actions.allowed;
|
|
93
|
+
if (action === undefined)
|
|
94
|
+
return false;
|
|
95
|
+
const value = row.actions[action];
|
|
96
|
+
return value === true;
|
|
97
|
+
},
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
//# sourceMappingURL=permissions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"permissions.js","sourceRoot":"","sources":["../../src/repos/permissions.ts"],"names":[],"mappings":"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"}
|