@adminiumjs/meta 0.2.2-rc.0 → 0.2.3
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/ids.d.ts +6 -0
- package/dist/ids.d.ts.map +1 -1
- package/dist/ids.js +14 -0
- package/dist/ids.js.map +1 -1
- package/dist/migrations/0014_public_surface.d.ts +40 -0
- package/dist/migrations/0014_public_surface.d.ts.map +1 -0
- package/dist/migrations/0014_public_surface.js +186 -0
- package/dist/migrations/0014_public_surface.js.map +1 -0
- package/dist/migrations/0015_connection_tenant_config.d.ts +42 -0
- package/dist/migrations/0015_connection_tenant_config.d.ts.map +1 -0
- package/dist/migrations/0015_connection_tenant_config.js +53 -0
- package/dist/migrations/0015_connection_tenant_config.js.map +1 -0
- package/dist/migrations/0016_audit_entity.d.ts +33 -0
- package/dist/migrations/0016_audit_entity.d.ts.map +1 -0
- package/dist/migrations/0016_audit_entity.js +137 -0
- package/dist/migrations/0016_audit_entity.js.map +1 -0
- package/dist/migrations/0017_surface_binding.d.ts +23 -0
- package/dist/migrations/0017_surface_binding.d.ts.map +1 -0
- package/dist/migrations/0017_surface_binding.js +37 -0
- package/dist/migrations/0017_surface_binding.js.map +1 -0
- package/dist/migrations/0018_connection_timezone_source.d.ts +44 -0
- package/dist/migrations/0018_connection_timezone_source.d.ts.map +1 -0
- package/dist/migrations/0018_connection_timezone_source.js +51 -0
- package/dist/migrations/0018_connection_timezone_source.js.map +1 -0
- package/dist/migrations/0019_connection_disabled.d.ts +38 -0
- package/dist/migrations/0019_connection_disabled.d.ts.map +1 -0
- package/dist/migrations/0019_connection_disabled.js +43 -0
- package/dist/migrations/0019_connection_disabled.js.map +1 -0
- package/dist/migrations/0020_manifests_add_on.d.ts +71 -0
- package/dist/migrations/0020_manifests_add_on.d.ts.map +1 -0
- package/dist/migrations/0020_manifests_add_on.js +111 -0
- package/dist/migrations/0020_manifests_add_on.js.map +1 -0
- package/dist/migrations/0021_add_on_credentials.d.ts +50 -0
- package/dist/migrations/0021_add_on_credentials.d.ts.map +1 -0
- package/dist/migrations/0021_add_on_credentials.js +80 -0
- package/dist/migrations/0021_add_on_credentials.js.map +1 -0
- package/dist/migrations/0022_studio_namespace.d.ts +44 -0
- package/dist/migrations/0022_studio_namespace.d.ts.map +1 -0
- package/dist/migrations/0022_studio_namespace.js +113 -0
- package/dist/migrations/0022_studio_namespace.js.map +1 -0
- package/dist/migrations/index.d.ts.map +1 -1
- package/dist/migrations/index.js +18 -0
- package/dist/migrations/index.js.map +1 -1
- package/dist/relocate.d.ts +1 -1
- package/dist/relocate.js +1 -1
- package/dist/repos/audit.d.ts.map +1 -1
- package/dist/repos/audit.js +8 -2
- package/dist/repos/audit.js.map +1 -1
- package/dist/repos/connections.d.ts +62 -0
- package/dist/repos/connections.d.ts.map +1 -1
- package/dist/repos/connections.js +104 -0
- package/dist/repos/connections.js.map +1 -1
- package/dist/repos/index.d.ts +2 -0
- package/dist/repos/index.d.ts.map +1 -1
- package/dist/repos/index.js +2 -0
- package/dist/repos/index.js.map +1 -1
- package/dist/repos/manifests.d.ts +139 -0
- package/dist/repos/manifests.d.ts.map +1 -0
- package/dist/repos/manifests.js +279 -0
- package/dist/repos/manifests.js.map +1 -0
- package/dist/repos/pages.d.ts +7 -0
- package/dist/repos/pages.d.ts.map +1 -1
- package/dist/repos/pages.js +39 -8
- package/dist/repos/pages.js.map +1 -1
- package/dist/repos/public-api.d.ts +140 -0
- package/dist/repos/public-api.d.ts.map +1 -0
- package/dist/repos/public-api.js +299 -0
- package/dist/repos/public-api.js.map +1 -0
- package/dist/schema/json-payloads.d.ts +27 -2
- package/dist/schema/json-payloads.d.ts.map +1 -1
- package/dist/schema/json-payloads.js +41 -2
- package/dist/schema/json-payloads.js.map +1 -1
- package/dist/schema/settings-registry.d.ts +65 -0
- package/dist/schema/settings-registry.d.ts.map +1 -1
- package/dist/schema/settings-registry.js +183 -0
- package/dist/schema/settings-registry.js.map +1 -1
- package/dist/schema/tables.d.ts +177 -1
- package/dist/schema/tables.d.ts.map +1 -1
- package/dist/schema/tables.js +6 -0
- package/dist/schema/tables.js.map +1 -1
- package/package.json +1 -1
package/dist/ids.d.ts
CHANGED
|
@@ -44,8 +44,14 @@ export declare const ID_PREFIXES: {
|
|
|
44
44
|
readonly whd: "adminium_webhook_deliveries";
|
|
45
45
|
readonly flag: "adminium_feature_flags";
|
|
46
46
|
readonly mft: "adminium_manifests";
|
|
47
|
+
readonly mat: "adminium_manifest_attachments";
|
|
48
|
+
readonly aoc: "adminium_add_on_credentials";
|
|
47
49
|
readonly loc: "adminium_locales";
|
|
48
50
|
readonly trn: "adminium_translations";
|
|
51
|
+
readonly pbk: "adminium_public_keys";
|
|
52
|
+
readonly psc: "adminium_public_scopes";
|
|
53
|
+
readonly pss: "adminium_public_sessions";
|
|
54
|
+
readonly pch: "adminium_public_challenges";
|
|
49
55
|
};
|
|
50
56
|
export type IdPrefix = keyof typeof ID_PREFIXES;
|
|
51
57
|
/** Maximum column width every id must fit (07-meta-store.md §2.1). */
|
package/dist/ids.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ids.d.ts","sourceRoot":"","sources":["../src/ids.ts"],"names":[],"mappings":"AACA;;;;;;;;;;;GAWG;AAQH,QAAA,MAAM,QAAQ,QAAsB,CAAC;AAErC;;;;;GAKG;AACH,eAAO,MAAM,WAAW
|
|
1
|
+
{"version":3,"file":"ids.d.ts","sourceRoot":"","sources":["../src/ids.ts"],"names":[],"mappings":"AACA;;;;;;;;;;;GAWG;AAQH,QAAA,MAAM,QAAQ,QAAsB,CAAC;AAErC;;;;;GAKG;AACH,eAAO,MAAM,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA2Cd,CAAC;AAEX,MAAM,MAAM,QAAQ,GAAG,MAAM,OAAO,WAAW,CAAC;AAOhD,sEAAsE;AACtE,eAAO,MAAM,aAAa,KAAK,CAAC;AAEhC,qBAAa,cAAe,SAAQ,KAAK;IAC9B,IAAI,SAAoB;CAClC;AAiDD;;;;GAIG;AACH,wBAAgB,KAAK,CAAC,MAAM,EAAE,QAAQ,GAAG,MAAM,CAc9C;AAED,MAAM,WAAW,QAAQ;IACvB,qCAAqC;IACrC,MAAM,EAAE,QAAQ,CAAC;IACjB,2DAA2D;IAC3D,KAAK,EAAE,MAAM,CAAC;IACd,6BAA6B;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,iDAAiD;IACjD,IAAI,EAAE,MAAM,CAAC;CACd;AAED,8FAA8F;AAC9F,wBAAgB,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,QAAQ,CAY5C;AAED,uFAAuF;AACvF,wBAAgB,IAAI,CAAC,EAAE,EAAE,OAAO,EAAE,MAAM,CAAC,EAAE,QAAQ,GAAG,OAAO,CAQ5D;AAED,yDAAyD;AACzD,wBAAgB,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAEzC;AAED,2CAA2C;AAC3C,wBAAgB,aAAa,IAAI,IAAI,CAGpC;AAED,OAAO,EAAE,QAAQ,IAAI,YAAY,EAAE,CAAC"}
|
package/dist/ids.js
CHANGED
|
@@ -50,8 +50,22 @@ export const ID_PREFIXES = {
|
|
|
50
50
|
whd: 'adminium_webhook_deliveries',
|
|
51
51
|
flag: 'adminium_feature_flags',
|
|
52
52
|
mft: 'adminium_manifests',
|
|
53
|
+
// 26-add-on-runtime.md §4. `mat` and `aoc` are separate spaces rather than
|
|
54
|
+
// reusing `mft`: an attachment and a credential are addressable rows with
|
|
55
|
+
// their own lifetimes (an attachment is disabled per host, a credential is
|
|
56
|
+
// deleted on disconnect while the manifest stays), and sharing an id space
|
|
57
|
+
// with the manifest they hang off would make a stray id look valid.
|
|
58
|
+
mat: 'adminium_manifest_attachments',
|
|
59
|
+
aoc: 'adminium_add_on_credentials',
|
|
53
60
|
loc: 'adminium_locales',
|
|
54
61
|
trn: 'adminium_translations',
|
|
62
|
+
// 28-public-surface.md §3.3 — the public surface. `pbk` is deliberately not
|
|
63
|
+
// `key`: a publishable key is never an RbacPrincipal (28 D3) and sharing an
|
|
64
|
+
// id space with `adminium_api_keys` would blur exactly that boundary.
|
|
65
|
+
pbk: 'adminium_public_keys',
|
|
66
|
+
psc: 'adminium_public_scopes',
|
|
67
|
+
pss: 'adminium_public_sessions',
|
|
68
|
+
pch: 'adminium_public_challenges',
|
|
55
69
|
};
|
|
56
70
|
const PREFIX_SET = new Set(Object.keys(ID_PREFIXES));
|
|
57
71
|
/** `<prefix>_<26 Crockford chars>` — Crockford charset excludes I, L, O, U. */
|
package/dist/ids.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ids.js","sourceRoot":"","sources":["../src/ids.ts"],"names":[],"mappings":"AAAA,yCAAyC;AACzC;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE1C,iDAAiD;AACjD,MAAM,QAAQ,GAAG,kCAAkC,CAAC;AACpD,MAAM,QAAQ,GAAG,EAAE,CAAC;AACpB,MAAM,QAAQ,GAAG,EAAE,CAAC;AACpB,MAAM,QAAQ,GAAG,QAAQ,GAAG,QAAQ,CAAC;AAErC;;;;;GAKG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG;IACzB,GAAG,EAAE,gBAAgB;IACrB,IAAI,EAAE,mBAAmB;IACzB,GAAG,EAAE,0BAA0B;IAC/B,GAAG,EAAE,mBAAmB;IACxB,IAAI,EAAE,gBAAgB;IACtB,IAAI,EAAE,2BAA2B;IACjC,GAAG,EAAE,oBAAoB;IACzB,GAAG,EAAE,eAAe;IACpB,IAAI,EAAE,sBAAsB;IAC5B,IAAI,EAAE,2BAA2B;IACjC,GAAG,EAAE,2BAA2B;IAChC,IAAI,EAAE,gBAAgB;IACtB,IAAI,EAAE,gBAAgB;IACtB,GAAG,EAAE,mBAAmB;IACxB,GAAG,EAAE,wBAAwB;IAC7B,IAAI,EAAE,sBAAsB;IAC5B,IAAI,EAAE,0BAA0B;IAChC,GAAG,EAAE,4BAA4B;IACjC,GAAG,EAAE,kBAAkB;IACvB,GAAG,EAAE,kBAAkB;IACvB,IAAI,EAAE,gBAAgB;IACtB,GAAG,EAAE,0BAA0B;IAC/B,EAAE,EAAE,mBAAmB;IACvB,GAAG,EAAE,6BAA6B;IAClC,IAAI,EAAE,wBAAwB;IAC9B,GAAG,EAAE,oBAAoB;IACzB,GAAG,EAAE,kBAAkB;IACvB,GAAG,EAAE,uBAAuB;
|
|
1
|
+
{"version":3,"file":"ids.js","sourceRoot":"","sources":["../src/ids.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wave 0014 — the public surface (28-public-surface.md §3).
|
|
3
|
+
*
|
|
4
|
+
* Four tables behind the scoped, off-by-default `/api/v1/public` namespace:
|
|
5
|
+
* the browser-safe key kind, the scope document that says what a key may reach,
|
|
6
|
+
* the end-customer session a `claim` mints, and the one-time challenge the
|
|
7
|
+
* `email-code` claim tier needs.
|
|
8
|
+
*
|
|
9
|
+
* WHY THESE ARE NOT `adminium_api_keys` ROWS WITH A `kind` COLUMN (28 §3.3).
|
|
10
|
+
* Two properties diverge and both are load-bearing:
|
|
11
|
+
*
|
|
12
|
+
* 1. The secret is RE-READABLE by an admin. `adminium_api_keys` reveals once
|
|
13
|
+
* and never again, which is right for a server-side credential a human
|
|
14
|
+
* copies into a deployment. An `adm_pub_` secret lives in a public bundle
|
|
15
|
+
* and has to survive a rebuild months later, so the row keeps a reversible
|
|
16
|
+
* copy alongside the hash.
|
|
17
|
+
* 2. A publishable key is NEVER an `RbacPrincipal` (28 D3). `parseBearerApiKey`
|
|
18
|
+
* gates on the `adm_sk_` prefix, so a token from this table cannot resolve
|
|
19
|
+
* through the rbac plugin at all — which is what makes it inert on every
|
|
20
|
+
* other route BY CONSTRUCTION rather than by an allow-list somebody has to
|
|
21
|
+
* maintain. Sharing a table would invite exactly the `kind`-check-per-route
|
|
22
|
+
* that property exists to avoid.
|
|
23
|
+
*
|
|
24
|
+
* NO SETTINGS TABLE HERE, deliberately (28 D20). Tenant configuration is
|
|
25
|
+
* hybrid: the only key Adminium must own is the `timezone` it needs to
|
|
26
|
+
* interpret its own `timestamptz` values, and that rides on the SCOPE document
|
|
27
|
+
* below — already per-connection, already operator-authored, already served on
|
|
28
|
+
* `/public/config`. A global settings key would be one line cheaper and wrong
|
|
29
|
+
* the moment an instance holds two connections for businesses in different
|
|
30
|
+
* zones. Everything else an app needs (tax rates, tip presets, opening hours)
|
|
31
|
+
* stays in the app's own tables, where the generated dashboard edits it for
|
|
32
|
+
* free.
|
|
33
|
+
*
|
|
34
|
+
* `0013_connection_last_error_hint` is the latest applied migration; never edit
|
|
35
|
+
* a shipped one (the one-time checksum exception in `index.ts` was pre-release).
|
|
36
|
+
*/
|
|
37
|
+
import type { Kysely } from 'kysely';
|
|
38
|
+
import type { ColumnHelpers } from '../columns.js';
|
|
39
|
+
export declare function up(db: Kysely<unknown>, c: ColumnHelpers): Promise<void>;
|
|
40
|
+
//# sourceMappingURL=0014_public_surface.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"0014_public_surface.d.ts","sourceRoot":"","sources":["../../src/migrations/0014_public_surface.ts"],"names":[],"mappings":"AACA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAErC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAGnD,wBAAsB,EAAE,CAAC,EAAE,EAAE,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CA6L7E"}
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
// SPDX-License-Identifier: AGPL-3.0-only
|
|
2
|
+
/**
|
|
3
|
+
* Wave 0014 — the public surface (28-public-surface.md §3).
|
|
4
|
+
*
|
|
5
|
+
* Four tables behind the scoped, off-by-default `/api/v1/public` namespace:
|
|
6
|
+
* the browser-safe key kind, the scope document that says what a key may reach,
|
|
7
|
+
* the end-customer session a `claim` mints, and the one-time challenge the
|
|
8
|
+
* `email-code` claim tier needs.
|
|
9
|
+
*
|
|
10
|
+
* WHY THESE ARE NOT `adminium_api_keys` ROWS WITH A `kind` COLUMN (28 §3.3).
|
|
11
|
+
* Two properties diverge and both are load-bearing:
|
|
12
|
+
*
|
|
13
|
+
* 1. The secret is RE-READABLE by an admin. `adminium_api_keys` reveals once
|
|
14
|
+
* and never again, which is right for a server-side credential a human
|
|
15
|
+
* copies into a deployment. An `adm_pub_` secret lives in a public bundle
|
|
16
|
+
* and has to survive a rebuild months later, so the row keeps a reversible
|
|
17
|
+
* copy alongside the hash.
|
|
18
|
+
* 2. A publishable key is NEVER an `RbacPrincipal` (28 D3). `parseBearerApiKey`
|
|
19
|
+
* gates on the `adm_sk_` prefix, so a token from this table cannot resolve
|
|
20
|
+
* through the rbac plugin at all — which is what makes it inert on every
|
|
21
|
+
* other route BY CONSTRUCTION rather than by an allow-list somebody has to
|
|
22
|
+
* maintain. Sharing a table would invite exactly the `kind`-check-per-route
|
|
23
|
+
* that property exists to avoid.
|
|
24
|
+
*
|
|
25
|
+
* NO SETTINGS TABLE HERE, deliberately (28 D20). Tenant configuration is
|
|
26
|
+
* hybrid: the only key Adminium must own is the `timezone` it needs to
|
|
27
|
+
* interpret its own `timestamptz` values, and that rides on the SCOPE document
|
|
28
|
+
* below — already per-connection, already operator-authored, already served on
|
|
29
|
+
* `/public/config`. A global settings key would be one line cheaper and wrong
|
|
30
|
+
* the moment an instance holds two connections for businesses in different
|
|
31
|
+
* zones. Everything else an app needs (tax rates, tip presets, opening hours)
|
|
32
|
+
* stays in the app's own tables, where the generated dashboard edits it for
|
|
33
|
+
* free.
|
|
34
|
+
*
|
|
35
|
+
* `0013_connection_last_error_hint` is the latest applied migration; never edit
|
|
36
|
+
* a shipped one (the one-time checksum exception in `index.ts` was pre-release).
|
|
37
|
+
*/
|
|
38
|
+
import { metaTable } from '../prefix.js';
|
|
39
|
+
export async function up(db, c) {
|
|
40
|
+
/*
|
|
41
|
+
* The scope document. One row per (connection, side) the operator publishes.
|
|
42
|
+
*
|
|
43
|
+
* `document` is the compiled-from JSON: resources, their logical refs, the
|
|
44
|
+
* `expose` column allow-list, `filterable`/`searchable`/`orderable`, the
|
|
45
|
+
* mandatory `where` predicate, and the claim strategy. It is validated by
|
|
46
|
+
* `compileScope` on every write and on every boot — never trusted from disk.
|
|
47
|
+
*
|
|
48
|
+
* `timezone` is a COLUMN rather than a field inside `document` because it is
|
|
49
|
+
* the one datum the SERIALIZER needs, not the caller: a reader has to reach it
|
|
50
|
+
* without parsing and validating the whole document first.
|
|
51
|
+
*
|
|
52
|
+
* `proposed_from_manifest` records whether this row was seeded from a
|
|
53
|
+
* manifest's `publicApi.propose` block (O2 option b) or authored from scratch
|
|
54
|
+
* in Studio (option c). It exists so widening past a publisher's proposal can
|
|
55
|
+
* be audited, and it is nullable because option (c) is unblocked today and
|
|
56
|
+
* option (b) is not yet ruled.
|
|
57
|
+
*/
|
|
58
|
+
await db.schema
|
|
59
|
+
.createTable(metaTable('public_scopes'))
|
|
60
|
+
.ifNotExists()
|
|
61
|
+
.addColumn('id', c.id, (col) => col.primaryKey())
|
|
62
|
+
.addColumn('connection_id', c.id, (col) => col.notNull())
|
|
63
|
+
.addColumn('side', c.str(16), (col) => col.notNull())
|
|
64
|
+
.addColumn('name', c.str(80), (col) => col.notNull())
|
|
65
|
+
/** IANA zone, e.g. `Europe/London`. Required — see 28 D20. */
|
|
66
|
+
.addColumn('timezone', c.str(64), (col) => col.notNull())
|
|
67
|
+
.addColumn('document', c.json, (col) => col.notNull())
|
|
68
|
+
.addColumn('proposed_from_manifest', c.str(80))
|
|
69
|
+
.addColumn('created_by', c.id)
|
|
70
|
+
.addColumn('created_at', c.ts, (col) => col.notNull())
|
|
71
|
+
.addColumn('updated_at', c.ts, (col) => col.notNull())
|
|
72
|
+
.addForeignKeyConstraint('fk_adminium_public_scopes_connection', ['connection_id'], metaTable('connections'), ['id'], (cb) => cb.onDelete('cascade'))
|
|
73
|
+
.addForeignKeyConstraint('fk_adminium_public_scopes_created_by', ['created_by'], metaTable('users'), ['id'], (cb) => cb.onDelete('set null'))
|
|
74
|
+
.execute();
|
|
75
|
+
await db.schema
|
|
76
|
+
.createIndex('ix_adminium_public_scopes_connection')
|
|
77
|
+
.on(metaTable('public_scopes'))
|
|
78
|
+
.columns(['connection_id'])
|
|
79
|
+
.execute();
|
|
80
|
+
/*
|
|
81
|
+
* The publishable key.
|
|
82
|
+
*
|
|
83
|
+
* `token_hash` is what a request is verified against. `token_encrypted` is the
|
|
84
|
+
* reversible copy that makes the secret re-readable (see the header) — it uses
|
|
85
|
+
* the same `dsnCryptoFromSecret` envelope as connection DSNs, so it is
|
|
86
|
+
* unreadable without `ADMINIUM_SECRET` and it never leaves the server except
|
|
87
|
+
* through an authenticated reveal.
|
|
88
|
+
*
|
|
89
|
+
* `origins` is a JSON array narrowing the instance-wide
|
|
90
|
+
* `ADMINIUM_PUBLIC_API_ORIGINS` allow-list for this one key. Empty array means
|
|
91
|
+
* "no narrowing" — it does NOT mean "any origin", because the env var is
|
|
92
|
+
* always the outer bound.
|
|
93
|
+
*
|
|
94
|
+
* `scope_id` is `restrict` on delete, not `cascade`: deleting a scope out from
|
|
95
|
+
* under a live key would silently turn a working public surface into a 500,
|
|
96
|
+
* so the operator has to revoke the key first and see what they are breaking.
|
|
97
|
+
*/
|
|
98
|
+
await db.schema
|
|
99
|
+
.createTable(metaTable('public_keys'))
|
|
100
|
+
.ifNotExists()
|
|
101
|
+
.addColumn('id', c.id, (col) => col.primaryKey())
|
|
102
|
+
.addColumn('name', c.str(80), (col) => col.notNull())
|
|
103
|
+
/** Display fragment, e.g. `adm_pub_4f2a91cd`. */
|
|
104
|
+
.addColumn('prefix', c.str(32), (col) => col.notNull())
|
|
105
|
+
.addColumn('token_hash', c.str(128), (col) => col.notNull())
|
|
106
|
+
.addColumn('token_encrypted', c.text, (col) => col.notNull())
|
|
107
|
+
.addColumn('scope_id', c.id, (col) => col.notNull())
|
|
108
|
+
.addColumn('side', c.str(16), (col) => col.notNull())
|
|
109
|
+
.addColumn('origins', c.json, (col) => col.notNull())
|
|
110
|
+
.addColumn('expires_at', c.ts)
|
|
111
|
+
.addColumn('revoked_at', c.ts)
|
|
112
|
+
.addColumn('last_used_at', c.ts)
|
|
113
|
+
.addColumn('created_by', c.id)
|
|
114
|
+
.addColumn('created_at', c.ts, (col) => col.notNull())
|
|
115
|
+
.addColumn('updated_at', c.ts, (col) => col.notNull())
|
|
116
|
+
.addForeignKeyConstraint('fk_adminium_public_keys_scope', ['scope_id'], metaTable('public_scopes'), ['id'], (cb) => cb.onDelete('restrict'))
|
|
117
|
+
.addForeignKeyConstraint('fk_adminium_public_keys_created_by', ['created_by'], metaTable('users'), ['id'], (cb) => cb.onDelete('set null'))
|
|
118
|
+
.execute();
|
|
119
|
+
/* Lookup is by prefix on every public request — the hot path of this wave. */
|
|
120
|
+
await db.schema
|
|
121
|
+
.createIndex('ix_adminium_public_keys_prefix')
|
|
122
|
+
.on(metaTable('public_keys'))
|
|
123
|
+
.columns(['prefix'])
|
|
124
|
+
.execute();
|
|
125
|
+
/*
|
|
126
|
+
* The end-customer session minted by `POST /public/claim` (28 §3.4).
|
|
127
|
+
*
|
|
128
|
+
* `grants` is the resolved claim — the mandatory predicate later requests are
|
|
129
|
+
* filtered by, e.g. "patient_id = 41". It is stored RESOLVED rather than
|
|
130
|
+
* re-derived per request so that editing a scope cannot retroactively widen a
|
|
131
|
+
* session that is already in someone's browser.
|
|
132
|
+
*
|
|
133
|
+
* There is no `user_id`. An end customer is deliberately NOT an
|
|
134
|
+
* `adminium_users` row (28 §3.4) — Adminium grows no second identity system,
|
|
135
|
+
* and this table is the whole of what a claimed customer is.
|
|
136
|
+
*/
|
|
137
|
+
await db.schema
|
|
138
|
+
.createTable(metaTable('public_sessions'))
|
|
139
|
+
.ifNotExists()
|
|
140
|
+
.addColumn('id', c.id, (col) => col.primaryKey())
|
|
141
|
+
.addColumn('key_id', c.id, (col) => col.notNull())
|
|
142
|
+
.addColumn('token_hash', c.str(128), (col) => col.notNull())
|
|
143
|
+
.addColumn('grants', c.json, (col) => col.notNull())
|
|
144
|
+
.addColumn('expires_at', c.ts, (col) => col.notNull())
|
|
145
|
+
.addColumn('created_at', c.ts, (col) => col.notNull())
|
|
146
|
+
.addColumn('last_seen_at', c.ts)
|
|
147
|
+
.addForeignKeyConstraint('fk_adminium_public_sessions_key', ['key_id'], metaTable('public_keys'), ['id'], (cb) => cb.onDelete('cascade'))
|
|
148
|
+
.execute();
|
|
149
|
+
await db.schema
|
|
150
|
+
.createIndex('ix_adminium_public_sessions_token_hash')
|
|
151
|
+
.on(metaTable('public_sessions'))
|
|
152
|
+
.columns(['token_hash'])
|
|
153
|
+
.execute();
|
|
154
|
+
/*
|
|
155
|
+
* One-time challenges for the `email-code` claim tier (28 D11/D17).
|
|
156
|
+
*
|
|
157
|
+
* `attempts` is on the row rather than in a counter elsewhere because the
|
|
158
|
+
* containment property this tier is bought for — a low-entropy code is only
|
|
159
|
+
* safe if guesses are bounded — has to survive a process restart, and the
|
|
160
|
+
* rate limiter is an in-process `Map` that does not.
|
|
161
|
+
*
|
|
162
|
+
* `destination_hash` rather than the address itself: a challenge table is a
|
|
163
|
+
* list of customer email addresses otherwise, which is a needless standing
|
|
164
|
+
* disclosure for a row whose whole life is five minutes.
|
|
165
|
+
*/
|
|
166
|
+
await db.schema
|
|
167
|
+
.createTable(metaTable('public_challenges'))
|
|
168
|
+
.ifNotExists()
|
|
169
|
+
.addColumn('id', c.id, (col) => col.primaryKey())
|
|
170
|
+
.addColumn('key_id', c.id, (col) => col.notNull())
|
|
171
|
+
.addColumn('ref', c.str(64), (col) => col.notNull())
|
|
172
|
+
.addColumn('destination_hash', c.str(128), (col) => col.notNull())
|
|
173
|
+
.addColumn('code_hash', c.str(128), (col) => col.notNull())
|
|
174
|
+
.addColumn('attempts', c.int, (col) => col.notNull().defaultTo(0))
|
|
175
|
+
.addColumn('consumed_at', c.ts)
|
|
176
|
+
.addColumn('expires_at', c.ts, (col) => col.notNull())
|
|
177
|
+
.addColumn('created_at', c.ts, (col) => col.notNull())
|
|
178
|
+
.addForeignKeyConstraint('fk_adminium_public_challenges_key', ['key_id'], metaTable('public_keys'), ['id'], (cb) => cb.onDelete('cascade'))
|
|
179
|
+
.execute();
|
|
180
|
+
await db.schema
|
|
181
|
+
.createIndex('ix_adminium_public_challenges_key_ref')
|
|
182
|
+
.on(metaTable('public_challenges'))
|
|
183
|
+
.columns(['key_id', 'ref'])
|
|
184
|
+
.execute();
|
|
185
|
+
}
|
|
186
|
+
//# sourceMappingURL=0014_public_surface.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"0014_public_surface.js","sourceRoot":"","sources":["../../src/migrations/0014_public_surface.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wave 0015 — `adminium_connections.timezone` / `.currency` (28-T34, D20).
|
|
3
|
+
*
|
|
4
|
+
* ─── Why these are connection columns and not a settings table ──────────────
|
|
5
|
+
*
|
|
6
|
+
* D20 ruled O7 as a hybrid: Adminium owns only what it needs to serve correct
|
|
7
|
+
* data, and it placed `timezone` on the SCOPE document — explicitly rejecting a
|
|
8
|
+
* global `adminium_settings` key, because that is "wrong the moment an instance
|
|
9
|
+
* holds two connections for businesses in different zones".
|
|
10
|
+
*
|
|
11
|
+
* That argument is right, and it points one level further than the ruling took
|
|
12
|
+
* it. The thing a zone belongs to is the BUSINESS, and the business is the
|
|
13
|
+
* database — the connection. A scope is a view onto a connection, and an
|
|
14
|
+
* instance can hold several scopes over one connection; putting the zone on the
|
|
15
|
+
* scope means the same business can be described by two scopes that disagree
|
|
16
|
+
* about what time it is there, with nothing to reconcile them.
|
|
17
|
+
*
|
|
18
|
+
* ─── What forced it ────────────────────────────────────────────────────────
|
|
19
|
+
*
|
|
20
|
+
* A surface hosted BY Adminium reads through the operator's session. It has no
|
|
21
|
+
* publishable key and therefore NO SCOPE, so under the old placement there was
|
|
22
|
+
* nowhere for it to read a zone from at all — the app took it as a build
|
|
23
|
+
* argument, which makes a property of the business a property of the artifact.
|
|
24
|
+
* Hosted mode postdates D20; this is the consequence, not a reversal.
|
|
25
|
+
*
|
|
26
|
+
* ─── What does not change ──────────────────────────────────────────────────
|
|
27
|
+
*
|
|
28
|
+
* `scope.timezone` stays, as an OVERRIDE. It becomes optional and inherits from
|
|
29
|
+
* the connection when omitted, and `compileScope` still refuses a scope that
|
|
30
|
+
* ends up with no valid IANA zone from either source — so D20's boot-fatal
|
|
31
|
+
* guarantee survives intact and every scope already stored keeps working
|
|
32
|
+
* unchanged.
|
|
33
|
+
*
|
|
34
|
+
* Both columns are nullable with no default. There is no defensible default for
|
|
35
|
+
* a timezone: a wrong zone is worse than a missing one, because it looks like
|
|
36
|
+
* data. `null` means "not configured", and the refusal happens where the value
|
|
37
|
+
* is needed rather than being papered over here.
|
|
38
|
+
*/
|
|
39
|
+
import type { Kysely } from 'kysely';
|
|
40
|
+
import type { ColumnHelpers } from '../columns.js';
|
|
41
|
+
export declare function up(db: Kysely<unknown>, c: ColumnHelpers): Promise<void>;
|
|
42
|
+
//# sourceMappingURL=0015_connection_tenant_config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"0015_connection_tenant_config.d.ts","sourceRoot":"","sources":["../../src/migrations/0015_connection_tenant_config.ts"],"names":[],"mappings":"AACA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAErC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAGnD,wBAAsB,EAAE,CAAC,EAAE,EAAE,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAY7E"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
// SPDX-License-Identifier: AGPL-3.0-only
|
|
2
|
+
/**
|
|
3
|
+
* Wave 0015 — `adminium_connections.timezone` / `.currency` (28-T34, D20).
|
|
4
|
+
*
|
|
5
|
+
* ─── Why these are connection columns and not a settings table ──────────────
|
|
6
|
+
*
|
|
7
|
+
* D20 ruled O7 as a hybrid: Adminium owns only what it needs to serve correct
|
|
8
|
+
* data, and it placed `timezone` on the SCOPE document — explicitly rejecting a
|
|
9
|
+
* global `adminium_settings` key, because that is "wrong the moment an instance
|
|
10
|
+
* holds two connections for businesses in different zones".
|
|
11
|
+
*
|
|
12
|
+
* That argument is right, and it points one level further than the ruling took
|
|
13
|
+
* it. The thing a zone belongs to is the BUSINESS, and the business is the
|
|
14
|
+
* database — the connection. A scope is a view onto a connection, and an
|
|
15
|
+
* instance can hold several scopes over one connection; putting the zone on the
|
|
16
|
+
* scope means the same business can be described by two scopes that disagree
|
|
17
|
+
* about what time it is there, with nothing to reconcile them.
|
|
18
|
+
*
|
|
19
|
+
* ─── What forced it ────────────────────────────────────────────────────────
|
|
20
|
+
*
|
|
21
|
+
* A surface hosted BY Adminium reads through the operator's session. It has no
|
|
22
|
+
* publishable key and therefore NO SCOPE, so under the old placement there was
|
|
23
|
+
* nowhere for it to read a zone from at all — the app took it as a build
|
|
24
|
+
* argument, which makes a property of the business a property of the artifact.
|
|
25
|
+
* Hosted mode postdates D20; this is the consequence, not a reversal.
|
|
26
|
+
*
|
|
27
|
+
* ─── What does not change ──────────────────────────────────────────────────
|
|
28
|
+
*
|
|
29
|
+
* `scope.timezone` stays, as an OVERRIDE. It becomes optional and inherits from
|
|
30
|
+
* the connection when omitted, and `compileScope` still refuses a scope that
|
|
31
|
+
* ends up with no valid IANA zone from either source — so D20's boot-fatal
|
|
32
|
+
* guarantee survives intact and every scope already stored keeps working
|
|
33
|
+
* unchanged.
|
|
34
|
+
*
|
|
35
|
+
* Both columns are nullable with no default. There is no defensible default for
|
|
36
|
+
* a timezone: a wrong zone is worse than a missing one, because it looks like
|
|
37
|
+
* data. `null` means "not configured", and the refusal happens where the value
|
|
38
|
+
* is needed rather than being papered over here.
|
|
39
|
+
*/
|
|
40
|
+
import { metaTable } from '../prefix.js';
|
|
41
|
+
export async function up(db, c) {
|
|
42
|
+
await db.schema
|
|
43
|
+
.alterTable(metaTable('connections'))
|
|
44
|
+
// IANA zone, e.g. `Europe/Lisbon`. 64 matches the scope schema's own cap.
|
|
45
|
+
.addColumn('timezone', c.str(64))
|
|
46
|
+
.execute();
|
|
47
|
+
await db.schema
|
|
48
|
+
.alterTable(metaTable('connections'))
|
|
49
|
+
// ISO-4217, three letters. Null when this business's data carries no money.
|
|
50
|
+
.addColumn('currency', c.str(3))
|
|
51
|
+
.execute();
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=0015_connection_tenant_config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"0015_connection_tenant_config.js","sourceRoot":"","sources":["../../src/migrations/0015_connection_tenant_config.ts"],"names":[],"mappings":"AAAA,yCAAyC;AACzC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AAKH,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAEzC,MAAM,CAAC,KAAK,UAAU,EAAE,CAAC,EAAmB,EAAE,CAAgB;IAC5D,MAAM,EAAE,CAAC,MAAM;SACZ,UAAU,CAAC,SAAS,CAAC,aAAa,CAAC,CAAC;QACrC,0EAA0E;SACzE,SAAS,CAAC,UAAU,EAAE,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;SAChC,OAAO,EAAE,CAAC;IAEb,MAAM,EAAE,CAAC,MAAM;SACZ,UAAU,CAAC,SAAS,CAAC,aAAa,CAAC,CAAC;QACrC,4EAA4E;SAC3E,SAAS,CAAC,UAAU,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;SAC/B,OAAO,EAAE,CAAC;AACf,CAAC"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wave 0016 — per-record audit lookup (30-record-pages.md WS-A).
|
|
3
|
+
*
|
|
4
|
+
* The record page's Activity tab needs "every audit entry for THIS record".
|
|
5
|
+
* The entry's `entity` column stores a RecordRef, but as packed JSON text —
|
|
6
|
+
* filtering on it means either a per-dialect JSON-extract expression (three
|
|
7
|
+
* syntaxes, none of them indexable without generated columns) or a full scan
|
|
8
|
+
* of the `data` category. Both are the amplification anti-pattern D6 exists
|
|
9
|
+
* to rule out, so the ref's two lookup facts are denormalized into real,
|
|
10
|
+
* indexed columns instead:
|
|
11
|
+
*
|
|
12
|
+
* entity_table the ref's qualified table ("public.invoices")
|
|
13
|
+
* entity_id the canonical record-id string — `pkLabel` form: a single
|
|
14
|
+
* PK stringified, a composite PK as the JSON value tuple —
|
|
15
|
+
* the same string the record route and `rowIdOf` use. See
|
|
16
|
+
* `keysOf` for why a BACKFILLED composite reads the ref's
|
|
17
|
+
* `label` rather than re-deriving from `pk`.
|
|
18
|
+
*
|
|
19
|
+
* Both are clamped by `auditEntityKeyPart` (write AND query side, so a
|
|
20
|
+
* truncated key still matches itself), written by `auditRepo.append` from
|
|
21
|
+
* this wave on, and BACKFILLED here from the stored refs so pre-existing
|
|
22
|
+
* activity shows up on day one. The backfill parses each ref in JS — the
|
|
23
|
+
* only portable JSON reader across the three dialects — in id-keyset batches;
|
|
24
|
+
* audit volume is retention-bounded (§8 `audit-log` policy), so this is a
|
|
25
|
+
* one-time bounded cost.
|
|
26
|
+
*
|
|
27
|
+
* Bulk writes (`record.bulk-*`) carry no per-row entity and stay out of the
|
|
28
|
+
* feed by design (30 D6) — their rows simply keep NULL keys.
|
|
29
|
+
*/
|
|
30
|
+
import type { Kysely } from 'kysely';
|
|
31
|
+
import type { ColumnHelpers } from '../columns.js';
|
|
32
|
+
export declare function up(db: Kysely<unknown>, c: ColumnHelpers): Promise<void>;
|
|
33
|
+
//# sourceMappingURL=0016_audit_entity.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"0016_audit_entity.d.ts","sourceRoot":"","sources":["../../src/migrations/0016_audit_entity.ts"],"names":[],"mappings":"AACA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAErC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAuEnD,wBAAsB,EAAE,CAAC,EAAE,EAAE,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CA8C7E"}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
// SPDX-License-Identifier: AGPL-3.0-only
|
|
2
|
+
/**
|
|
3
|
+
* Wave 0016 — per-record audit lookup (30-record-pages.md WS-A).
|
|
4
|
+
*
|
|
5
|
+
* The record page's Activity tab needs "every audit entry for THIS record".
|
|
6
|
+
* The entry's `entity` column stores a RecordRef, but as packed JSON text —
|
|
7
|
+
* filtering on it means either a per-dialect JSON-extract expression (three
|
|
8
|
+
* syntaxes, none of them indexable without generated columns) or a full scan
|
|
9
|
+
* of the `data` category. Both are the amplification anti-pattern D6 exists
|
|
10
|
+
* to rule out, so the ref's two lookup facts are denormalized into real,
|
|
11
|
+
* indexed columns instead:
|
|
12
|
+
*
|
|
13
|
+
* entity_table the ref's qualified table ("public.invoices")
|
|
14
|
+
* entity_id the canonical record-id string — `pkLabel` form: a single
|
|
15
|
+
* PK stringified, a composite PK as the JSON value tuple —
|
|
16
|
+
* the same string the record route and `rowIdOf` use. See
|
|
17
|
+
* `keysOf` for why a BACKFILLED composite reads the ref's
|
|
18
|
+
* `label` rather than re-deriving from `pk`.
|
|
19
|
+
*
|
|
20
|
+
* Both are clamped by `auditEntityKeyPart` (write AND query side, so a
|
|
21
|
+
* truncated key still matches itself), written by `auditRepo.append` from
|
|
22
|
+
* this wave on, and BACKFILLED here from the stored refs so pre-existing
|
|
23
|
+
* activity shows up on day one. The backfill parses each ref in JS — the
|
|
24
|
+
* only portable JSON reader across the three dialects — in id-keyset batches;
|
|
25
|
+
* audit volume is retention-bounded (§8 `audit-log` policy), so this is a
|
|
26
|
+
* one-time bounded cost.
|
|
27
|
+
*
|
|
28
|
+
* Bulk writes (`record.bulk-*`) carry no per-row entity and stay out of the
|
|
29
|
+
* feed by design (30 D6) — their rows simply keep NULL keys.
|
|
30
|
+
*/
|
|
31
|
+
import { metaTable } from '../prefix.js';
|
|
32
|
+
import { AUDIT_ENTITY_KEY_MAX, auditEntityKeyOf, auditEntityKeyPart, recordRefSchema, } from '../schema/json-payloads.js';
|
|
33
|
+
const BACKFILL_BATCH = 500;
|
|
34
|
+
/**
|
|
35
|
+
* A STORED ref has lost the one thing `auditEntityKeyOf` reads for a composite
|
|
36
|
+
* key: the order of `pk`.
|
|
37
|
+
*
|
|
38
|
+
* `entity` is `jsonb` on postgres and `json` on mysql, and both normalise
|
|
39
|
+
* object keys on the way in — jsonb by key length then bytes, mysql likewise.
|
|
40
|
+
* So `{invoice_id, line}` comes back as `{line, invoice_id}` and
|
|
41
|
+
* `Object.values()` yields the tuple backwards, producing an id that matches no
|
|
42
|
+
* record. Only sqlite, which stores the column as text, keeps the written
|
|
43
|
+
* order, which is why deriving from `pk` passed there and failed on the other
|
|
44
|
+
* two. There is no recovering the order from the stored value: jsonb and mysql
|
|
45
|
+
* json are parsed forms, so even casting back to text returns the normalised
|
|
46
|
+
* key order rather than the original.
|
|
47
|
+
*
|
|
48
|
+
* A single-column key has no order to lose, so it still derives from `pk`.
|
|
49
|
+
*
|
|
50
|
+
* A composite one falls back to the ref's `label`, which the only writer of a
|
|
51
|
+
* record ref (`routes/data`) sets to `pkLabel(table, pk)` — the same tuple, in
|
|
52
|
+
* the table's primary-key order, as a plain string no store reorders. `label`
|
|
53
|
+
* is display text by contract and NOT guaranteed to be that tuple, so it is
|
|
54
|
+
* used only when it still parses as a JSON array of the right arity; anything
|
|
55
|
+
* else leaves the row NULL. A legacy row that opts out of the feed is the
|
|
56
|
+
* documented outcome for a ref this reader cannot trust — a confidently wrong
|
|
57
|
+
* key that silently attaches one record's history to another is not.
|
|
58
|
+
*/
|
|
59
|
+
function keysOf(entity) {
|
|
60
|
+
// Postgres `jsonb` columns come back as objects; sqlite/mysql as text.
|
|
61
|
+
let candidate = entity;
|
|
62
|
+
if (typeof entity === 'string') {
|
|
63
|
+
try {
|
|
64
|
+
candidate = JSON.parse(entity);
|
|
65
|
+
}
|
|
66
|
+
catch {
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
const parsed = recordRefSchema.safeParse(candidate);
|
|
71
|
+
if (!parsed.success)
|
|
72
|
+
return null;
|
|
73
|
+
const ref = parsed.data;
|
|
74
|
+
const arity = Object.keys(ref.pk).length;
|
|
75
|
+
if (arity <= 1)
|
|
76
|
+
return auditEntityKeyOf(ref);
|
|
77
|
+
let tuple;
|
|
78
|
+
try {
|
|
79
|
+
tuple = JSON.parse(ref.label);
|
|
80
|
+
}
|
|
81
|
+
catch {
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
84
|
+
if (!Array.isArray(tuple) || tuple.length !== arity)
|
|
85
|
+
return null;
|
|
86
|
+
return { entityTable: auditEntityKeyPart(ref.table), entityId: auditEntityKeyPart(ref.label) };
|
|
87
|
+
}
|
|
88
|
+
export async function up(db, c) {
|
|
89
|
+
await db.schema
|
|
90
|
+
.alterTable(metaTable('audit_log'))
|
|
91
|
+
.addColumn('entity_table', c.str(AUDIT_ENTITY_KEY_MAX))
|
|
92
|
+
.execute();
|
|
93
|
+
await db.schema
|
|
94
|
+
.alterTable(metaTable('audit_log'))
|
|
95
|
+
.addColumn('entity_id', c.str(AUDIT_ENTITY_KEY_MAX))
|
|
96
|
+
.execute();
|
|
97
|
+
// (entity_table, entity_id, created_at): the feed's exact access path —
|
|
98
|
+
// equality on both keys, ordered by recency. 2×200×4 utf8mb4 bytes + the
|
|
99
|
+
// timestamp stays under MySQL's 3072-byte InnoDB index cap.
|
|
100
|
+
await db.schema
|
|
101
|
+
.createIndex('idx_adminium_audit_log_entity_created')
|
|
102
|
+
.on(metaTable('audit_log'))
|
|
103
|
+
.columns(['entity_table', 'entity_id', 'created_at'])
|
|
104
|
+
.execute();
|
|
105
|
+
// Backfill: id-keyset batches over rows that carry a ref. Unparseable JSON
|
|
106
|
+
// stays NULL — a row this reader cannot interpret must not fail the wave.
|
|
107
|
+
const backfill = db;
|
|
108
|
+
const audit = metaTable('audit_log');
|
|
109
|
+
let after = '';
|
|
110
|
+
for (;;) {
|
|
111
|
+
const rows = await backfill
|
|
112
|
+
.selectFrom(audit)
|
|
113
|
+
.select(['id', 'entity'])
|
|
114
|
+
.where('entity', 'is not', null)
|
|
115
|
+
.where('id', '>', after)
|
|
116
|
+
.orderBy('id', 'asc')
|
|
117
|
+
.limit(BACKFILL_BATCH)
|
|
118
|
+
.execute();
|
|
119
|
+
if (rows.length === 0)
|
|
120
|
+
break;
|
|
121
|
+
for (const row of rows) {
|
|
122
|
+
const keys = keysOf(row.entity);
|
|
123
|
+
if (keys === null)
|
|
124
|
+
continue;
|
|
125
|
+
await backfill
|
|
126
|
+
.updateTable(audit)
|
|
127
|
+
.set({ entityTable: keys.entityTable, entityId: keys.entityId })
|
|
128
|
+
.where('id', '=', row.id)
|
|
129
|
+
.execute();
|
|
130
|
+
}
|
|
131
|
+
const last = rows[rows.length - 1];
|
|
132
|
+
if (last === undefined || rows.length < BACKFILL_BATCH)
|
|
133
|
+
break;
|
|
134
|
+
after = last.id;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
//# sourceMappingURL=0016_audit_entity.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"0016_audit_entity.js","sourceRoot":"","sources":["../../src/migrations/0016_audit_entity.ts"],"names":[],"mappings":"AAAA,yCAAyC;AACzC;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAKH,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AACzC,OAAO,EACL,oBAAoB,EACpB,gBAAgB,EAChB,kBAAkB,EAClB,eAAe,GAChB,MAAM,4BAA4B,CAAC;AAEpC,MAAM,cAAc,GAAG,GAAG,CAAC;AAW3B;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,SAAS,MAAM,CAAC,MAAe;IAC7B,uEAAuE;IACvE,IAAI,SAAS,GAAY,MAAM,CAAC;IAChC,IAAI,OAAO,MAAM,KAAK,QAAQ,EAAE,CAAC;QAC/B,IAAI,CAAC;YACH,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;QACjC,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;IACD,MAAM,MAAM,GAAG,eAAe,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC;IACpD,IAAI,CAAC,MAAM,CAAC,OAAO;QAAE,OAAO,IAAI,CAAC;IACjC,MAAM,GAAG,GAAG,MAAM,CAAC,IAAI,CAAC;IACxB,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,MAAM,CAAC;IACzC,IAAI,KAAK,IAAI,CAAC;QAAE,OAAO,gBAAgB,CAAC,GAAG,CAAC,CAAC;IAE7C,IAAI,KAAc,CAAC;IACnB,IAAI,CAAC;QACH,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;IAChC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;IACD,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,MAAM,KAAK,KAAK;QAAE,OAAO,IAAI,CAAC;IACjE,OAAO,EAAE,WAAW,EAAE,kBAAkB,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,QAAQ,EAAE,kBAAkB,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;AACjG,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,EAAE,CAAC,EAAmB,EAAE,CAAgB;IAC5D,MAAM,EAAE,CAAC,MAAM;SACZ,UAAU,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC;SAClC,SAAS,CAAC,cAAc,EAAE,CAAC,CAAC,GAAG,CAAC,oBAAoB,CAAC,CAAC;SACtD,OAAO,EAAE,CAAC;IACb,MAAM,EAAE,CAAC,MAAM;SACZ,UAAU,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC;SAClC,SAAS,CAAC,WAAW,EAAE,CAAC,CAAC,GAAG,CAAC,oBAAoB,CAAC,CAAC;SACnD,OAAO,EAAE,CAAC;IACb,wEAAwE;IACxE,yEAAyE;IACzE,4DAA4D;IAC5D,MAAM,EAAE,CAAC,MAAM;SACZ,WAAW,CAAC,uCAAuC,CAAC;SACpD,EAAE,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC;SAC1B,OAAO,CAAC,CAAC,cAAc,EAAE,WAAW,EAAE,YAAY,CAAC,CAAC;SACpD,OAAO,EAAE,CAAC;IAEb,2EAA2E;IAC3E,0EAA0E;IAC1E,MAAM,QAAQ,GAAG,EAA2B,CAAC;IAC7C,MAAM,KAAK,GAAG,SAAS,CAAC,WAAW,CAAC,CAAC;IACrC,IAAI,KAAK,GAAG,EAAE,CAAC;IACf,SAAS,CAAC;QACR,MAAM,IAAI,GAAG,MAAM,QAAQ;aACxB,UAAU,CAAC,KAAK,CAAC;aACjB,MAAM,CAAC,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;aACxB,KAAK,CAAC,QAAQ,EAAE,QAAQ,EAAE,IAAI,CAAC;aAC/B,KAAK,CAAC,IAAI,EAAE,GAAG,EAAE,KAAK,CAAC;aACvB,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC;aACpB,KAAK,CAAC,cAAc,CAAC;aACrB,OAAO,EAAE,CAAC;QACb,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;YAAE,MAAM;QAC7B,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;YACvB,MAAM,IAAI,GAAG,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;YAChC,IAAI,IAAI,KAAK,IAAI;gBAAE,SAAS;YAC5B,MAAM,QAAQ;iBACX,WAAW,CAAC,KAAK,CAAC;iBAClB,GAAG,CAAC,EAAE,WAAW,EAAE,IAAI,CAAC,WAAW,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,CAAC;iBAC/D,KAAK,CAAC,IAAI,EAAE,GAAG,EAAE,GAAG,CAAC,EAAE,CAAC;iBACxB,OAAO,EAAE,CAAC;QACf,CAAC;QACD,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;QACnC,IAAI,IAAI,KAAK,SAAS,IAAI,IAAI,CAAC,MAAM,GAAG,cAAc;YAAE,MAAM;QAC9D,KAAK,GAAG,IAAI,CAAC,EAAE,CAAC;IAClB,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wave 0017 — key ↔ app-surface binding (29-app-surfaces.md D10, 29-T15).
|
|
3
|
+
*
|
|
4
|
+
* A hosted CUSTOMER surface is configured at serve time, not bake time: the
|
|
5
|
+
* surfaces plugin answers `GET /apps/<key>/customer/surface-config.json` with
|
|
6
|
+
* the newest live publishable key bound to that app, so rotating a key is
|
|
7
|
+
* Studio + reload rather than a rebuild. "Bound to that app" needs a COLUMN —
|
|
8
|
+
* before this wave, "minted per side per app" was a naming convention on
|
|
9
|
+
* `adminium_public_keys.name`, which nothing could query.
|
|
10
|
+
*
|
|
11
|
+
* `app_key` is the manifest key of the owning app (`clients`, `clinic`, …),
|
|
12
|
+
* nullable because binding is optional: a key minted for a standalone build
|
|
13
|
+
* or a third-party integration belongs to no hosted surface and stays NULL.
|
|
14
|
+
*
|
|
15
|
+
* Planned as `0016_surface_binding` (29 D10); renumbered to 0017 because
|
|
16
|
+
* plan 30's `0016_audit_entity` shipped first and the list is append-only.
|
|
17
|
+
*/
|
|
18
|
+
import type { Kysely } from 'kysely';
|
|
19
|
+
import type { ColumnHelpers } from '../columns.js';
|
|
20
|
+
/** Matches the discovery layout: a surface directory name, e.g. `clients`. */
|
|
21
|
+
export declare const PUBLIC_KEY_APP_KEY_MAX = 64;
|
|
22
|
+
export declare function up(db: Kysely<unknown>, c: ColumnHelpers): Promise<void>;
|
|
23
|
+
//# sourceMappingURL=0017_surface_binding.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"0017_surface_binding.d.ts","sourceRoot":"","sources":["../../src/migrations/0017_surface_binding.ts"],"names":[],"mappings":"AACA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAErC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAGnD,8EAA8E;AAC9E,eAAO,MAAM,sBAAsB,KAAK,CAAC;AAEzC,wBAAsB,EAAE,CAAC,EAAE,EAAE,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAc7E"}
|