// SPDX-License-Identifier: LicenseRef-ConusAI-Template-1.0 // Deploys this app's Better Auth functions (./functions) to its Better Auth Convex deployment, after // installing the two variables Better Auth reads there: SITE_URL, the app's public origin and the // only origin Better Auth trusts, and BETTER_AUTH_SECRET. The migrate job runs it on the Postgres // profile (apps/worker/src/migrate.ts), the convex-deploy job on the Convex profile; a development // machine runs `node better-auth/deploy.mjs`. The temporary env file is mode 0600 and removed on // success and on failure; no value is printed. // // BETTER_AUTH_CONVEX_URL the Better Auth deployment's URL // BETTER_AUTH_CONVEX_ADMIN_KEY its admin key (this job and the web server's revocation relay only) // BETTER_AUTH_SECRET Better Auth's secret, at least 32 characters (this job only) // CONUSAI_PUBLIC_URL the app's public origin import { spawnSync } from "node:child_process" import { mkdtempSync, rmSync, writeFileSync } from "node:fs" import { tmpdir } from "node:os" import { join } from "node:path" import { fileURLToPath } from "node:url" const required = (name, value, why) => { const trimmed = value?.trim() if (!trimmed) throw new Error(`${name} is not set: ${why}`) if (/[\r\n]/.test(trimmed)) throw new Error(`${name} holds a line break`) return trimmed } export function deployBetterAuth(env = process.env) { const url = required( "BETTER_AUTH_CONVEX_URL", env.BETTER_AUTH_CONVEX_URL, "the URL of this app's Better Auth deployment", ) const adminKey = required( "BETTER_AUTH_CONVEX_ADMIN_KEY", env.BETTER_AUTH_CONVEX_ADMIN_KEY, "the admin key of the Better Auth deployment", ) const secret = required( "BETTER_AUTH_SECRET", env.BETTER_AUTH_SECRET, "Better Auth's secret, 32 random characters or more", ) if (secret.length < 32) throw new Error("BETTER_AUTH_SECRET is shorter than 32 characters") const publicUrl = required( "CONUSAI_PUBLIC_URL", env.CONUSAI_PUBLIC_URL, "the app's public origin, which Better Auth trusts", ) const site = new URL(publicUrl).origin const project = fileURLToPath(new URL(".", import.meta.url)) const cli = fileURLToPath(new URL("../node_modules/convex/bin/main.js", import.meta.url)) const childEnv = { ...env, CONVEX_SELF_HOSTED_URL: url, CONVEX_SELF_HOSTED_ADMIN_KEY: adminKey } delete childEnv.CONVEX_DEPLOYMENT delete childEnv.CONVEX_DEPLOY_KEY const directory = mkdtempSync(join(tmpdir(), "conusai-better-auth-env-")) const envFile = join(directory, ".env") try { writeFileSync(envFile, `SITE_URL=${site}\nBETTER_AUTH_SECRET=${secret}\n`, { mode: 0o600, flag: "wx" }) const seeded = spawnSync(process.execPath, [cli, "env", "set", "--from-file", envFile, "--force"], { cwd: project, env: childEnv, stdio: ["ignore", "ignore", "pipe"], encoding: "utf8", }) if (seeded.error || seeded.status !== 0) { // The CLI names what failed (the connection, the key); it does not print the values it was given. throw new Error( `the Better Auth deployment's environment could not be set: ${seeded.stderr?.trim().split("\n").at(-1) ?? ""}`, ) } const deployed = spawnSync(process.execPath, [cli, "deploy", "--yes", "--typecheck", "disable"], { cwd: project, env: childEnv, stdio: "inherit", }) if (deployed.error) throw deployed.error if (deployed.status !== 0) throw new Error(`convex deploy of the Better Auth functions exited ${deployed.status}`) } finally { rmSync(directory, { recursive: true, force: true }) } } if (process.argv[1] && fileURLToPath(import.meta.url) === process.argv[1]) { try { deployBetterAuth() console.info("better-auth: functions deployed") } catch (error) { console.error(`better-auth: ${error instanceof Error ? error.message : String(error)}`) process.exit(1) } }