feat: @crudy/pgmorbac-fastify, MIT, its own repository
The Fastify layer over pgmorbac: permission checks, RLS session context, management routes, PostgREST data proxy, and the migration loader for the SQL from @crudy/pgmorbac. The SQL is no longer bundled here.
This commit is contained in:
+6
-3
@@ -243,7 +243,10 @@ export interface MorbacApi {
|
||||
revokeDelegation(token: string, orgIds: string[], id: string): Promise<null>;
|
||||
}
|
||||
|
||||
export function createMorbacApi(apiUrl: string): MorbacApi {
|
||||
// apiUrl may be a function so a browser caller resolves the origin per request
|
||||
// rather than when its module loads.
|
||||
export function createMorbacApi(apiUrl: string | (() => string)): MorbacApi {
|
||||
const base = typeof apiUrl === 'function' ? apiUrl : () => apiUrl;
|
||||
async function api(method: string, path: string, token: string, orgIds: string[], body?: unknown): Promise<unknown> {
|
||||
const headers: Record<string, string> = {
|
||||
Authorization: `Bearer ${token}`,
|
||||
@@ -254,7 +257,7 @@ export function createMorbacApi(apiUrl: string): MorbacApi {
|
||||
headers['X-Org-Ids'] = orgIds.join(',');
|
||||
}
|
||||
if (body !== undefined) headers['Content-Type'] = 'application/json';
|
||||
const res = await fetch(`${apiUrl}${path}`, {
|
||||
const res = await fetch(`${base()}${path}`, {
|
||||
method,
|
||||
headers,
|
||||
credentials: 'include',
|
||||
@@ -279,7 +282,7 @@ export function createMorbacApi(apiUrl: string): MorbacApi {
|
||||
const PERM_TTL_MS = 30_000;
|
||||
|
||||
async function fetchPermission(token: string, orgId: string, activity: string, view: string): Promise<boolean> {
|
||||
const res = await fetch(`${apiUrl}/me/permissions/check`, {
|
||||
const res = await fetch(`${base()}/me/permissions/check`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Authorization: `Bearer ${token}`,
|
||||
|
||||
+20
-32
@@ -1,40 +1,28 @@
|
||||
import { readFileSync, readdirSync, existsSync } from 'fs';
|
||||
import { fileURLToPath } from 'url';
|
||||
import { dirname, join } from 'path';
|
||||
|
||||
const sqlDir = join(dirname(fileURLToPath(import.meta.url)), '..', 'sql');
|
||||
import { extensionSql, upgradeScripts, version } from '@crudy/pgmorbac';
|
||||
|
||||
export interface MorbacMigration {
|
||||
filename: string;
|
||||
sql: string;
|
||||
}
|
||||
|
||||
// The morbac schema ships with this package as a PGXN-style extension script
|
||||
// (sql/pgmorbac--<version>.sql) plus upgrade scripts (sql/upgrades/). For the
|
||||
// crudy migration runner they are exposed as plain migrations: the base script
|
||||
// keeps the historical filename 0001_morbac.sql so schema_migrations rows on
|
||||
// existing databases stay valid, and upgrade scripts follow in version order
|
||||
// as 0001_morbac--<old>--<new>.sql.
|
||||
// The pgmorbac install and upgrade scripts (from @crudy/pgmorbac) as plain
|
||||
// migrations for a migration runner. Without CREATE EXTENSION the morbac schema is
|
||||
// created up front, and the installed version is recorded. The base keeps the
|
||||
// historical filename 0001_morbac.sql so schema_migrations rows on existing
|
||||
// databases stay valid; its bytes must not change for an unchanged extension
|
||||
// version, or boot re-runs it. Upgrades follow as 0001_morbac--<old>--<new>.sql.
|
||||
export function morbacMigrations(): MorbacMigration[] {
|
||||
const base = readdirSync(sqlDir)
|
||||
.filter(f => /^pgmorbac--\d+\.\d+\.\d+\.sql$/.test(f))
|
||||
.sort();
|
||||
if (base.length !== 1) {
|
||||
throw new Error(`[pgmorbac] Expected exactly one base script in ${sqlDir}, found ${base.length}`);
|
||||
}
|
||||
const migrations: MorbacMigration[] = [{
|
||||
filename: '0001_morbac.sql',
|
||||
sql: readFileSync(join(sqlDir, base[0]), 'utf8'),
|
||||
}];
|
||||
|
||||
const upgradesDir = join(sqlDir, 'upgrades');
|
||||
if (existsSync(upgradesDir)) {
|
||||
for (const f of readdirSync(upgradesDir).filter(n => n.endsWith('.sql')).sort()) {
|
||||
migrations.push({
|
||||
filename: `0001_morbac${f.replace(/^pgmorbac/, '').replace(/\.sql$/, '')}.sql`,
|
||||
sql: readFileSync(join(upgradesDir, f), 'utf8'),
|
||||
});
|
||||
}
|
||||
}
|
||||
return migrations;
|
||||
const lines = extensionSql().split('\n');
|
||||
lines.splice(lines.indexOf('') + 1, 0, 'CREATE SCHEMA IF NOT EXISTS morbac;');
|
||||
const sql = lines.join('\n') + `
|
||||
-- Record the installed schema version (readable via
|
||||
-- morbac.get_config('schema_version'); upgrade scripts bump it).
|
||||
INSERT INTO morbac.config (key, value, description)
|
||||
VALUES ('schema_version', '${version}', 'Installed pgmorbac schema version')
|
||||
ON CONFLICT (key) DO UPDATE SET value = EXCLUDED.value, updated_at = CURRENT_TIMESTAMP;
|
||||
`;
|
||||
return [
|
||||
{ filename: '0001_morbac.sql', sql },
|
||||
...upgradeScripts().map(u => ({ filename: `0001_morbac--${u.from}--${u.to}.sql`, sql: u.sql })),
|
||||
];
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user