UNPKG

everything-dev

Version:

A consolidated product package for building Module Federation apps with oRPC APIs.

176 lines (174 loc) • 7.31 kB
const require_ownership = require("./scaffold/ownership.cjs"); const require_defaults = require("../db/defaults.cjs"); const require_binding = require("../db/binding.cjs"); let node_fs = require("node:fs"); let node_path = require("node:path"); let node_crypto = require("node:crypto"); //#region src/services/infra.ts const HOST_SECRET = "CORS_ORIGIN"; const REDIS_PORT = 6379; const TEST_AUTH_SECRET = "regression-test-secret-do-not-use-in-production"; const VESTIGIAL_SECRETS = /* @__PURE__ */ new Set(["NEAR_RELAYER_PRIVATE_KEY_MAINNET", "NEAR_RELAYER_PRIVATE_KEY_TESTNET"]); /** * Local development conventions. Databases default to a persistent, * file-backed PGlite data dir per workspace process (`.bos/db`) — no ports, * no docker. Operators point a secret at a `postgres://` URL to demand a * shared server. The two-database split (auth separate from everything * else) survives independent of compose. */ const PGLITE_DEV_DATABASE_URL = require_binding.PGLITE_DEFAULT_URL; const PGLITE_TEST_DATABASE_URL = require_defaults.pgliteTestUrl; function uniqueSecrets(values) { const secrets = []; const seen = /* @__PURE__ */ new Set(); for (const value of values) { if (!value || seen.has(value)) continue; seen.add(value); secrets.push(value); } return secrets; } function loadPortState(configDir) { if (!configDir) return {}; const statePath = (0, node_path.join)(configDir, ".bos", "infra-state.json"); if (!(0, node_fs.existsSync)(statePath)) return {}; try { return { devPorts: JSON.parse((0, node_fs.readFileSync)(statePath, "utf-8")).devPorts }; } catch { return {}; } } function savePortState(configDir, state) { const statePath = (0, node_path.join)(configDir, ".bos", "infra-state.json"); (0, node_fs.mkdirSync)((0, node_path.dirname)(statePath), { recursive: true }); (0, node_fs.writeFileSync)(statePath, `${JSON.stringify(state, null, 2)}\n`); } function normalizeDatabaseSlug(secret) { return secret.replace(/_DATABASE_URL$/, "").toLowerCase(); } function normalizeRedisSlug(secret) { return secret.replace(/_REDIS_URL$/, "").toLowerCase(); } function databaseUrl() { return PGLITE_DEV_DATABASE_URL; } function buildConventionalDatabases(secrets) { return uniqueSecrets(secrets.filter((secret) => secret.endsWith("_DATABASE_URL"))).sort((a, b) => normalizeDatabaseSlug(a).localeCompare(normalizeDatabaseSlug(b))).map((secret) => { return { secret, slug: normalizeDatabaseSlug(secret), url: databaseUrl() }; }); } function buildConventionalRedis(secrets) { return uniqueSecrets(secrets.filter((secret) => secret.endsWith("_REDIS_URL"))).sort((a, b) => normalizeRedisSlug(a).localeCompare(normalizeRedisSlug(b))).map((secret) => ({ secret, slug: normalizeRedisSlug(secret), port: REDIS_PORT, url: `redis://localhost:${REDIS_PORT}` })); } function testDatabaseUrl() { return PGLITE_TEST_DATABASE_URL; } function getSecretGroups(runtimeConfig) { const groups = []; const seen = /* @__PURE__ */ new Set(); const addGroup = (section, secrets) => { const filtered = secrets.filter((s) => { if (VESTIGIAL_SECRETS.has(s)) return false; if (seen.has(s)) return false; seen.add(s); return true; }); if (filtered.length > 0) groups.push({ section, secrets: filtered }); }; addGroup("app.host", uniqueSecrets([...runtimeConfig.host.secrets ?? [], HOST_SECRET])); addGroup("app.api", uniqueSecrets(runtimeConfig.api.secrets ?? [])); if (runtimeConfig.auth) addGroup("app.auth", uniqueSecrets(runtimeConfig.auth.secrets ?? [])); if (runtimeConfig.plugins) { for (const [pluginKey, plugin] of Object.entries(runtimeConfig.plugins)) if (plugin.secrets && plugin.secrets.length > 0) addGroup(`plugins.${pluginKey}`, plugin.secrets); } return groups; } function defaultSecretValue(secret, databases, redisConfigs, options) { if (secret === "BETTER_AUTH_SECRET") return options.forExample ? "" : (0, node_crypto.randomBytes)(32).toString("base64url"); if (secret === "CORS_ORIGIN") { if (options.forExample) return "http://localhost:3000"; if (typeof options.devHostPort === "number") return `http://localhost:${options.devHostPort}`; return "http://localhost:3000"; } if (options.forExample) return ""; return databases.get(secret)?.url ?? redisConfigs.get(secret)?.url ?? ""; } /** * Plugin `*_DATABASE_URL` secrets share the API database (the platform's * two-database contract), so they are not rendered as template entries — * the host falls back to `API_DATABASE_URL` when they are unset. */ function isFallbackDatabaseSecret(secret) { return secret.endsWith("_DATABASE_URL") && secret !== "API_DATABASE_URL" && secret !== "AUTH_DATABASE_URL"; } function renderEnvFile(groups, options) { const rendered = groups.map((group) => ({ section: group.section, secrets: group.secrets.filter((secret) => !isFallbackDatabaseSecret(secret)) })).filter((group) => group.secrets.length > 0); const allSecrets = rendered.flatMap((group) => group.secrets); const databaseMap = new Map(buildConventionalDatabases(allSecrets).map((entry) => [entry.secret, entry])); const redisMap = new Map(buildConventionalRedis(allSecrets).map((entry) => [entry.secret, entry])); const lines = options.forExample ? [ ...require_ownership.frameworkOwnedHeaderComment("hash").trimEnd().split("\n"), "", "# Generated from configured bos secrets", "# Update values as needed for your local environment", "" ] : [ "# Generated from configured bos secrets", "# Update values as needed for your local environment", "" ]; for (const group of rendered) { lines.push(`# ${group.section}`); for (const secret of group.secrets) { if (options.forExample && (secret.endsWith("_DATABASE_URL") || secret.endsWith("_REDIS_URL"))) lines.push(secret.endsWith("_REDIS_URL") ? "# Leave empty for dev defaults; set a redis:// URL to opt into a real Redis." : "# Leave empty for the persistent local PGlite default (.bos/db); set a postgres:// URL for production or shared access."); lines.push(`${secret}=${defaultSecretValue(secret, databaseMap, redisMap, options)}`); } lines.push(""); } return `${lines.join("\n")}\n`; } function renderEnvTestFile(groups) { const lines = [ "# Generated test environment — loaded by test suites instead of .env", "# Test databases run on in-memory pglite — no docker involved", "" ]; for (const group of groups) { const entries = []; for (const secret of group.secrets) if (secret === "BETTER_AUTH_SECRET") entries.push(`${secret}=${TEST_AUTH_SECRET}`); else if (secret.endsWith("_DATABASE_URL")) entries.push(`${secret}=${testDatabaseUrl()}`); if (entries.length > 0) { lines.push(`# ${group.section}`); lines.push(...entries); lines.push(""); } } return `${lines.join("\n")}\n`; } //#endregion exports.PGLITE_DEV_DATABASE_URL = PGLITE_DEV_DATABASE_URL; exports.PGLITE_TEST_DATABASE_URL = PGLITE_TEST_DATABASE_URL; exports.buildConventionalDatabases = buildConventionalDatabases; exports.buildConventionalRedis = buildConventionalRedis; exports.getSecretGroups = getSecretGroups; exports.loadPortState = loadPortState; exports.normalizeDatabaseSlug = normalizeDatabaseSlug; exports.renderEnvFile = renderEnvFile; exports.renderEnvTestFile = renderEnvTestFile; exports.savePortState = savePortState; //# sourceMappingURL=infra.cjs.map