Files
template-app/better-auth/deploy.mjs
T

93 lines
3.9 KiB
JavaScript

// 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)
}
}