UNPKG

@launchql/core

Version:
149 lines (146 loc) 4.59 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.LaunchQLInit = void 0; const logger_1 = require("@launchql/logger"); const fs_1 = require("fs"); const path_1 = require("path"); const pg_cache_1 = require("pg-cache"); const log = new logger_1.Logger('init'); class LaunchQLInit { pool; pgConfig; constructor(config) { this.pgConfig = config; this.pool = (0, pg_cache_1.getPgPool)(this.pgConfig); } /** * Bootstrap standard roles (anonymous, authenticated, administrator) */ async bootstrapRoles() { try { log.info('Bootstrapping LaunchQL roles...'); const sqlPath = (0, path_1.join)(__dirname, 'sql', 'bootstrap-roles.sql'); const sql = (0, fs_1.readFileSync)(sqlPath, 'utf-8'); await this.pool.query(sql); log.success('Successfully bootstrapped LaunchQL roles'); } catch (error) { log.error('Failed to bootstrap roles:', error); throw error; } } /** * Bootstrap test roles (roles only, no users) */ async bootstrapTestRoles() { try { log.warn('WARNING: This command creates test roles and should NEVER be run on a production database!'); log.info('Bootstrapping LaunchQL test roles...'); const sqlPath = (0, path_1.join)(__dirname, 'sql', 'bootstrap-test-roles.sql'); const sql = (0, fs_1.readFileSync)(sqlPath, 'utf-8'); await this.pool.query(sql); log.success('Successfully bootstrapped LaunchQL test roles'); } catch (error) { log.error('Failed to bootstrap test roles:', error); throw error; } } /** * Bootstrap database roles with custom username and password */ async bootstrapDbRoles(username, password) { try { log.info(`Bootstrapping LaunchQL database roles for user: ${username}...`); const sql = ` BEGIN; DO $do$ DECLARE v_username TEXT := '${username.replace(/'/g, "''")}'; v_password TEXT := '${password.replace(/'/g, "''")}'; BEGIN BEGIN EXECUTE format('CREATE ROLE %I LOGIN PASSWORD %L', v_username, v_password); EXCEPTION WHEN duplicate_object THEN -- Role already exists; optionally sync attributes here with ALTER ROLE NULL; END; END $do$; -- Robust GRANTs under concurrency: GRANT can race on pg_auth_members unique index. -- Catch unique_violation (23505) and continue so CI/CD concurrent jobs don't fail. DO $do$ DECLARE v_username TEXT := '${username.replace(/'/g, "''")}'; BEGIN BEGIN EXECUTE format('GRANT %I TO %I', 'anonymous', v_username); EXCEPTION WHEN unique_violation THEN -- Membership was granted concurrently; ignore. NULL; WHEN undefined_object THEN -- One of the roles doesn't exist yet; order operations as needed. RAISE NOTICE 'Missing role when granting % to %', 'anonymous', v_username; END; BEGIN EXECUTE format('GRANT %I TO %I', 'authenticated', v_username); EXCEPTION WHEN unique_violation THEN -- Membership was granted concurrently; ignore. NULL; WHEN undefined_object THEN RAISE NOTICE 'Missing role when granting % to %', 'authenticated', v_username; END; END $do$; COMMIT; `; await this.pool.query(sql); log.success(`Successfully bootstrapped LaunchQL database roles for user: ${username}`); } catch (error) { log.error(`Failed to bootstrap database roles for user ${username}:`, error); throw error; } } /** * Remove database roles and revoke grants */ async removeDbRoles(username) { try { log.info(`Removing LaunchQL database roles for user: ${username}...`); const sql = ` BEGIN; DO $do$ BEGIN IF EXISTS ( SELECT 1 FROM pg_catalog.pg_roles WHERE rolname = '${username}') THEN REVOKE anonymous FROM ${username}; REVOKE authenticated FROM ${username}; DROP ROLE ${username}; END IF; END $do$; COMMIT; `; await this.pool.query(sql); log.success(`Successfully removed LaunchQL database roles for user: ${username}`); } catch (error) { log.error(`Failed to remove database roles for user ${username}:`, error); throw error; } } /** * Close the database connection */ async close() { } } exports.LaunchQLInit = LaunchQLInit;