UNPKG

everything-dev

Version:

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

1 lines • 18.5 kB
{"version":3,"file":"infra.cjs","names":["join","existsSync","readFileSync","dirname","randomBytes"],"sources":["../../src/cli/infra.ts"],"sourcesContent":["import { randomBytes } from \"node:crypto\";\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport type { RuntimeConfig } from \"../types\";\n\nconst POSTGRES_USER = \"everythingdev\";\nconst POSTGRES_PASSWORD = \"everythingdev\";\nconst AUTH_DATABASE_SECRET = \"AUTH_DATABASE_URL\";\nconst HOST_SECRET = \"CORS_ORIGIN\";\nconst REDIS_PORT = 6379;\nconst TEST_AUTH_SECRET = \"regression-test-secret-do-not-use-in-production\";\nconst VESTIGIAL_SECRETS = new Set([\n \"NEAR_RELAYER_PRIVATE_KEY_MAINNET\",\n \"NEAR_RELAYER_PRIVATE_KEY_TESTNET\",\n]);\n\n/**\n * Fixed local-development database conventions. The committed\n * docker-compose.yml provisions exactly these services/ports — nothing is\n * derived from runtime config. Every non-auth `*_DATABASE_URL` secret shares\n * the api database (schema-isolated per plugin); auth gets its own.\n */\nconst DEV_API_DB_PORT = 5432;\nconst DEV_AUTH_DB_PORT = 5433;\nconst TEST_API_DB_PORT = 5434;\nconst TEST_AUTH_DB_PORT = 5435;\n\nexport interface DatabaseConvention {\n secret: string;\n slug: string;\n port: number;\n databaseName: string;\n url: string;\n}\n\nexport interface RedisConvention {\n secret: string;\n slug: string;\n port: number;\n url: string;\n}\n\nexport interface SecretGroup {\n section: string;\n secrets: string[];\n}\n\ninterface DevPortState {\n host?: number;\n api?: number;\n ui?: number;\n auth?: number;\n pluginPortStart?: number;\n}\n\nexport type { DevPortState };\n\nexport interface PortState {\n devPorts?: DevPortState;\n}\n\nfunction uniqueSecrets(values: Array<string | undefined>): string[] {\n const secrets: string[] = [];\n const seen = new Set<string>();\n\n for (const value of values) {\n if (!value || seen.has(value)) continue;\n seen.add(value);\n secrets.push(value);\n }\n\n return secrets;\n}\n\nexport function loadPortState(configDir?: string): PortState {\n if (!configDir) return {};\n const statePath = join(configDir, \".bos\", \"infra-state.json\");\n if (!existsSync(statePath)) return {};\n try {\n const raw = JSON.parse(readFileSync(statePath, \"utf-8\")) as Partial<PortState>;\n return { devPorts: raw.devPorts };\n } catch {\n return {};\n }\n}\n\nexport function savePortState(configDir: string, state: PortState): void {\n const statePath = join(configDir, \".bos\", \"infra-state.json\");\n mkdirSync(dirname(statePath), { recursive: true });\n writeFileSync(statePath, `${JSON.stringify(state, null, 2)}\\n`);\n}\n\nexport function normalizeDatabaseSlug(secret: string): string {\n return secret.replace(/_DATABASE_URL$/, \"\").toLowerCase();\n}\n\nfunction normalizeRedisSlug(secret: string): string {\n return secret.replace(/_REDIS_URL$/, \"\").toLowerCase();\n}\n\nfunction databaseUrl(port: number, databaseName: string): string {\n return `postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@localhost:${port}/${databaseName}`;\n}\n\nexport function buildConventionalDatabases(secrets: string[]): DatabaseConvention[] {\n const databaseSecrets = uniqueSecrets(\n secrets.filter((secret) => secret.endsWith(\"_DATABASE_URL\")),\n );\n\n return databaseSecrets\n .sort((a, b) => normalizeDatabaseSlug(a).localeCompare(normalizeDatabaseSlug(b)))\n .map((secret) => {\n const slug = normalizeDatabaseSlug(secret);\n const isAuth = secret === AUTH_DATABASE_SECRET;\n const port = isAuth ? DEV_AUTH_DB_PORT : DEV_API_DB_PORT;\n const databaseName = isAuth ? \"auth_db\" : \"api_db\";\n return { secret, slug, port, databaseName, url: databaseUrl(port, databaseName) };\n });\n}\n\nexport function buildConventionalRedis(secrets: string[]): RedisConvention[] {\n const redisSecrets = uniqueSecrets(secrets.filter((secret) => secret.endsWith(\"_REDIS_URL\")));\n\n return redisSecrets\n .sort((a, b) => normalizeRedisSlug(a).localeCompare(normalizeRedisSlug(b)))\n .map((secret) => ({\n secret,\n slug: normalizeRedisSlug(secret),\n port: REDIS_PORT,\n url: `redis://localhost:${REDIS_PORT}`,\n }));\n}\n\nfunction testDatabaseUrl(secret: string): string {\n const isAuth = secret === AUTH_DATABASE_SECRET;\n const port = isAuth ? TEST_AUTH_DB_PORT : TEST_API_DB_PORT;\n const databaseName = isAuth ? \"auth_test_db\" : \"api_test_db\";\n return databaseUrl(port, databaseName);\n}\n\nexport function getSecretGroups(runtimeConfig: RuntimeConfig): SecretGroup[] {\n const groups: SecretGroup[] = [];\n const seen = new Set<string>();\n\n const addGroup = (section: string, secrets: string[]) => {\n const filtered = secrets.filter((s) => {\n if (VESTIGIAL_SECRETS.has(s)) return false;\n if (seen.has(s)) return false;\n seen.add(s);\n return true;\n });\n if (filtered.length > 0) {\n groups.push({ section, secrets: filtered });\n }\n };\n\n addGroup(\"app.host\", uniqueSecrets([...(runtimeConfig.host.secrets ?? []), HOST_SECRET]));\n\n addGroup(\"app.api\", uniqueSecrets(runtimeConfig.api.secrets ?? []));\n\n if (runtimeConfig.auth) {\n addGroup(\"app.auth\", uniqueSecrets(runtimeConfig.auth.secrets ?? []));\n }\n\n if (runtimeConfig.plugins) {\n for (const [pluginKey, plugin] of Object.entries(runtimeConfig.plugins)) {\n if (plugin.secrets && plugin.secrets.length > 0) {\n addGroup(`plugins.${pluginKey}`, plugin.secrets);\n }\n }\n }\n\n return groups;\n}\n\nfunction extractPortFromUrl(url: string): string | null {\n const match = url.match(/:(\\d{4,5})(?:\\/|$)/);\n return match?.[1] ?? null;\n}\n\nfunction resolveDevHostPort(runtimeConfig: RuntimeConfig): number {\n if (typeof runtimeConfig.host?.port === \"number\") return runtimeConfig.host.port;\n const fromUrl = runtimeConfig.host?.url ? extractPortFromUrl(runtimeConfig.host.url) : null;\n if (fromUrl) {\n const parsed = Number.parseInt(fromUrl, 10);\n if (!Number.isNaN(parsed)) return parsed;\n }\n return 3000;\n}\n\nexport { resolveDevHostPort };\n\nfunction defaultSecretValue(\n secret: string,\n databases: Map<string, DatabaseConvention>,\n redisConfigs: Map<string, RedisConvention>,\n options: { forExample: boolean; devHostPort?: number },\n): string {\n if (secret === \"BETTER_AUTH_SECRET\") {\n return options.forExample ? \"\" : randomBytes(32).toString(\"base64url\");\n }\n\n if (secret === \"CORS_ORIGIN\") {\n if (options.forExample) {\n return \"http://localhost:3000\";\n }\n if (typeof options.devHostPort === \"number\") {\n return `http://localhost:${options.devHostPort}`;\n }\n return \"http://localhost:3000\";\n }\n\n return databases.get(secret)?.url ?? redisConfigs.get(secret)?.url ?? \"\";\n}\n\n/**\n * Plugin `*_DATABASE_URL` secrets share the API database (the platform's\n * two-database contract), so they are not rendered as template entries —\n * the host falls back to `API_DATABASE_URL` when they are unset.\n */\nfunction isFallbackDatabaseSecret(secret: string): boolean {\n return (\n secret.endsWith(\"_DATABASE_URL\") &&\n secret !== \"API_DATABASE_URL\" &&\n secret !== \"AUTH_DATABASE_URL\"\n );\n}\n\nfunction renderEnvFile(\n groups: SecretGroup[],\n options: { forExample: boolean; devHostPort?: number },\n): string {\n const rendered = groups\n .map((group) => ({\n section: group.section,\n secrets: group.secrets.filter((secret) => !isFallbackDatabaseSecret(secret)),\n }))\n .filter((group) => group.secrets.length > 0);\n const allSecrets = rendered.flatMap((group) => group.secrets);\n const databaseMap = new Map(\n buildConventionalDatabases(allSecrets).map((entry) => [entry.secret, entry]),\n );\n const redisMap = new Map(\n buildConventionalRedis(allSecrets).map((entry) => [entry.secret, entry]),\n );\n const lines: string[] = [\n \"# Generated from configured bos secrets\",\n \"# Update values as needed for your local environment\",\n \"\",\n ];\n\n for (const group of rendered) {\n lines.push(`# ${group.section}`);\n for (const secret of group.secrets) {\n lines.push(`${secret}=${defaultSecretValue(secret, databaseMap, redisMap, options)}`);\n }\n lines.push(\"\");\n }\n\n return `${lines.join(\"\\n\")}\\n`;\n}\n\nexport { renderEnvFile };\n\nfunction renderEnvTestFile(groups: SecretGroup[]): string {\n const lines: string[] = [\n \"# Generated test environment — loaded by test suites instead of .env\",\n \"# Test databases run on dedicated *-test docker-compose services\",\n \"\",\n ];\n\n for (const group of groups) {\n const entries: string[] = [];\n for (const secret of group.secrets) {\n if (secret === \"BETTER_AUTH_SECRET\") {\n entries.push(`${secret}=${TEST_AUTH_SECRET}`);\n } else if (secret.endsWith(\"_DATABASE_URL\")) {\n entries.push(`${secret}=${testDatabaseUrl(secret)}`);\n }\n }\n if (entries.length > 0) {\n lines.push(`# ${group.section}`);\n lines.push(...entries);\n lines.push(\"\");\n }\n }\n\n return `${lines.join(\"\\n\")}\\n`;\n}\n\nexport { renderEnvTestFile };\n\nexport interface CiServiceSpec {\n key: string;\n slug: string;\n image: string;\n env: Record<string, string>;\n ports: string[];\n healthcheck?: { test: string[]; interval: string; timeout: string; retries: number };\n volumes: string[];\n database?: { user: string; password: string; name: string };\n}\n\nexport interface CiInfraPlan {\n account: string;\n gateway: string;\n project: string;\n env: Record<string, string>;\n services: CiServiceSpec[];\n generatedAt: string;\n}\n\nexport function buildCiInfraPlan(\n runtimeConfig: RuntimeConfig,\n options: { hostPortOverride?: number } = {},\n): CiInfraPlan {\n const allSecrets = collectAllSecrets(runtimeConfig);\n const databases = buildConventionalDatabases(allSecrets);\n const redisConfigs = buildConventionalRedis(allSecrets);\n\n const hostPort =\n options.hostPortOverride ??\n (Number.isFinite(Number(process.env.BOS_CI_HOST_PORT))\n ? Number(process.env.BOS_CI_HOST_PORT)\n : undefined) ??\n resolveDevHostPort(runtimeConfig);\n const env: Record<string, string> = {};\n for (const db of databases) env[db.secret] = db.url;\n for (const r of redisConfigs) env[r.secret] = r.url;\n\n env.CORS_ORIGIN = `http://127.0.0.1:${hostPort}`;\n if (!env.BETTER_AUTH_SECRET) env.BETTER_AUTH_SECRET = \"\";\n\n const services: CiServiceSpec[] = [];\n const seenPorts = new Set<number>();\n\n for (const db of databases) {\n if (seenPorts.has(db.port)) continue;\n seenPorts.add(db.port);\n services.push({\n key: db.slug,\n slug: db.slug,\n image: \"public.ecr.aws/docker/library/postgres:17-alpine\",\n env: {\n POSTGRES_USER: POSTGRES_USER,\n POSTGRES_PASSWORD: POSTGRES_PASSWORD,\n POSTGRES_DB: db.databaseName,\n },\n ports: [`${db.port}:5432`],\n healthcheck: {\n test: [\"CMD-SHELL\", `pg_isready -U ${POSTGRES_USER} -d ${db.databaseName}`],\n interval: \"3s\",\n timeout: \"3s\",\n retries: 10,\n },\n volumes: [`${db.slug}_data:/var/lib/postgresql/data`],\n database: { user: POSTGRES_USER, password: POSTGRES_PASSWORD, name: db.databaseName },\n });\n }\n\n for (const r of redisConfigs) {\n services.push({\n key: r.slug,\n slug: r.slug,\n image: \"public.ecr.aws/docker/library/redis:7-alpine\",\n env: {},\n ports: [`${r.port}:6379`],\n healthcheck: {\n test: [\"CMD\", \"redis-cli\", \"ping\"],\n interval: \"3s\",\n timeout: \"3s\",\n retries: 10,\n },\n volumes: [`${r.slug}_data:/data`],\n });\n }\n\n return {\n account: runtimeConfig.account,\n gateway: runtimeConfig.domain ?? runtimeConfig.account,\n project: runtimeConfig.account,\n env,\n services,\n generatedAt: new Date().toISOString(),\n };\n}\n\nfunction collectAllSecrets(runtimeConfig: RuntimeConfig): string[] {\n const all: string[] = [];\n all.push(...(runtimeConfig.host.secrets ?? []));\n all.push(...(runtimeConfig.api.secrets ?? []));\n if (runtimeConfig.auth) all.push(...(runtimeConfig.auth.secrets ?? []));\n if (runtimeConfig.plugins) {\n for (const plugin of Object.values(runtimeConfig.plugins)) {\n if (plugin.secrets) all.push(...plugin.secrets);\n }\n }\n return all.filter((s) => !VESTIGIAL_SECRETS.has(s));\n}\n"],"mappings":"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"}