UNPKG

everything-dev

Version:

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

176 lines (174 loc) • 7.28 kB
import { frameworkOwnedHeaderComment } from "./scaffold/ownership.mjs"; import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs"; import { dirname, join } from "node:path"; import { randomBytes } from "node:crypto"; //#region src/services/infra.ts const POSTGRES_USER = "everythingdev"; const POSTGRES_PASSWORD = "everythingdev"; const AUTH_DATABASE_SECRET = "AUTH_DATABASE_URL"; 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"]); /** * Fixed local-development database conventions. The committed * docker-compose.yml provisions exactly these services/ports — nothing is * derived from runtime config. Every non-auth `*_DATABASE_URL` secret shares * the api database (schema-isolated per plugin); auth gets its own. */ const DEV_API_DB_PORT = 5432; const DEV_AUTH_DB_PORT = 5433; const TEST_API_DB_PORT = 5434; const TEST_AUTH_DB_PORT = 5435; 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 = join(configDir, ".bos", "infra-state.json"); if (!existsSync(statePath)) return {}; try { return { devPorts: JSON.parse(readFileSync(statePath, "utf-8")).devPorts }; } catch { return {}; } } function savePortState(configDir, state) { const statePath = join(configDir, ".bos", "infra-state.json"); mkdirSync(dirname(statePath), { recursive: true }); 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(port, databaseName) { return `postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@localhost:${port}/${databaseName}`; } function buildConventionalDatabases(secrets) { return uniqueSecrets(secrets.filter((secret) => secret.endsWith("_DATABASE_URL"))).sort((a, b) => normalizeDatabaseSlug(a).localeCompare(normalizeDatabaseSlug(b))).map((secret) => { const slug = normalizeDatabaseSlug(secret); const isAuth = secret === AUTH_DATABASE_SECRET; const port = isAuth ? DEV_AUTH_DB_PORT : DEV_API_DB_PORT; const databaseName = isAuth ? "auth_db" : "api_db"; return { secret, slug, port, databaseName, url: databaseUrl(port, databaseName) }; }); } 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(secret) { const isAuth = secret === AUTH_DATABASE_SECRET; return databaseUrl(isAuth ? TEST_AUTH_DB_PORT : TEST_API_DB_PORT, isAuth ? "auth_test_db" : "api_test_db"); } 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 ? "" : 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 ? [ ...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 to run dev on ephemeral pglite; set a postgres:// URL to opt into compose Postgres."); 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 dedicated *-test docker-compose services", "" ]; 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(secret)}`); if (entries.length > 0) { lines.push(`# ${group.section}`); lines.push(...entries); lines.push(""); } } return `${lines.join("\n")}\n`; } //#endregion export { buildConventionalDatabases, buildConventionalRedis, getSecretGroups, loadPortState, normalizeDatabaseSlug, renderEnvFile, renderEnvTestFile, savePortState }; //# sourceMappingURL=infra.mjs.map