@kontrolia/db 2.1.4 → 2.1.5
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/migrate.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"migrate.d.ts","sourceRoot":"","sources":["../src/migrate.ts"],"names":[],"mappings":"AAQA,MAAM,WAAW,cAAc;IAC7B;;sDAEkD;IAClD,gBAAgB,EAAE,MAAM,CAAC;IACzB,8CAA8C;IAC9C,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;
|
|
1
|
+
{"version":3,"file":"migrate.d.ts","sourceRoot":"","sources":["../src/migrate.ts"],"names":[],"mappings":"AAQA,MAAM,WAAW,cAAc;IAC7B;;sDAEkD;IAClD,gBAAgB,EAAE,MAAM,CAAC;IACzB,8CAA8C;IAC9C,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAsCD;;;GAGG;AACH,wBAAsB,OAAO,CAAC,EAAE,gBAAgB,EAAE,OAAe,EAAE,EAAE,cAAc,iBAgClF"}
|
package/dist/migrate.js
CHANGED
|
@@ -4,13 +4,34 @@ import { fileURLToPath } from "node:url";
|
|
|
4
4
|
import { Client } from "pg";
|
|
5
5
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
6
6
|
const MIGRATIONS_DIR = join(__dirname, "..", "migrations");
|
|
7
|
+
/**
|
|
8
|
+
* Bootstraps (and self-heals the location of) the migration-tracking
|
|
9
|
+
* table, then returns its schema-qualified name for the rest of migrate()
|
|
10
|
+
* to use. On a genuinely fresh database neither `kontrolia` nor
|
|
11
|
+
* `kontrolia_auth` exists yet — migration 0001 is what creates the former,
|
|
12
|
+
* 0020 renames it to the latter — so this table has nowhere but `public`
|
|
13
|
+
* to start out in. Once `kontrolia_auth` exists (every subsequent run,
|
|
14
|
+
* fresh or not), it belongs there instead: an install that ran migrate()
|
|
15
|
+
* before this fix shipped may still have it sitting in `public`, so this
|
|
16
|
+
* relocates it once, idempotently, rather than leaving two copies around.
|
|
17
|
+
*/
|
|
7
18
|
async function ensureMigrationsTable(client) {
|
|
19
|
+
const { rows: schemaRows } = await client.query(`select exists (select 1 from information_schema.schemata where schema_name = 'kontrolia_auth') as exists`);
|
|
20
|
+
const kontroliaAuthExists = schemaRows[0]?.exists ?? false;
|
|
21
|
+
if (kontroliaAuthExists) {
|
|
22
|
+
const { rows: publicRows } = await client.query(`select exists (select 1 from information_schema.tables where table_schema = 'public' and table_name = 'kontrolia_migrations') as exists`);
|
|
23
|
+
if (publicRows[0]?.exists) {
|
|
24
|
+
await client.query("alter table public.kontrolia_migrations set schema kontrolia_auth");
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
const schema = kontroliaAuthExists ? "kontrolia_auth" : "public";
|
|
8
28
|
await client.query(`
|
|
9
|
-
create table if not exists kontrolia_migrations (
|
|
29
|
+
create table if not exists ${schema}.kontrolia_migrations (
|
|
10
30
|
filename text primary key,
|
|
11
31
|
applied_at timestamptz not null default now()
|
|
12
32
|
)
|
|
13
33
|
`);
|
|
34
|
+
return schema;
|
|
14
35
|
}
|
|
15
36
|
/**
|
|
16
37
|
* Applies every .sql file under migrations/ that hasn't run yet, in
|
|
@@ -20,8 +41,8 @@ export async function migrate({ connectionString, verbose = false }) {
|
|
|
20
41
|
const client = new Client({ connectionString });
|
|
21
42
|
await client.connect();
|
|
22
43
|
try {
|
|
23
|
-
await ensureMigrationsTable(client)
|
|
24
|
-
const { rows: applied } = await client.query(
|
|
44
|
+
const migrationsTable = `${await ensureMigrationsTable(client)}.kontrolia_migrations`;
|
|
45
|
+
const { rows: applied } = await client.query(`select filename from ${migrationsTable}`);
|
|
25
46
|
const appliedSet = new Set(applied.map((r) => r.filename));
|
|
26
47
|
const files = (await readdir(MIGRATIONS_DIR)).filter((f) => f.endsWith(".sql")).sort();
|
|
27
48
|
for (const file of files) {
|
|
@@ -33,7 +54,7 @@ export async function migrate({ connectionString, verbose = false }) {
|
|
|
33
54
|
await client.query("begin");
|
|
34
55
|
try {
|
|
35
56
|
await client.query(sql);
|
|
36
|
-
await client.query(
|
|
57
|
+
await client.query(`insert into ${migrationsTable} (filename) values ($1)`, [file]);
|
|
37
58
|
await client.query("commit");
|
|
38
59
|
}
|
|
39
60
|
catch (error) {
|
|
@@ -14,14 +14,6 @@ export interface RegisterApplicationOptions {
|
|
|
14
14
|
export interface RegisteredApplication {
|
|
15
15
|
applicationId: string;
|
|
16
16
|
permissionKeys: string[];
|
|
17
|
-
/**
|
|
18
|
-
* The plaintext sync API key (see POST /api/applications/sync on
|
|
19
|
-
* auth-server) — only present the first time this slug is registered.
|
|
20
|
-
* Only the hash is stored; there is no way to recover it later, so the
|
|
21
|
-
* caller must surface it to the operator immediately. `null` means the
|
|
22
|
-
* application already existed and its key (if any) was left untouched.
|
|
23
|
-
*/
|
|
24
|
-
apiKey: string | null;
|
|
25
17
|
}
|
|
26
18
|
/**
|
|
27
19
|
* Inserts an application and its permission catalog directly against
|
|
@@ -30,7 +22,15 @@ export interface RegisteredApplication {
|
|
|
30
22
|
* go through a platform-admin path), so this is that path: a direct,
|
|
31
23
|
* service-role-equivalent write, the same way migrate() bypasses RLS to
|
|
32
24
|
* apply schema changes. Safe to re-run — the slug and permission key are
|
|
33
|
-
* both upserted
|
|
25
|
+
* both upserted.
|
|
26
|
+
*
|
|
27
|
+
* Does NOT generate a sync API key — migration 0040 moved keys off
|
|
28
|
+
* `applications` onto the org-scoped `application_api_keys` table, and at
|
|
29
|
+
* the point this runs (typically during install, before any organization
|
|
30
|
+
* necessarily exists or has enabled the app) there's no organization to
|
|
31
|
+
* scope one to. Generate the first key afterward from admin-panel
|
|
32
|
+
* (Aplicaciones → tu app → API Keys), once at least one organization has
|
|
33
|
+
* enabled the application.
|
|
34
34
|
*/
|
|
35
35
|
export declare function registerApplication(options: RegisterApplicationOptions): Promise<RegisteredApplication>;
|
|
36
36
|
//# sourceMappingURL=register-application.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"register-application.d.ts","sourceRoot":"","sources":["../src/register-application.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"register-application.d.ts","sourceRoot":"","sources":["../src/register-application.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,0BAA0B;IACzC,4DAA4D;IAC5D,gBAAgB,EAAE,MAAM,CAAC;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,aAAa,GAAG,SAAS,GAAG,YAAY,CAAC;IACtD,WAAW,EAAE,eAAe,EAAE,CAAC;CAChC;AAED,MAAM,WAAW,qBAAqB;IACpC,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,MAAM,EAAE,CAAC;CAC1B;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAsB,mBAAmB,CAAC,OAAO,EAAE,0BAA0B,GAAG,OAAO,CAAC,qBAAqB,CAAC,CAgC7G"}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { Client } from "pg";
|
|
2
|
-
import { generateApplicationApiKey, hashApplicationApiKey } from "./api-key.js";
|
|
3
2
|
/**
|
|
4
3
|
* Inserts an application and its permission catalog directly against
|
|
5
4
|
* Postgres. kontrolia_auth.applications/permissions have no insert policy for
|
|
@@ -7,17 +6,24 @@ import { generateApplicationApiKey, hashApplicationApiKey } from "./api-key.js";
|
|
|
7
6
|
* go through a platform-admin path), so this is that path: a direct,
|
|
8
7
|
* service-role-equivalent write, the same way migrate() bypasses RLS to
|
|
9
8
|
* apply schema changes. Safe to re-run — the slug and permission key are
|
|
10
|
-
* both upserted
|
|
9
|
+
* both upserted.
|
|
10
|
+
*
|
|
11
|
+
* Does NOT generate a sync API key — migration 0040 moved keys off
|
|
12
|
+
* `applications` onto the org-scoped `application_api_keys` table, and at
|
|
13
|
+
* the point this runs (typically during install, before any organization
|
|
14
|
+
* necessarily exists or has enabled the app) there's no organization to
|
|
15
|
+
* scope one to. Generate the first key afterward from admin-panel
|
|
16
|
+
* (Aplicaciones → tu app → API Keys), once at least one organization has
|
|
17
|
+
* enabled the application.
|
|
11
18
|
*/
|
|
12
19
|
export async function registerApplication(options) {
|
|
13
20
|
const client = new Client({ connectionString: options.connectionString });
|
|
14
21
|
await client.connect();
|
|
15
22
|
try {
|
|
16
|
-
const
|
|
17
|
-
|
|
18
|
-
values ($1, $2, $3, $4)
|
|
23
|
+
const { rows: [application], } = await client.query(`insert into kontrolia_auth.applications (name, slug, environment)
|
|
24
|
+
values ($1, $2, $3)
|
|
19
25
|
on conflict (slug) do update set name = excluded.name, environment = excluded.environment
|
|
20
|
-
returning id
|
|
26
|
+
returning id`, [options.name, options.slug, options.environment]);
|
|
21
27
|
if (!application)
|
|
22
28
|
throw new Error(`Failed to upsert application "${options.slug}"`);
|
|
23
29
|
const permissionKeys = [];
|
|
@@ -28,7 +34,7 @@ export async function registerApplication(options) {
|
|
|
28
34
|
on conflict (key) do update set description = excluded.description`, [application.id, permission.resource, permission.action, key, permission.description ?? null]);
|
|
29
35
|
permissionKeys.push(key);
|
|
30
36
|
}
|
|
31
|
-
return { applicationId: application.id, permissionKeys
|
|
37
|
+
return { applicationId: application.id, permissionKeys };
|
|
32
38
|
}
|
|
33
39
|
finally {
|
|
34
40
|
await client.end();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kontrolia/db",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.5",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "KontrolIA Auth database layer: SQL migrations for the `kontrolia` schema (organizations, RBAC, Custom Access Token Hook) and a connection-string-agnostic migration runner.",
|
|
6
6
|
"keywords": [
|