create-authup 1.0.0-beta.70 → 1.0.0-beta.71
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -3
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":["DOCS_URL"],"sources":["../src/ask.ts","../src/constants.ts","../src/deployment.ts","../src/utils.ts","../src/prompts.ts","../src/targets/docker.ts","../src/targets/helm.ts","../src/targets/bare-metal.ts","../src/targets/compose.ts","../src/targets/index.ts","../src/version.ts","../src/index.ts"],"sourcesContent":["/*\n * Copyright (c) 2026.\n * Author Peter Placzek (tada5hi)\n * For the full copyright and license information,\n * view the LICENSE file that was distributed with this source code.\n */\n\nimport process from 'node:process';\nimport { createInterface } from 'node:readline/promises';\nimport { Writable } from 'node:stream';\nimport type { ReadlineAskContext, ReadlineAskResult } from './types.ts';\n\ntype PendingLine = {\n resolve: (line: string) => void,\n reject: (error: Error) => void\n};\n\nconst CLOSED_MESSAGE = 'Input ended before every question was answered.';\n\n/**\n * The readline seam. The streams are arguments rather than `process.stdin` / `process.stdout` reached for directly,\n * so the echo behaviour below is testable without a terminal: a spec passes a pair of in-memory streams claiming to\n * be one.\n */\nexport function createReadlineAsk(ctx: ReadlineAskContext = {}): ReadlineAskResult {\n const input = ctx.input || process.stdin;\n const target = ctx.output || process.stdout;\n\n // On a terminal readline echoes what is typed through the output stream itself, so muting that stream while a\n // secret's line is read is what keeps the password off the screen. Piped input takes none of this path.\n const terminal = Boolean(input.isTTY && target.isTTY);\n let muted = false;\n const output = new Writable({\n write(chunk, _encoding, callback) {\n if (!muted) {\n target.write(chunk);\n }\n callback();\n },\n });\n const rl = createInterface({\n input,\n output,\n terminal,\n });\n\n // readline emits every line of one stdin chunk synchronously, while the next question is only registered on a\n // later microtask, so `rl.question` drops all but the first answer of a piped script. The lines are buffered\n // instead, and a close with a question still open is an error rather than the silent exit 0 node answers with.\n const lines: string[] = [];\n let pending: PendingLine | undefined;\n let closed = false;\n\n rl.on('line', (line) => {\n if (pending) {\n const { resolve } = pending;\n pending = undefined;\n resolve(line);\n return;\n }\n\n lines.push(line);\n });\n\n rl.on('close', () => {\n closed = true;\n if (pending) {\n const { reject } = pending;\n pending = undefined;\n reject(new Error(CLOSED_MESSAGE));\n }\n });\n\n // Ctrl+C on a TTY reaches readline rather than the process; without this it closes the interface and reads as EOF.\n rl.on('SIGINT', () => process.exit(130));\n\n function readLine(): Promise<{ line: string, buffered: boolean }> {\n const buffered = lines.shift();\n if (buffered !== undefined) {\n return Promise.resolve({ line: buffered, buffered: true });\n }\n\n if (closed) {\n return Promise.reject(new Error(CLOSED_MESSAGE));\n }\n\n return new Promise((resolve, reject) => {\n pending = { resolve: (line) => resolve({ line, buffered: false }), reject };\n });\n }\n\n return {\n // The answer is handed over untrimmed: a secret keeps its whitespace, and each parser trims what it should.\n ask: async (question, fallback, options) => {\n rl.setPrompt(fallback === undefined ? `${question}: ` : `${question} [${fallback}]: `);\n rl.prompt();\n const secret = terminal && Boolean(options?.secret);\n muted = secret;\n try {\n const { line, buffered } = await readLine();\n if (buffered && terminal && !secret) {\n // a type-ahead answer never echoed after this prompt, so the next prompt would overwrite the line\n target.write(`${line}\\n`);\n }\n\n return fallback !== undefined && line.trim() === '' ? fallback : line;\n } finally {\n if (secret) {\n // the Enter was swallowed with the echo\n muted = false;\n target.write('\\n');\n }\n }\n },\n close: () => rl.close(),\n };\n}\n","/*\n * Copyright (c) 2026.\n * Author Peter Placzek (tada5hi)\n * For the full copyright and license information,\n * view the LICENSE file that was distributed with this source code.\n */\n\nimport type { Target } from './types.ts';\n\n/**\n * The emitted files that hold a password, written owner-only by `src/index.ts`. The compose file, `nginx.conf`,\n * `package.json` and `authup.yml` carry none: every secret rides an `.env`, or `values.yaml` for helm.\n * `test/unit/secret-files.spec.ts` holds the renderers to this list, so a new secret-bearing file cannot slip past it.\n */\nexport const SECRET_FILES = new Set(['.env', 'authup.env', 'values.yaml']);\n\nexport const TARGETS: readonly Target[] = ['docker', 'compose', 'helm', 'bare-metal'] as const;\n","/*\n * Copyright (c) 2026.\n * Author Peter Placzek (tada5hi)\n * For the full copyright and license information,\n * view the LICENSE file that was distributed with this source code.\n */\n\nimport type { Exposure } from './types.ts';\n\n/** The port the process listens on inside a container, and the default everywhere else. */\nexport const LISTEN_PORT = 3000;\n\n// A public url with an explicit port is dialed directly, so that port is the one to publish or to listen on. A\n// default port, or https, means a reverse proxy in front owns the port a browser dials and reaches the listener\n// on its own, so the default listen port stays. `http://localhost` with no port is read as a proxy too; the\n// operator who really serves port 80 directly edits the mapping.\nexport function describeExposure(publicUrl: string): Exposure {\n const url = new URL(publicUrl);\n const behindProxy = url.protocol === 'https:' || url.port === '';\n\n return {\n behindProxy,\n hostPort: behindProxy ? LISTEN_PORT : Number(url.port),\n };\n}\n\n// The value core.trustProxy / TRUST_PROXY takes: the number of proxy hops the deployment itself knows about\n// (the operator's proxy in front, plus the one the compose console split emits), or false when nothing is in front,\n// since authup's default trusts every hop and lets any direct client spoof its address.\nexport function trustProxyValue(exposure: Exposure, ownProxy = false): string {\n const hops = (exposure.behindProxy ? 1 : 0) + (ownProxy ? 1 : 0);\n\n return hops > 0 ? String(hops) : 'false';\n}\n","/*\n * Copyright (c) 2026.\n * Author Peter Placzek (tada5hi)\n * For the full copyright and license information,\n * view the LICENSE file that was distributed with this source code.\n */\n\n// A JSON string is a valid YAML double-quoted scalar, which also defuses the Norway problem and a leading `*`.\nexport function quoteYaml(value: string): string {\n return JSON.stringify(value);\n}\n\nconst PLAIN = /^[A-Za-z0-9_@:/.\\-+=%?&,]*$/;\n\n// Whether a value survives the .env round trip at all. dotenv strips the surrounding quotes and, inside DOUBLE quotes,\n// turns a literal \\n or \\r into the control character itself, with no escape that survives (it never unescapes a\n// doubled backslash). A value carrying a single quote, which rules the single-quoted form out, together with one of\n// those two sequences therefore has no representation, and `askSecret` refuses it rather than corrupting a password.\nexport function isEnvRepresentable(value: string): boolean {\n return !value.includes('\\'') || !/\\\\[nr]/.test(value);\n}\n\n// For the .env dotenv reads (`authup start` loads it from cwd): a value is single-quoted when it carries no single\n// quote and written raw inside double quotes otherwise, which round-trips everything `isEnvRepresentable` admits.\nexport function quoteEnv(value: string): string {\n if (PLAIN.test(value)) {\n return value;\n }\n\n if (!value.includes('\\'')) {\n return `'${value}'`;\n }\n\n return `\"${value}\"`;\n}\n\n// For the .env compose-go reads: double quotes take backslash escapes and `$` interpolates unless doubled, while a\n// single-quoted `\\'` there is an escaped quote rather than a literal, so one double-quoted branch covers every value.\nexport function quoteComposeEnv(value: string): string {\n if (PLAIN.test(value)) {\n return value;\n }\n\n return `\"${escapeComposeInterpolation(value.replace(/[\\\\\"]/g, '\\\\$&'))}\"`;\n}\n\n// compose interpolates `$name` and `${name}` in every yaml value and .env value; `$$` is its literal dollar.\nexport function escapeComposeInterpolation(value: string): string {\n return value.replace(/\\$/g, '$$$$');\n}\n\nexport function indent(text: string, spaces: number): string {\n const prefix = ' '.repeat(spaces);\n\n return text\n .split('\\n')\n .map((line) => (line.length > 0 ? prefix + line : line))\n .join('\\n');\n}\n","/*\n * Copyright (c) 2026.\n * Author Peter Placzek (tada5hi)\n * For the full copyright and license information,\n * view the LICENSE file that was distributed with this source code.\n */\n\nimport { TARGETS } from './constants.ts';\nimport { isEnvRepresentable } from './utils.ts';\nimport type {\n Answers,\n Ask,\n AskOptions,\n Database,\n Target,\n} from './types.ts';\n\ntype Parsed<T> = { value: T } | { error: string };\n\nconst ENGINE_PORTS = { postgres: 5432, mysql: 3306 } as const;\nconst BUNDLED_REDIS_URL = 'redis://redis:6379';\nconst UNREPRESENTABLE = 'A single quote together with a \\\\n or \\\\r sequence has no .env representation. Choose another value.';\nconst REDIS_NOTE = 'Redis is required for a split deployment: the console sign-in and the token blocklist ride the cache.';\n\n// A refused answer re-asks with the reason on the line above, so the flow stays pure over `ask`.\nasync function askUntil<T>(\n ask: Ask,\n question: string,\n fallback: string | undefined,\n parse: (answer: string) => Parsed<T>,\n options?: AskOptions,\n): Promise<T> {\n let prefix = '';\n\n for (;;) {\n const parsed = parse(await ask(prefix + question, fallback, options));\n if ('value' in parsed) {\n return parsed.value;\n }\n\n prefix = `${parsed.error}\\n`;\n }\n}\n\nfunction askChoice<T extends string>(ask: Ask, question: string, options: readonly T[], fallback: T): Promise<T> {\n const listed = options.map((option, index) => `${index + 1}: ${option}`).join(', ');\n\n return askUntil<T>(ask, `${question} (${listed})`, fallback, (answer) => {\n const normalized = answer.trim().toLowerCase();\n if ((options as readonly string[]).includes(normalized)) {\n return { value: normalized as T };\n }\n\n const numbered = options[Number(normalized) - 1];\n if (numbered !== undefined) {\n return { value: numbered };\n }\n\n return { error: `Pick one of ${options.join(', ')}, by name or number.` };\n });\n}\n\nfunction askYesNo(ask: Ask, question: string, fallback: boolean): Promise<boolean> {\n return askUntil<boolean>(ask, `${question} (y/n)`, fallback ? 'y' : 'n', (answer) => {\n const normalized = answer.trim().toLowerCase();\n if (normalized === 'y' || normalized === 'yes') {\n return { value: true };\n }\n if (normalized === 'n' || normalized === 'no') {\n return { value: false };\n }\n\n return { error: 'Answer y or n.' };\n });\n}\n\nfunction askUrl(ask: Ask, question: string, protocols: readonly string[] = ['http:', 'https:'], fallback?: string): Promise<string> {\n return askUntil<string>(ask, question, fallback, (answer) => {\n const trimmed = answer.trim();\n try {\n // a non-special scheme parses `smtp:mail` with an empty host, which would land in the file as written\n const url = new URL(trimmed);\n if (protocols.includes(url.protocol) && url.hostname !== '') {\n return { value: trimmed };\n }\n } catch {\n // falls through to the refusal\n }\n\n return { error: `Enter an absolute URL using ${protocols.map((protocol) => `${protocol}//`).join(' or ')}.` };\n });\n}\n\n// A free-text answer that must not be blank; trimmed, unlike a secret.\nfunction askText(ask: Ask, question: string, fallback?: string): Promise<string> {\n return askUntil<string>(ask, question, fallback, (answer) => {\n const trimmed = answer.trim();\n if (trimmed === '') {\n return { error: 'A value is required.' };\n }\n\n // a host, user or database name reaches the same .env the secrets do\n return isEnvRepresentable(trimmed) ? { value: trimmed } : { error: UNREPRESENTABLE };\n });\n}\n\n// The issuer: no credentials, query or fragment (OIDC Core 3.1.2.1), and for the helm target no path either, since\n// the chart publishes authup at the origin root and refuses a console split under a sub-path.\nfunction askPublicUrl(ask: Ask, target: Target): Promise<string> {\n return askUntil<string>(\n ask,\n 'Public URL: the address a browser reaches authup at. It is the OIDC issuer, and every console url derives from it',\n undefined,\n (answer) => {\n const trimmed = answer.trim();\n let url: URL;\n try {\n url = new URL(trimmed);\n } catch {\n return { error: 'Enter an absolute URL using http:// or https://.' };\n }\n\n if (url.protocol !== 'http:' && url.protocol !== 'https:') {\n return { error: 'Enter an absolute URL using http:// or https://.' };\n }\n if (url.username || url.password || url.search || url.hash) {\n return { error: 'The public URL is the OIDC issuer: no credentials, query or fragment.' };\n }\n if (target === 'helm' && url.pathname !== '/') {\n return { error: 'The helm chart publishes authup at the origin root: enter the URL without a path.' };\n }\n\n return { value: trimmed };\n },\n );\n}\n\nfunction askPort(ask: Ask, question: string, fallback: number): Promise<number> {\n return askUntil<number>(ask, question, String(fallback), (answer) => {\n const port = Number(answer.trim());\n if (Number.isSafeInteger(port) && port >= 1 && port <= 65535) {\n return { value: port };\n }\n\n return { error: 'Enter a port between 1 and 65535.' };\n });\n}\n\nfunction askSecret(ask: Ask, question: string): Promise<string> {\n return askUntil<string>(ask, question, undefined, (answer) => {\n if (answer.trim() === '') {\n return { error: 'A value is required.' };\n }\n if (answer === 'start123') {\n return { error: 'start123 is the provisioning default the wizard refuses to emit. Choose another value.' };\n }\n // the bounds the config registry declares for userAdminPassword\n if (answer.length < 3 || answer.length > 256) {\n return { error: 'Use between 3 and 256 characters.' };\n }\n if (!isEnvRepresentable(answer)) {\n return { error: UNREPRESENTABLE };\n }\n\n return { value: answer };\n }, { secret: true });\n}\n\nasync function askDatabase(ask: Ask, target: Target): Promise<Database> {\n const options: readonly ('postgres' | 'mysql' | 'sqlite')[] = target === 'bare-metal' ?\n ['postgres', 'mysql', 'sqlite'] :\n ['postgres', 'mysql'];\n const type = await askChoice(ask, 'Database', options, 'postgres');\n if (type === 'sqlite') {\n return { type: 'better-sqlite3' };\n }\n\n const bundled = (target === 'compose' || target === 'helm') &&\n await askYesNo(ask, 'Run the database alongside authup?', true);\n if (bundled) {\n return {\n type,\n bundled: true,\n host: type,\n port: ENGINE_PORTS[type],\n username: 'authup',\n password: await askSecret(ask, 'Database password'),\n database: 'authup',\n };\n }\n\n // 127.0.0.1 is the operator's machine on bare metal and the container itself everywhere else\n const hostQuestion = target === 'bare-metal' ?\n 'Database host' :\n 'Database host (a name the container resolves; 127.0.0.1 would be the container itself)';\n\n return {\n type,\n bundled: false,\n host: await askText(ask, hostQuestion, target === 'bare-metal' ? '127.0.0.1' : undefined),\n port: await askPort(ask, 'Database port', ENGINE_PORTS[type]),\n username: await askText(ask, 'Database username', 'authup'),\n password: await askSecret(ask, 'Database password'),\n database: await askText(ask, 'Database name', 'authup'),\n };\n}\n\nexport async function collectAnswers(ask: Ask): Promise<{ answers: Answers, notes: string[] }> {\n const target = await askChoice(ask, 'Deployment target', TARGETS, 'compose');\n const publicUrl = await askPublicUrl(ask, target);\n const tlsCertManager = target === 'helm' && publicUrl.startsWith('https:') &&\n await askYesNo(ask, 'Request the TLS certificate from cert-manager? The alternative is a <hostname>-tls secret in the release namespace', false);\n const db = await askDatabase(ask, target);\n\n const registrationEnabled = await askYesNo(ask, 'Enable user registration?', false);\n const passwordRecoveryEnabled = await askYesNo(ask, 'Enable password recovery?', false);\n const smtp = registrationEnabled || passwordRecoveryEnabled ?\n { url: await askUrl(ask, 'SMTP connection URL (smtp(s)://user:pass@host:port)', ['smtp:', 'smtps:']) } :\n false;\n\n const emailVerificationEnabled = registrationEnabled && await askYesNo(ask, 'Verify email addresses on registration?', true);\n\n const adminPassword = await askSecret(ask, 'Admin password');\n\n const splittable = target === 'compose' || target === 'helm';\n const workerSplit = splittable && await askYesNo(ask, 'Run the background worker as a separate service?', false);\n const consoleSplit = splittable && await askYesNo(ask, 'Run the consoles as a separate service?', false);\n\n const notes: string[] = [];\n let redis: Answers['redis'] = false;\n if (workerSplit || consoleSplit) {\n redis = { url: BUNDLED_REDIS_URL };\n notes.push(REDIS_NOTE);\n } else if (await askYesNo(ask, 'Use redis?', false)) {\n redis = {\n url: splittable ?\n BUNDLED_REDIS_URL :\n await askUrl(\n ask,\n target === 'bare-metal' ? 'Redis URL' : 'Redis URL (a host the container resolves)',\n ['redis:', 'rediss:'],\n target === 'bare-metal' ? 'redis://127.0.0.1:6379' : undefined,\n ),\n };\n }\n\n return {\n answers: {\n target,\n publicUrl,\n db,\n redis,\n smtp,\n registrationEnabled,\n passwordRecoveryEnabled,\n emailVerificationEnabled,\n adminPassword,\n workerSplit,\n consoleSplit,\n tlsCertManager,\n },\n notes,\n };\n}\n","/*\n * Copyright (c) 2026.\n * Author Peter Placzek (tada5hi)\n * For the full copyright and license information,\n * view the LICENSE file that was distributed with this source code.\n */\n\nimport { describeExposure, trustProxyValue } from '../deployment.ts';\nimport type { Answers, Rendered } from '../types.ts';\n\nexport function dockerRunCommand(version: string, hostPort: number): string {\n return `docker run -d --name authup --restart unless-stopped --env-file authup.env -p ${hostPort}:3000 authup/authup:${version} start`;\n}\n\nexport function renderDocker(answers: Answers, version: string): Rendered {\n if (answers.db.type === 'better-sqlite3') {\n throw new Error('The docker image runs in production mode and refuses sqlite: configure a postgres or mysql database.');\n }\n\n if (answers.workerSplit || answers.consoleSplit) {\n throw new Error('The docker run target is one container: a worker or console split needs the compose or helm target.');\n }\n\n const exposure = describeExposure(answers.publicUrl);\n\n // `docker run --env-file` reads KEY=value verbatim and does no quote handling, so values are written raw rather than through quoteEnv.\n const lines = [\n `PUBLIC_URL=${answers.publicUrl}`,\n '# The proxies in front whose X-Forwarded-For is trusted, or false; authup trusts every hop by default.',\n `TRUST_PROXY=${trustProxyValue(exposure)}`,\n `DB_TYPE=${answers.db.type}`,\n `DB_HOST=${answers.db.host}`,\n `DB_PORT=${answers.db.port}`,\n `DB_USERNAME=${answers.db.username}`,\n `DB_PASSWORD=${answers.db.password}`,\n `DB_DATABASE=${answers.db.database}`,\n ];\n\n if (answers.redis) {\n lines.push(`REDIS=${answers.redis.url}`);\n }\n\n if (answers.smtp) {\n lines.push(`SMTP=${answers.smtp.url}`);\n }\n\n if (answers.registrationEnabled) {\n lines.push('REGISTRATION_ENABLED=true');\n }\n\n if (answers.passwordRecoveryEnabled) {\n lines.push('PASSWORD_RECOVERY_ENABLED=true');\n }\n\n if (answers.emailVerificationEnabled) {\n lines.push('EMAIL_VERIFICATION_ENABLED=true');\n }\n\n lines.push(\n `USER_ADMIN_PASSWORD=${answers.adminPassword}`,\n '# Every downstream application origin, comma-separated; each may obtain a full-permission token.',\n '# TRUSTED_ORIGINS=https://app.example.com',\n '# Wraps the realm key store at rest (base64, 32 bytes). Write-once: back it up.',\n '# SECRETS_ENCRYPTION_KEY=<base64 32 bytes>',\n );\n\n return {\n 'authup.env': [\n '# Environment of the authup container. It holds secrets: keep it out of version control.',\n `# ${dockerRunCommand(version, exposure.hostPort)}`,\n '# The database must exist already; the image runs in production mode and refuses sqlite.',\n '# A configuration file or a provisioning directory is mounted with',\n '# -v ./authup.yml:/etc/authup/authup.yml:ro -v ./provisioning:/etc/authup/provisioning:ro',\n '# https://authup.org/guide/deployment/docker',\n ...lines,\n '',\n ].join('\\n'),\n };\n}\n","/*\n * Copyright (c) 2026.\n * Author Peter Placzek (tada5hi)\n * For the full copyright and license information,\n * view the LICENSE file that was distributed with this source code.\n */\n\nimport type { Answers, Rendered } from '../types.ts';\nimport { quoteYaml } from '../utils.ts';\n\n// The chart version, not the authup version: it is what `helm install` selects, and 0.4.0 is the first that carries\n// worker.* and server.splitConsoles (0.3.0 rejects both through its values schema). Every document names worker.enabled,\n// so every document needs it.\nexport const CHART_REQUIREMENT = '# This file needs the authup helm chart 0.4.0 or later.';\n\nexport const HELM_COMMANDS: string[] = [\n 'helm repo add authup https://helm.authup.org',\n 'helm install authup authup/authup -f values.yaml',\n];\n\nexport function renderHelm(answers: Answers, _version: string): Rendered {\n const { db } = answers;\n if (db.type === 'better-sqlite3') {\n throw new Error('The helm chart needs a server database (postgres or mysql); the image refuses sqlite.');\n }\n\n const url = new URL(answers.publicUrl);\n const tls = url.protocol === 'https:';\n const lines: string[] = [\n `# Written by npm create authup. Install with: ${HELM_COMMANDS.join(' && ')}`,\n '# Secrets in this file (auth.adminPassword, the database password, smtp.connectionString) can move to the chart\\'s existingSecret keys; see the chart README.',\n // worker.* arrived with the chart that tracks v1.0.0-beta.64 (authup/helm PR #29); the released 0.3.0 rejects it\n // through its values schema.\n CHART_REQUIREMENT,\n 'server:',\n ` publicUrl: ${quoteYaml(answers.publicUrl)}`,\n ...(tls && !answers.tlsCertManager ?\n [` # TLS needs a secret named ${url.hostname}-tls in the release namespace, or set server.ingress.certManager: true.`] :\n []),\n ' ingress:',\n ' enabled: true',\n ` hostname: ${quoteYaml(url.hostname)}`,\n ` tls: ${tls}`,\n ...(tls && answers.tlsCertManager ? [' certManager: true'] : []),\n ];\n\n if (answers.registrationEnabled || answers.passwordRecoveryEnabled || answers.emailVerificationEnabled) {\n lines.push(' features:');\n if (answers.registrationEnabled) {\n lines.push(' registration: true');\n }\n if (answers.passwordRecoveryEnabled) {\n lines.push(' passwordRecovery: true');\n }\n if (answers.emailVerificationEnabled) {\n lines.push(' emailVerification: true');\n }\n }\n\n if (answers.workerSplit) {\n lines.push(' migration:', ' enabled: true');\n }\n\n if (answers.consoleSplit) {\n lines.push(' splitConsoles: true');\n }\n\n lines.push(\n ' # Every downstream application origin; each may obtain a full-permission token.',\n ' # trustedOrigins:',\n ' # - https://app.example.com',\n ' # An authup.yml the chart mounts, for the options that only the file can carry.',\n ' # configuration: |',\n ' # core:',\n ' # passwordMinLength: 12',\n );\n\n // Always spelled out: the chart's default flipped to a dedicated worker in\n // 0.5.0, so leaving the key out would split a deployment that asked not to.\n lines.push('', 'worker:', ` enabled: ${answers.workerSplit}`);\n\n lines.push(\n '',\n 'database:',\n ` type: ${db.type}`,\n '',\n );\n\n // The bundled engine takes the answered password; left empty, the chart would generate one and drop the answer.\n for (const engine of ['postgresql', 'mysql'] as const) {\n const enabled = db.bundled && db.type === (engine === 'postgresql' ? 'postgres' : 'mysql');\n lines.push(`${engine}:`, ` enabled: ${enabled}`);\n if (enabled) {\n lines.push(' auth:', ` password: ${quoteYaml(db.password)}`);\n }\n }\n\n if (!db.bundled) {\n // The chart schema types the port as a string (\"\" = engine default), so a bare integer fails helm's validation.\n lines.push(\n 'externalDatabase:',\n ` host: ${quoteYaml(db.host)}`,\n ` port: ${quoteYaml(String(db.port))}`,\n ` user: ${quoteYaml(db.username)}`,\n ` database: ${quoteYaml(db.database)}`,\n ` password: ${quoteYaml(db.password)}`,\n );\n }\n\n if (answers.redis) {\n lines.push(\n '',\n 'valkey:',\n ' enabled: true',\n '# For an external Redis set valkey.enabled to false and externalRedis.url to its redis:// URL instead.',\n );\n }\n\n lines.push(\n '',\n 'auth:',\n ` adminPassword: ${quoteYaml(answers.adminPassword)}`,\n ' # Wraps the realm key store at rest (base64, 32 bytes). Write-once: back it up.',\n ' # secretsEncryptionKey: <base64 32 bytes>',\n );\n\n if (answers.smtp) {\n lines.push('', 'smtp:', ` connectionString: ${quoteYaml(answers.smtp.url)}`);\n }\n\n return { 'values.yaml': `${lines.join('\\n')}\\n` };\n}\n","/*\n * Copyright (c) 2026.\n * Author Peter Placzek (tada5hi)\n * For the full copyright and license information,\n * view the LICENSE file that was distributed with this source code.\n */\n\nimport { LISTEN_PORT, describeExposure, trustProxyValue } from '../deployment.ts';\nimport type { Answers, Rendered } from '../types.ts';\nimport { quoteEnv, quoteYaml } from '../utils.ts';\n\nconst SCHEMA_COMMENT = '# yaml-language-server: $schema=https://authup.org/schema/config.json';\nconst DATABASE_DOCS_URL = 'https://authup.org/guide/deployment/configuration-server-core-database';\n\nexport function renderBareMetal(answers: Answers, version: string): Rendered {\n if (answers.workerSplit || answers.consoleSplit) {\n throw new Error('The bare-metal target runs one process: a worker or console split is a second process under a process manager, which the wizard does not write.');\n }\n\n return {\n 'package.json': renderPackageJson(version),\n 'authup.yml': renderConfig(answers),\n '.env': renderEnv(answers),\n };\n}\n\nfunction renderPackageJson(version: string): string {\n const manifest = {\n name: 'authup-deployment',\n private: true,\n type: 'module',\n scripts: { start: 'authup start' },\n // exact, like the image tag the container targets write: the wizard scaffolds one release, not a range\n dependencies: { authup: version },\n };\n\n return `${JSON.stringify(manifest, null, 4)}\\n`;\n}\n\nfunction renderConfig(answers: Answers): string {\n const { db } = answers;\n const lines = [\n SCHEMA_COMMENT,\n `publicUrl: ${quoteYaml(answers.publicUrl)}`,\n ];\n\n if (db.type === 'better-sqlite3') {\n lines.push(\n '# Production refuses sqlite, so this file keeps the development default of env.',\n `# That is fine for a local trial only: ${DATABASE_DOCS_URL}`,\n '',\n '# db.sqlite is written into this directory.',\n );\n } else {\n lines.push(\n 'env: production',\n '',\n // A `db` block here would be used AS IS: server-core never merges DB_* over it, so the password could not\n // stay in .env. The whole connection rides the DB_* variables there instead (the documented env form).\n '# The database connection is the DB_* set in .env.',\n );\n }\n\n if (answers.redis) {\n lines.push('', `redis: ${quoteYaml(answers.redis.url)}`);\n }\n\n if (answers.smtp) {\n lines.push('', '# The connection URL is SMTP in .env.', 'smtp: true');\n }\n\n lines.push(\n '',\n '# Every downstream application origin; each may obtain a full-permission token.',\n '# trustedOrigins:',\n '# - https://app.example.com',\n );\n\n // The process listens where the url says when nothing is in front; behind a proxy the default stays.\n const exposure = describeExposure(answers.publicUrl);\n lines.push(\n '',\n 'core:',\n ...(exposure.hostPort !== LISTEN_PORT ? [` port: ${exposure.hostPort}`] : []),\n ' # The proxies in front whose X-Forwarded-For is trusted, or false; authup trusts every hop by default.',\n ` trustProxy: ${trustProxyValue(exposure)}`,\n ...(answers.registrationEnabled ? [' registrationEnabled: true'] : []),\n ...(answers.passwordRecoveryEnabled ? [' passwordRecoveryEnabled: true'] : []),\n ...(answers.emailVerificationEnabled ? [' emailVerificationEnabled: true'] : []),\n );\n\n return `${lines.join('\\n')}\\n`;\n}\n\nfunction renderEnv(answers: Answers): string {\n const lines = ['# Secrets. authup start reads this file from the directory it is started in. Keep it out of version control.'];\n\n const { db } = answers;\n if (db.type !== 'better-sqlite3') {\n lines.push(\n `DB_TYPE=${db.type}`,\n `DB_HOST=${quoteEnv(db.host)}`,\n `DB_PORT=${db.port}`,\n `DB_USERNAME=${quoteEnv(db.username)}`,\n `DB_PASSWORD=${quoteEnv(db.password)}`,\n `DB_DATABASE=${quoteEnv(db.database)}`,\n );\n }\n\n lines.push(`USER_ADMIN_PASSWORD=${quoteEnv(answers.adminPassword)}`);\n\n if (answers.smtp) {\n lines.push(`SMTP=${quoteEnv(answers.smtp.url)}`);\n }\n\n lines.push(\n '# Wraps the realm key store at rest (base64, 32 bytes). Write-once: back it up.',\n '# SECRETS_ENCRYPTION_KEY=',\n );\n\n return `${lines.join('\\n')}\\n`;\n}\n","/*\n * Copyright (c) 2026.\n * Author Peter Placzek (tada5hi)\n * For the full copyright and license information,\n * view the LICENSE file that was distributed with this source code.\n */\n\nimport { describeExposure, trustProxyValue } from '../deployment.ts';\nimport type { Answers, Rendered } from '../types.ts';\nimport {\n escapeComposeInterpolation,\n indent,\n quoteComposeEnv,\n quoteYaml,\n} from '../utils.ts';\n\nconst DOCS_URL = 'https://authup.org/guide/deployment';\n\nconst NGINX_CONF = `# Written by npm create authup: the console split's routing, ${DOCS_URL}/console-replicas\n# TLS, if any, terminates in front of this proxy; X-Forwarded-Proto is passed through as the scheme it sees.\nupstream authup_api { server authup:3000; }\nupstream authup_auth_console { server authup-console:3020; }\nupstream authup_admin_console { server authup-console:3021; }\nupstream authup_account_console { server authup-console:3022; }\n\nserver {\n listen 80;\n # nginx listens on 80 inside the container while the published port is another one: a redirect it builds itself\n # (the trailing slash it adds to /console/<name>) must stay relative or it would send the browser to port 80.\n absolute_redirect off;\n\n # The sign-in pair stays on the API. Exact matches, so they win over the prefix rules below.\n location = /console/admin/login/start { proxy_pass http://authup_api; }\n location = /console/admin/callback { proxy_pass http://authup_api; }\n location = /console/account/login/start { proxy_pass http://authup_api; }\n location = /console/account/callback { proxy_pass http://authup_api; }\n\n location /console/auth/ {\n rewrite ^/console/auth(/.*)$ $1 break;\n proxy_set_header Host $host;\n proxy_set_header X-Forwarded-Proto $scheme;\n proxy_set_header X-Forwarded-Tls-Client-Cert \"\";\n proxy_pass http://authup_auth_console;\n }\n\n location /console/admin/ {\n rewrite ^/console/admin(/.*)$ $1 break;\n proxy_set_header Host $host;\n proxy_set_header X-Forwarded-Proto $scheme;\n proxy_set_header X-Forwarded-Tls-Client-Cert \"\";\n proxy_pass http://authup_admin_console;\n }\n\n location /console/account/ {\n rewrite ^/console/account(/.*)$ $1 break;\n proxy_set_header Host $host;\n proxy_set_header X-Forwarded-Proto $scheme;\n proxy_set_header X-Forwarded-Tls-Client-Cert \"\";\n proxy_pass http://authup_account_console;\n }\n\n location / {\n proxy_set_header Host $host;\n proxy_set_header X-Real-IP $remote_addr;\n proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\n proxy_set_header X-Forwarded-Proto $scheme;\n proxy_set_header X-Forwarded-Tls-Client-Cert \"\";\n proxy_pass http://authup_api;\n }\n}\n`;\n\n\nconst ENGINES = {\n postgres: {\n image: 'postgres:17',\n port: 5432,\n dataPath: '/var/lib/postgresql/data',\n },\n mysql: {\n image: 'mysql:8.4',\n port: 3306,\n dataPath: '/var/lib/mysql',\n },\n} as const;\n\n// One list entry of a compose `environment` block; the whole KEY=value is one YAML scalar, so quoting it covers the\n// value, and an operator's `$` is doubled so compose does not interpolate it away.\nfunction env(key: string, value: string): string {\n return `- ${quoteYaml(`${key}=${escapeComposeInterpolation(value)}`)}`;\n}\n\n// The same entry carrying a deliberate `${VARIABLE}` reference into .env.\nfunction ref(key: string, variable: string): string {\n return `- ${quoteYaml(`${key}=\\${${variable}}`)}`;\n}\n\nfunction block(key: string, lines: string[]): string[] {\n return [`${key}:`, ...indent(lines.join('\\n'), 2).split('\\n')];\n}\n\nexport function renderCompose(answers: Answers, version: string): Rendered {\n const { db } = answers;\n if (db.type === 'better-sqlite3') {\n throw new Error('The compose target needs a server database (postgres or mysql): the image runs in production mode and refuses sqlite.');\n }\n\n const image = `image: authup/authup:${version}`;\n const restart = 'restart: unless-stopped';\n const engine = ENGINES[db.type];\n const exposure = describeExposure(answers.publicUrl);\n\n const dbEnv = [\n env('DB_TYPE', db.type),\n env('DB_HOST', db.bundled ? db.type : db.host),\n env('DB_PORT', String(db.bundled ? engine.port : db.port)),\n env('DB_USERNAME', db.username),\n ref('DB_PASSWORD', 'DB_PASSWORD'),\n env('DB_DATABASE', db.database),\n ];\n if (answers.redis) {\n dbEnv.push(env('REDIS', 'redis://redis:6379'));\n }\n\n const dependsOn: string[] = [];\n if (db.bundled) {\n // The one-off `migration run` and the services would otherwise race an engine still initializing its volume.\n dependsOn.push(`${db.type}:`, ' condition: service_healthy');\n }\n if (answers.redis) {\n dependsOn.push('redis:', ' condition: service_started');\n }\n const dependsOnBlock = dependsOn.length > 0 ? block('depends_on', dependsOn) : [];\n\n const authupEnv = [\n env('PUBLIC_URL', answers.publicUrl),\n '# the proxies in front whose X-Forwarded-For is trusted, or false; authup trusts every hop by default',\n env('TRUST_PROXY', trustProxyValue(exposure, answers.consoleSplit)),\n ...dbEnv,\n ref('USER_ADMIN_PASSWORD', 'USER_ADMIN_PASSWORD'),\n ];\n if (answers.smtp) {\n authupEnv.push(ref('SMTP', 'SMTP'));\n }\n if (answers.registrationEnabled) {\n authupEnv.push(env('REGISTRATION_ENABLED', 'true'));\n }\n if (answers.passwordRecoveryEnabled) {\n authupEnv.push(env('PASSWORD_RECOVERY_ENABLED', 'true'));\n }\n if (answers.emailVerificationEnabled) {\n authupEnv.push(env('EMAIL_VERIFICATION_ENABLED', 'true'));\n }\n authupEnv.push(\n '# every downstream application origin, comma-separated; each may obtain a full-permission token',\n '# - \"TRUSTED_ORIGINS=https://app.example.com\"',\n '# wraps the realm key store at rest (base64, 32 bytes); write-once, back it up',\n '# - \"SECRETS_ENCRYPTION_KEY=<base64 32 bytes>\"',\n );\n // A console-only split keeps one API process, which sweeps and migrates itself like a plain `start` does.\n if (answers.workerSplit) {\n authupEnv.push(\n '# the sweeps run in the worker below',\n env('WORKER_ENABLED', 'false'),\n '# run \"docker compose run --rm authup migration run\" once before starting',\n env('MIGRATION_ENABLED', 'false'),\n );\n }\n\n const services = block('authup', [\n image,\n restart,\n // under a console split the proxy below owns the published port\n ...(answers.consoleSplit ? [] : block('ports', [`- \"${exposure.hostPort}:3000\"`])),\n '# a configuration file and a provisioning directory are optional; mount them under /etc/authup',\n '# volumes:',\n '# - ./authup.yml:/etc/authup/authup.yml:ro',\n '# - ./provisioning:/etc/authup/provisioning:ro',\n ...dependsOnBlock,\n ...block('environment', authupEnv),\n `command: ${answers.consoleSplit ? 'start core' : 'start'}`,\n ]);\n\n if (db.bundled) {\n const credentials = db.type === 'postgres' ?\n [\n env('POSTGRES_USER', db.username),\n ref('POSTGRES_PASSWORD', 'DB_PASSWORD'),\n env('POSTGRES_DB', db.database),\n ] :\n [\n env('MYSQL_USER', db.username),\n ref('MYSQL_PASSWORD', 'DB_PASSWORD'),\n ref('MYSQL_ROOT_PASSWORD', 'DB_PASSWORD'),\n env('MYSQL_DATABASE', db.database),\n ];\n\n // Both official images run a temporary, network-less server while initializing an empty volume, so a probe\n // over TCP answers only once the real server is up (pg_isready over the socket would answer too early).\n const probe = db.type === 'postgres' ?\n `test: [\"CMD\", \"pg_isready\", \"-h\", \"localhost\", \"-U\", ${quoteYaml(escapeComposeInterpolation(db.username))}, \"-d\", ${quoteYaml(escapeComposeInterpolation(db.database))}]` :\n // mysqladmin ping exits 0 whenever the server takes the connection, credentials or not\n 'test: [\"CMD\", \"mysqladmin\", \"ping\", \"-h\", \"127.0.0.1\"]';\n\n services.push('', ...block(db.type, [\n `image: ${engine.image}`,\n restart,\n ...block('volumes', [`- ${db.type}_data:${engine.dataPath}`]),\n ...block('environment', credentials),\n ...block('healthcheck', [probe, 'interval: 5s', 'timeout: 5s', 'retries: 24']),\n ]));\n }\n\n if (answers.redis) {\n services.push('', ...block('redis', ['image: redis:7', restart]));\n }\n\n if (answers.workerSplit) {\n services.push(\n '',\n '# one instance is enough, whatever the API scales to',\n ...block('authup-worker', [\n image,\n restart,\n ...dependsOnBlock,\n // the worker needs no url of its own; without one the CLI derives it and warns on every start\n ...block('environment', [env('PUBLIC_URL', answers.publicUrl), ...dbEnv]),\n 'command: start worker',\n ]),\n );\n }\n\n if (answers.consoleSplit) {\n services.push(\n '',\n '# Two proxy rules, in this order: /console/<name>/login/start and',\n '# /console/<name>/callback (admin and account) go to the authup service;',\n '# everything else under /console/** goes here, with the /console/<name>',\n `# prefix stripped. See ${DOCS_URL}/console-replicas`,\n ...block('authup-console', [\n image,\n restart,\n ...block('environment', [\n env('PUBLIC_URL', answers.publicUrl),\n '# the auth console renders server-side and calls the API itself: reach it on this network',\n env('INTERNAL_URL', 'http://authup:3000'),\n ]),\n '# the image probes the API port, which this process never opens; the auth',\n '# console cannot be disabled, so it is always listening (3020 by default)',\n ...block('healthcheck', [\n 'test: [\"CMD\", \"wget\", \"--spider\", \"--proxy\", \"off\", \"http://127.0.0.1:3020/healthy\"]',\n 'interval: 10s',\n ]),\n 'command: start console',\n ]),\n '',\n '# The proxy owns the published port and carries the two console routing rules (nginx.conf).',\n ...block('nginx', [\n 'image: nginx:1.31-alpine',\n restart,\n ...block('ports', [`- \"${exposure.hostPort}:80\"`]),\n ...block('volumes', ['- ./nginx.conf:/etc/nginx/conf.d/default.conf:ro']),\n ...block('depends_on', ['authup:', ' condition: service_started', 'authup-console:', ' condition: service_started']),\n ]),\n );\n }\n\n const compose = [\n `# Written by npm create authup. Secrets live in .env next to this file: ${DOCS_URL}/docker-compose`,\n '',\n ];\n if (db.bundled) {\n compose.push(...block('volumes', [`${db.type}_data:`]), '');\n }\n compose.push(...block('services', services));\n\n const dotenv = [\n '# Secrets docker-compose.yml interpolates. Keep this file out of version control.',\n `DB_PASSWORD=${quoteComposeEnv(db.password)}`,\n `USER_ADMIN_PASSWORD=${quoteComposeEnv(answers.adminPassword)}`,\n ];\n if (answers.smtp) {\n dotenv.push(`SMTP=${quoteComposeEnv(answers.smtp.url)}`);\n }\n dotenv.push(\n '# Wraps the realm key store at rest (base64, 32 bytes). Write-once: back it up.',\n '# SECRETS_ENCRYPTION_KEY=',\n );\n\n return {\n 'docker-compose.yml': `${compose.join('\\n')}\\n`,\n '.env': `${dotenv.join('\\n')}\\n`,\n ...(answers.consoleSplit ? { 'nginx.conf': NGINX_CONF } : {}),\n };\n}\n","/*\n * Copyright (c) 2026.\n * Author Peter Placzek (tada5hi)\n * For the full copyright and license information,\n * view the LICENSE file that was distributed with this source code.\n */\n\nimport type { Renderer, Target } from '../types.ts';\nimport { renderBareMetal } from './bare-metal.ts';\nimport { renderCompose } from './compose.ts';\nimport { renderDocker } from './docker.ts';\nimport { renderHelm } from './helm.ts';\n\nexport const RENDERERS: Record<Target, Renderer> = {\n docker: renderDocker,\n compose: renderCompose,\n helm: renderHelm,\n 'bare-metal': renderBareMetal,\n};\n","/*\n * Copyright (c) 2026.\n * Author Peter Placzek (tada5hi)\n * For the full copyright and license information,\n * view the LICENSE file that was distributed with this source code.\n */\n\nimport { readFileSync } from 'node:fs';\n\n// dist/index.mjs and src/version.ts both sit one level below the package root, so one relative URL serves the built bin and vitest alike.\nexport const VERSION: string = JSON.parse(readFileSync(new URL('../package.json', import.meta.url), 'utf8')).version;\n","#!/usr/bin/env node\n\n/*\n * Copyright (c) 2026.\n * Author Peter Placzek (tada5hi)\n * For the full copyright and license information,\n * view the LICENSE file that was distributed with this source code.\n */\n\n/* eslint-disable no-console */\n\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport process from 'node:process';\nimport { parseArgs } from 'node:util';\nimport { createReadlineAsk } from './ask.ts';\nimport { SECRET_FILES, TARGETS } from './constants.ts';\nimport { describeExposure } from './deployment.ts';\nimport { collectAnswers } from './prompts.ts';\nimport { dockerRunCommand } from './targets/docker.ts';\nimport { HELM_COMMANDS } from './targets/helm.ts';\nimport { RENDERERS } from './targets/index.ts';\nimport type { Answers, Target } from './types.ts';\nimport { VERSION } from './version.ts';\n\nconst DOCS_URL = 'https://authup.org/guide/deployment/';\n\nconst USAGE = `Usage: npx create-authup [--force] [--help]\n npm create authup -- [--force] [--help]\n\nWrites the deployment files for one authup installation into the current directory.\n\nTargets: ${TARGETS.join(', ')}\n\nFlags:\n --force overwrite files that already exist\n --help print this text`;\n\nfunction nextSteps(answers: Answers): string[] {\n const steps: Record<Target, string[]> = {\n docker: [\n dockerRunCommand(VERSION, describeExposure(answers.publicUrl).hostPort),\n 'The database must already exist: the image runs in production mode and refuses sqlite.',\n ],\n compose: [\n ...(answers.workerSplit ? ['docker compose run --rm authup migration run'] : []),\n 'docker compose up -d',\n ...(answers.consoleSplit ? ['The nginx service publishes the port; TLS, if any, terminates in front of it.'] : []),\n ],\n helm: [...HELM_COMMANDS],\n 'bare-metal': ['npm install', 'npx authup config validate', 'npm start'],\n };\n\n return steps[answers.target];\n}\n\nasync function main(): Promise<void> {\n const { values } = parseArgs({\n options: {\n force: { type: 'boolean', default: false },\n help: { type: 'boolean', default: false },\n },\n allowPositionals: false,\n });\n if (values.help) {\n console.log(USAGE);\n return;\n }\n\n console.log(`create-authup ${VERSION}`);\n console.log(`Files are written into the current directory (${process.cwd()}).`);\n\n const { ask, close } = createReadlineAsk();\n let collected: Awaited<ReturnType<typeof collectAnswers>>;\n try {\n collected = await collectAnswers(ask);\n } finally {\n close();\n }\n const { answers, notes } = collected;\n\n const rendered = RENDERERS[answers.target](answers, VERSION);\n const files = Object.keys(rendered);\n const directories = files.filter((name) => {\n try {\n return fs.statSync(path.join(process.cwd(), name)).isDirectory();\n } catch {\n return false;\n }\n });\n if (directories.length > 0) {\n console.error(`Cannot write over a directory: ${directories.join(', ')}.`);\n process.exit(1);\n }\n\n const existing = files.filter((name) => fs.existsSync(path.join(process.cwd(), name)));\n if (existing.length > 0 && !values.force) {\n console.error(`Refusing to overwrite: ${existing.join(', ')}. Re-run with --force.`);\n process.exit(1);\n }\n\n for (const name of files) {\n const target = path.join(process.cwd(), name);\n const secret = SECRET_FILES.has(name);\n fs.writeFileSync(target, rendered[name], secret ? { encoding: 'utf8', mode: 0o600 } : 'utf8');\n if (secret) {\n // writeFileSync's mode applies to a file it creates, so an overwrite keeps whatever the old one had\n fs.chmodSync(target, 0o600);\n }\n }\n\n for (const note of notes) {\n console.log(note);\n }\n console.log(`Written: ${files.join(', ')}`);\n console.log('Next steps:');\n for (const step of nextSteps(answers)) {\n console.log(` ${step}`);\n }\n console.log(`Docs: ${DOCS_URL}`);\n}\n\nmain().catch((e) => {\n console.error(e instanceof Error ? e.message : String(e));\n process.exit(1);\n});\n\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":["DOCS_URL"],"sources":["../src/ask.ts","../src/constants.ts","../src/deployment.ts","../src/utils.ts","../src/prompts.ts","../src/targets/docker.ts","../src/targets/helm.ts","../src/targets/bare-metal.ts","../src/targets/compose.ts","../src/targets/index.ts","../src/version.ts","../src/index.ts"],"sourcesContent":["/*\n * Copyright (c) 2026.\n * Author Peter Placzek (tada5hi)\n * For the full copyright and license information,\n * view the LICENSE file that was distributed with this source code.\n */\n\nimport process from 'node:process';\nimport { createInterface } from 'node:readline/promises';\nimport { Writable } from 'node:stream';\nimport type { ReadlineAskContext, ReadlineAskResult } from './types.ts';\n\ntype PendingLine = {\n resolve: (line: string) => void,\n reject: (error: Error) => void\n};\n\nconst CLOSED_MESSAGE = 'Input ended before every question was answered.';\n\n/**\n * The readline seam. The streams are arguments rather than `process.stdin` / `process.stdout` reached for directly,\n * so the echo behaviour below is testable without a terminal: a spec passes a pair of in-memory streams claiming to\n * be one.\n */\nexport function createReadlineAsk(ctx: ReadlineAskContext = {}): ReadlineAskResult {\n const input = ctx.input || process.stdin;\n const target = ctx.output || process.stdout;\n\n // On a terminal readline echoes what is typed through the output stream itself, so muting that stream while a\n // secret's line is read is what keeps the password off the screen. Piped input takes none of this path.\n const terminal = Boolean(input.isTTY && target.isTTY);\n let muted = false;\n const output = new Writable({\n write(chunk, _encoding, callback) {\n if (!muted) {\n target.write(chunk);\n }\n callback();\n },\n });\n const rl = createInterface({\n input,\n output,\n terminal,\n });\n\n // readline emits every line of one stdin chunk synchronously, while the next question is only registered on a\n // later microtask, so `rl.question` drops all but the first answer of a piped script. The lines are buffered\n // instead, and a close with a question still open is an error rather than the silent exit 0 node answers with.\n const lines: string[] = [];\n let pending: PendingLine | undefined;\n let closed = false;\n\n rl.on('line', (line) => {\n if (pending) {\n const { resolve } = pending;\n pending = undefined;\n resolve(line);\n return;\n }\n\n lines.push(line);\n });\n\n rl.on('close', () => {\n closed = true;\n if (pending) {\n const { reject } = pending;\n pending = undefined;\n reject(new Error(CLOSED_MESSAGE));\n }\n });\n\n // Ctrl+C on a TTY reaches readline rather than the process; without this it closes the interface and reads as EOF.\n rl.on('SIGINT', () => process.exit(130));\n\n function readLine(): Promise<{ line: string, buffered: boolean }> {\n const buffered = lines.shift();\n if (buffered !== undefined) {\n return Promise.resolve({ line: buffered, buffered: true });\n }\n\n if (closed) {\n return Promise.reject(new Error(CLOSED_MESSAGE));\n }\n\n return new Promise((resolve, reject) => {\n pending = { resolve: (line) => resolve({ line, buffered: false }), reject };\n });\n }\n\n return {\n // The answer is handed over untrimmed: a secret keeps its whitespace, and each parser trims what it should.\n ask: async (question, fallback, options) => {\n rl.setPrompt(fallback === undefined ? `${question}: ` : `${question} [${fallback}]: `);\n rl.prompt();\n const secret = terminal && Boolean(options?.secret);\n muted = secret;\n try {\n const { line, buffered } = await readLine();\n if (buffered && terminal && !secret) {\n // a type-ahead answer never echoed after this prompt, so the next prompt would overwrite the line\n target.write(`${line}\\n`);\n }\n\n return fallback !== undefined && line.trim() === '' ? fallback : line;\n } finally {\n if (secret) {\n // the Enter was swallowed with the echo\n muted = false;\n target.write('\\n');\n }\n }\n },\n close: () => rl.close(),\n };\n}\n","/*\n * Copyright (c) 2026.\n * Author Peter Placzek (tada5hi)\n * For the full copyright and license information,\n * view the LICENSE file that was distributed with this source code.\n */\n\nimport type { Target } from './types.ts';\n\n/**\n * The emitted files that hold a password, written owner-only by `src/index.ts`. The compose file, `nginx.conf`,\n * `package.json` and `authup.yml` carry none: every secret rides an `.env`, or `values.yaml` for helm.\n * `test/unit/secret-files.spec.ts` holds the renderers to this list, so a new secret-bearing file cannot slip past it.\n */\nexport const SECRET_FILES = new Set(['.env', 'authup.env', 'values.yaml']);\n\nexport const TARGETS: readonly Target[] = ['docker', 'compose', 'helm', 'bare-metal'] as const;\n","/*\n * Copyright (c) 2026.\n * Author Peter Placzek (tada5hi)\n * For the full copyright and license information,\n * view the LICENSE file that was distributed with this source code.\n */\n\nimport type { Exposure } from './types.ts';\n\n/** The port the process listens on inside a container, and the default everywhere else. */\nexport const LISTEN_PORT = 3000;\n\n// A public url with an explicit port is dialed directly, so that port is the one to publish or to listen on. A\n// default port, or https, means a reverse proxy in front owns the port a browser dials and reaches the listener\n// on its own, so the default listen port stays. `http://localhost` with no port is read as a proxy too; the\n// operator who really serves port 80 directly edits the mapping.\nexport function describeExposure(publicUrl: string): Exposure {\n const url = new URL(publicUrl);\n const behindProxy = url.protocol === 'https:' || url.port === '';\n\n return {\n behindProxy,\n hostPort: behindProxy ? LISTEN_PORT : Number(url.port),\n };\n}\n\n// The value core.trustProxy / TRUST_PROXY takes: the number of proxy hops the deployment itself knows about\n// (the operator's proxy in front, plus the one the compose console split emits), or false when nothing is in front,\n// since authup's default trusts every hop and lets any direct client spoof its address.\nexport function trustProxyValue(exposure: Exposure, ownProxy = false): string {\n const hops = (exposure.behindProxy ? 1 : 0) + (ownProxy ? 1 : 0);\n\n return hops > 0 ? String(hops) : 'false';\n}\n","/*\n * Copyright (c) 2026.\n * Author Peter Placzek (tada5hi)\n * For the full copyright and license information,\n * view the LICENSE file that was distributed with this source code.\n */\n\n// A JSON string is a valid YAML double-quoted scalar, which also defuses the Norway problem and a leading `*`.\nexport function quoteYaml(value: string): string {\n return JSON.stringify(value);\n}\n\nconst PLAIN = /^[A-Za-z0-9_@:/.\\-+=%?&,]*$/;\n\n// Whether a value survives the .env round trip at all. dotenv strips the surrounding quotes and, inside DOUBLE quotes,\n// turns a literal \\n or \\r into the control character itself, with no escape that survives (it never unescapes a\n// doubled backslash). A value carrying a single quote, which rules the single-quoted form out, together with one of\n// those two sequences therefore has no representation, and `askSecret` refuses it rather than corrupting a password.\nexport function isEnvRepresentable(value: string): boolean {\n return !value.includes('\\'') || !/\\\\[nr]/.test(value);\n}\n\n// For the .env dotenv reads (`authup start` loads it from cwd): a value is single-quoted when it carries no single\n// quote and written raw inside double quotes otherwise, which round-trips everything `isEnvRepresentable` admits.\nexport function quoteEnv(value: string): string {\n if (PLAIN.test(value)) {\n return value;\n }\n\n if (!value.includes('\\'')) {\n return `'${value}'`;\n }\n\n return `\"${value}\"`;\n}\n\n// For the .env compose-go reads: double quotes take backslash escapes and `$` interpolates unless doubled, while a\n// single-quoted `\\'` there is an escaped quote rather than a literal, so one double-quoted branch covers every value.\nexport function quoteComposeEnv(value: string): string {\n if (PLAIN.test(value)) {\n return value;\n }\n\n return `\"${escapeComposeInterpolation(value.replace(/[\\\\\"]/g, '\\\\$&'))}\"`;\n}\n\n// compose interpolates `$name` and `${name}` in every yaml value and .env value; `$$` is its literal dollar.\nexport function escapeComposeInterpolation(value: string): string {\n return value.replace(/\\$/g, '$$$$');\n}\n\nexport function indent(text: string, spaces: number): string {\n const prefix = ' '.repeat(spaces);\n\n return text\n .split('\\n')\n .map((line) => (line.length > 0 ? prefix + line : line))\n .join('\\n');\n}\n","/*\n * Copyright (c) 2026.\n * Author Peter Placzek (tada5hi)\n * For the full copyright and license information,\n * view the LICENSE file that was distributed with this source code.\n */\n\nimport { TARGETS } from './constants.ts';\nimport { isEnvRepresentable } from './utils.ts';\nimport type {\n Answers,\n Ask,\n AskOptions,\n Database,\n Target,\n} from './types.ts';\n\ntype Parsed<T> = { value: T } | { error: string };\n\nconst ENGINE_PORTS = { postgres: 5432, mysql: 3306 } as const;\nconst BUNDLED_REDIS_URL = 'redis://redis:6379';\nconst UNREPRESENTABLE = 'A single quote together with a \\\\n or \\\\r sequence has no .env representation. Choose another value.';\nconst REDIS_NOTE = 'Redis is required for a split deployment: the console sign-in and the token blocklist ride the cache.';\n\n// A refused answer re-asks with the reason on the line above, so the flow stays pure over `ask`.\nasync function askUntil<T>(\n ask: Ask,\n question: string,\n fallback: string | undefined,\n parse: (answer: string) => Parsed<T>,\n options?: AskOptions,\n): Promise<T> {\n let prefix = '';\n\n for (;;) {\n const parsed = parse(await ask(prefix + question, fallback, options));\n if ('value' in parsed) {\n return parsed.value;\n }\n\n prefix = `${parsed.error}\\n`;\n }\n}\n\nfunction askChoice<T extends string>(ask: Ask, question: string, options: readonly T[], fallback: T): Promise<T> {\n const listed = options.map((option, index) => `${index + 1}: ${option}`).join(', ');\n\n return askUntil<T>(ask, `${question} (${listed})`, fallback, (answer) => {\n const normalized = answer.trim().toLowerCase();\n if ((options as readonly string[]).includes(normalized)) {\n return { value: normalized as T };\n }\n\n const numbered = options[Number(normalized) - 1];\n if (numbered !== undefined) {\n return { value: numbered };\n }\n\n return { error: `Pick one of ${options.join(', ')}, by name or number.` };\n });\n}\n\nfunction askYesNo(ask: Ask, question: string, fallback: boolean): Promise<boolean> {\n return askUntil<boolean>(ask, `${question} (y/n)`, fallback ? 'y' : 'n', (answer) => {\n const normalized = answer.trim().toLowerCase();\n if (normalized === 'y' || normalized === 'yes') {\n return { value: true };\n }\n if (normalized === 'n' || normalized === 'no') {\n return { value: false };\n }\n\n return { error: 'Answer y or n.' };\n });\n}\n\nfunction askUrl(ask: Ask, question: string, protocols: readonly string[] = ['http:', 'https:'], fallback?: string): Promise<string> {\n return askUntil<string>(ask, question, fallback, (answer) => {\n const trimmed = answer.trim();\n try {\n // a non-special scheme parses `smtp:mail` with an empty host, which would land in the file as written\n const url = new URL(trimmed);\n if (protocols.includes(url.protocol) && url.hostname !== '') {\n return { value: trimmed };\n }\n } catch {\n // falls through to the refusal\n }\n\n return { error: `Enter an absolute URL using ${protocols.map((protocol) => `${protocol}//`).join(' or ')}.` };\n });\n}\n\n// A free-text answer that must not be blank; trimmed, unlike a secret.\nfunction askText(ask: Ask, question: string, fallback?: string): Promise<string> {\n return askUntil<string>(ask, question, fallback, (answer) => {\n const trimmed = answer.trim();\n if (trimmed === '') {\n return { error: 'A value is required.' };\n }\n\n // a host, user or database name reaches the same .env the secrets do\n return isEnvRepresentable(trimmed) ? { value: trimmed } : { error: UNREPRESENTABLE };\n });\n}\n\n// The issuer: no credentials, query or fragment (OIDC Core 3.1.2.1), and for the helm target no path either, since\n// the chart publishes authup at the origin root and refuses a console split under a sub-path.\nfunction askPublicUrl(ask: Ask, target: Target): Promise<string> {\n return askUntil<string>(\n ask,\n 'Public URL: the address a browser reaches authup at. It is the OIDC issuer, and every console url derives from it',\n undefined,\n (answer) => {\n const trimmed = answer.trim();\n let url: URL;\n try {\n url = new URL(trimmed);\n } catch {\n return { error: 'Enter an absolute URL using http:// or https://.' };\n }\n\n if (url.protocol !== 'http:' && url.protocol !== 'https:') {\n return { error: 'Enter an absolute URL using http:// or https://.' };\n }\n if (url.username || url.password || url.search || url.hash) {\n return { error: 'The public URL is the OIDC issuer: no credentials, query or fragment.' };\n }\n if (target === 'helm' && url.pathname !== '/') {\n return { error: 'The helm chart publishes authup at the origin root: enter the URL without a path.' };\n }\n\n return { value: trimmed };\n },\n );\n}\n\nfunction askPort(ask: Ask, question: string, fallback: number): Promise<number> {\n return askUntil<number>(ask, question, String(fallback), (answer) => {\n const port = Number(answer.trim());\n if (Number.isSafeInteger(port) && port >= 1 && port <= 65535) {\n return { value: port };\n }\n\n return { error: 'Enter a port between 1 and 65535.' };\n });\n}\n\nfunction askSecret(ask: Ask, question: string): Promise<string> {\n return askUntil<string>(ask, question, undefined, (answer) => {\n if (answer.trim() === '') {\n return { error: 'A value is required.' };\n }\n if (answer === 'start123') {\n return { error: 'start123 is the provisioning default the wizard refuses to emit. Choose another value.' };\n }\n // the bounds the config registry declares for userAdminPassword\n if (answer.length < 3 || answer.length > 256) {\n return { error: 'Use between 3 and 256 characters.' };\n }\n if (!isEnvRepresentable(answer)) {\n return { error: UNREPRESENTABLE };\n }\n\n return { value: answer };\n }, { secret: true });\n}\n\nasync function askDatabase(ask: Ask, target: Target): Promise<Database> {\n const options: readonly ('postgres' | 'mysql' | 'sqlite')[] = target === 'bare-metal' ?\n ['postgres', 'mysql', 'sqlite'] :\n ['postgres', 'mysql'];\n const type = await askChoice(ask, 'Database', options, 'postgres');\n if (type === 'sqlite') {\n return { type: 'better-sqlite3' };\n }\n\n const bundled = (target === 'compose' || target === 'helm') &&\n await askYesNo(ask, 'Run the database alongside authup?', true);\n if (bundled) {\n return {\n type,\n bundled: true,\n host: type,\n port: ENGINE_PORTS[type],\n username: 'authup',\n password: await askSecret(ask, 'Database password'),\n database: 'authup',\n };\n }\n\n // 127.0.0.1 is the operator's machine on bare metal and the container itself everywhere else\n const hostQuestion = target === 'bare-metal' ?\n 'Database host' :\n 'Database host (a name the container resolves; 127.0.0.1 would be the container itself)';\n\n return {\n type,\n bundled: false,\n host: await askText(ask, hostQuestion, target === 'bare-metal' ? '127.0.0.1' : undefined),\n port: await askPort(ask, 'Database port', ENGINE_PORTS[type]),\n username: await askText(ask, 'Database username', 'authup'),\n password: await askSecret(ask, 'Database password'),\n database: await askText(ask, 'Database name', 'authup'),\n };\n}\n\nexport async function collectAnswers(ask: Ask): Promise<{ answers: Answers, notes: string[] }> {\n const target = await askChoice(ask, 'Deployment target', TARGETS, 'compose');\n const publicUrl = await askPublicUrl(ask, target);\n const tlsCertManager = target === 'helm' && publicUrl.startsWith('https:') &&\n await askYesNo(ask, 'Request the TLS certificate from cert-manager? The alternative is a <hostname>-tls secret in the release namespace', false);\n const db = await askDatabase(ask, target);\n\n const registrationEnabled = await askYesNo(ask, 'Enable user registration?', false);\n const passwordRecoveryEnabled = await askYesNo(ask, 'Enable password recovery?', false);\n const smtp = registrationEnabled || passwordRecoveryEnabled ?\n { url: await askUrl(ask, 'SMTP connection URL (smtp(s)://user:pass@host:port)', ['smtp:', 'smtps:']) } :\n false;\n\n const emailVerificationEnabled = registrationEnabled && await askYesNo(ask, 'Verify email addresses on registration?', true);\n\n const adminPassword = await askSecret(ask, 'Admin password');\n\n const splittable = target === 'compose' || target === 'helm';\n const workerSplit = splittable && await askYesNo(ask, 'Run the background worker as a separate service?', false);\n const consoleSplit = splittable && await askYesNo(ask, 'Run the consoles as a separate service?', false);\n\n const notes: string[] = [];\n let redis: Answers['redis'] = false;\n if (workerSplit || consoleSplit) {\n redis = { url: BUNDLED_REDIS_URL };\n notes.push(REDIS_NOTE);\n } else if (await askYesNo(ask, 'Use redis?', false)) {\n redis = {\n url: splittable ?\n BUNDLED_REDIS_URL :\n await askUrl(\n ask,\n target === 'bare-metal' ? 'Redis URL' : 'Redis URL (a host the container resolves)',\n ['redis:', 'rediss:'],\n target === 'bare-metal' ? 'redis://127.0.0.1:6379' : undefined,\n ),\n };\n }\n\n return {\n answers: {\n target,\n publicUrl,\n db,\n redis,\n smtp,\n registrationEnabled,\n passwordRecoveryEnabled,\n emailVerificationEnabled,\n adminPassword,\n workerSplit,\n consoleSplit,\n tlsCertManager,\n },\n notes,\n };\n}\n","/*\n * Copyright (c) 2026.\n * Author Peter Placzek (tada5hi)\n * For the full copyright and license information,\n * view the LICENSE file that was distributed with this source code.\n */\n\nimport { describeExposure, trustProxyValue } from '../deployment.ts';\nimport type { Answers, Rendered } from '../types.ts';\n\nexport function dockerRunCommand(version: string, hostPort: number): string {\n return `docker run -d --name authup --restart unless-stopped --env-file authup.env -p ${hostPort}:3000 authup/authup:${version} start`;\n}\n\nexport function renderDocker(answers: Answers, version: string): Rendered {\n if (answers.db.type === 'better-sqlite3') {\n throw new Error('The docker image runs in production mode and refuses sqlite: configure a postgres or mysql database.');\n }\n\n if (answers.workerSplit || answers.consoleSplit) {\n throw new Error('The docker run target is one container: a worker or console split needs the compose or helm target.');\n }\n\n const exposure = describeExposure(answers.publicUrl);\n\n // `docker run --env-file` reads KEY=value verbatim and does no quote handling, so values are written raw rather than through quoteEnv.\n const lines = [\n `PUBLIC_URL=${answers.publicUrl}`,\n '# The proxies in front whose X-Forwarded-For is trusted, or false; authup trusts every hop by default.',\n `TRUST_PROXY=${trustProxyValue(exposure)}`,\n `DB_TYPE=${answers.db.type}`,\n `DB_HOST=${answers.db.host}`,\n `DB_PORT=${answers.db.port}`,\n `DB_USERNAME=${answers.db.username}`,\n `DB_PASSWORD=${answers.db.password}`,\n `DB_DATABASE=${answers.db.database}`,\n ];\n\n if (answers.redis) {\n lines.push(`REDIS=${answers.redis.url}`);\n }\n\n if (answers.smtp) {\n lines.push(`SMTP=${answers.smtp.url}`);\n }\n\n if (answers.registrationEnabled) {\n lines.push('REGISTRATION_ENABLED=true');\n }\n\n if (answers.passwordRecoveryEnabled) {\n lines.push('PASSWORD_RECOVERY_ENABLED=true');\n }\n\n if (answers.emailVerificationEnabled) {\n lines.push('EMAIL_VERIFICATION_ENABLED=true');\n }\n\n lines.push(\n `USER_ADMIN_PASSWORD=${answers.adminPassword}`,\n '# Every downstream application origin, comma-separated; each may obtain a full-permission token.',\n '# TRUSTED_ORIGINS=https://app.example.com',\n '# Wraps the realm key store at rest (base64, 32 bytes). Write-once: back it up.',\n '# SECRETS_ENCRYPTION_KEY=<base64 32 bytes>',\n );\n\n return {\n 'authup.env': [\n '# Environment of the authup container. It holds secrets: keep it out of version control.',\n `# ${dockerRunCommand(version, exposure.hostPort)}`,\n '# The database must exist already; the image runs in production mode and refuses sqlite.',\n '# A configuration file or a provisioning directory is mounted with',\n '# -v ./authup.yml:/etc/authup/authup.yml:ro -v ./provisioning:/etc/authup/provisioning:ro',\n '# https://authup.org/guide/deployment/docker',\n ...lines,\n '',\n ].join('\\n'),\n };\n}\n","/*\n * Copyright (c) 2026.\n * Author Peter Placzek (tada5hi)\n * For the full copyright and license information,\n * view the LICENSE file that was distributed with this source code.\n */\n\nimport type { Answers, Rendered } from '../types.ts';\nimport { quoteYaml } from '../utils.ts';\n\n// The chart version, not the authup version: it is what `helm install` selects, and 0.4.0 is the first that carries\n// worker.* and server.splitConsoles (0.3.0 rejects both through its values schema). Every document names worker.enabled,\n// so every document needs it.\nexport const CHART_REQUIREMENT = '# This file needs the authup helm chart 0.4.0 or later.';\n\nexport const HELM_COMMANDS: string[] = [\n 'helm repo add authup https://helm.authup.org',\n 'helm install authup authup/authup -f values.yaml',\n];\n\nexport function renderHelm(answers: Answers, _version: string): Rendered {\n const { db } = answers;\n if (db.type === 'better-sqlite3') {\n throw new Error('The helm chart needs a server database (postgres or mysql); the image refuses sqlite.');\n }\n\n const url = new URL(answers.publicUrl);\n const tls = url.protocol === 'https:';\n const lines: string[] = [\n `# Written by npm create authup. Install with: ${HELM_COMMANDS.join(' && ')}`,\n '# Secrets in this file (auth.adminPassword, the database password, smtp.connectionString) can move to the chart\\'s existingSecret keys; see the chart README.',\n // worker.* arrived with the chart that tracks v1.0.0-beta.64 (authup/helm PR #29); the released 0.3.0 rejects it\n // through its values schema.\n CHART_REQUIREMENT,\n 'server:',\n ` publicUrl: ${quoteYaml(answers.publicUrl)}`,\n ...(tls && !answers.tlsCertManager ?\n [` # TLS needs a secret named ${url.hostname}-tls in the release namespace, or set server.ingress.certManager: true.`] :\n []),\n ' ingress:',\n ' enabled: true',\n ` hostname: ${quoteYaml(url.hostname)}`,\n ` tls: ${tls}`,\n ...(tls && answers.tlsCertManager ? [' certManager: true'] : []),\n ];\n\n if (answers.registrationEnabled || answers.passwordRecoveryEnabled || answers.emailVerificationEnabled) {\n lines.push(' features:');\n if (answers.registrationEnabled) {\n lines.push(' registration: true');\n }\n if (answers.passwordRecoveryEnabled) {\n lines.push(' passwordRecovery: true');\n }\n if (answers.emailVerificationEnabled) {\n lines.push(' emailVerification: true');\n }\n }\n\n if (answers.workerSplit) {\n lines.push(' migration:', ' enabled: true');\n }\n\n if (answers.consoleSplit) {\n lines.push(' splitConsoles: true');\n }\n\n lines.push(\n ' # Every downstream application origin; each may obtain a full-permission token.',\n ' # trustedOrigins:',\n ' # - https://app.example.com',\n ' # An authup.yml the chart mounts, for the options that only the file can carry.',\n ' # configuration: |',\n ' # core:',\n ' # passwordMinLength: 12',\n );\n\n // Always spelled out: the chart's default flipped to a dedicated worker in\n // 0.5.0, so leaving the key out would split a deployment that asked not to.\n lines.push('', 'worker:', ` enabled: ${answers.workerSplit}`);\n\n lines.push(\n '',\n 'database:',\n ` type: ${db.type}`,\n '',\n );\n\n // The bundled engine takes the answered password; left empty, the chart would generate one and drop the answer.\n for (const engine of ['postgresql', 'mysql'] as const) {\n const enabled = db.bundled && db.type === (engine === 'postgresql' ? 'postgres' : 'mysql');\n lines.push(`${engine}:`, ` enabled: ${enabled}`);\n if (enabled) {\n lines.push(' auth:', ` password: ${quoteYaml(db.password)}`);\n }\n }\n\n if (!db.bundled) {\n // The chart schema types the port as a string (\"\" = engine default), so a bare integer fails helm's validation.\n lines.push(\n 'externalDatabase:',\n ` host: ${quoteYaml(db.host)}`,\n ` port: ${quoteYaml(String(db.port))}`,\n ` user: ${quoteYaml(db.username)}`,\n ` database: ${quoteYaml(db.database)}`,\n ` password: ${quoteYaml(db.password)}`,\n );\n }\n\n if (answers.redis) {\n lines.push(\n '',\n 'valkey:',\n ' enabled: true',\n '# For an external Redis set valkey.enabled to false and externalRedis.url to its redis:// URL instead.',\n );\n }\n\n lines.push(\n '',\n 'auth:',\n ` adminPassword: ${quoteYaml(answers.adminPassword)}`,\n ' # Wraps the realm key store at rest (base64, 32 bytes). Write-once: back it up.',\n ' # secretsEncryptionKey: <base64 32 bytes>',\n );\n\n if (answers.smtp) {\n lines.push('', 'smtp:', ` connectionString: ${quoteYaml(answers.smtp.url)}`);\n }\n\n return { 'values.yaml': `${lines.join('\\n')}\\n` };\n}\n","/*\n * Copyright (c) 2026.\n * Author Peter Placzek (tada5hi)\n * For the full copyright and license information,\n * view the LICENSE file that was distributed with this source code.\n */\n\nimport { LISTEN_PORT, describeExposure, trustProxyValue } from '../deployment.ts';\nimport type { Answers, Rendered } from '../types.ts';\nimport { quoteEnv, quoteYaml } from '../utils.ts';\n\nconst SCHEMA_COMMENT = '# yaml-language-server: $schema=https://authup.org/schema/config.json';\nconst DATABASE_DOCS_URL = 'https://authup.org/guide/deployment/configuration-server-core-database';\n\nexport function renderBareMetal(answers: Answers, version: string): Rendered {\n if (answers.workerSplit || answers.consoleSplit) {\n throw new Error('The bare-metal target runs one process: a worker or console split is a second process under a process manager, which the wizard does not write.');\n }\n\n return {\n 'package.json': renderPackageJson(version),\n 'authup.yml': renderConfig(answers),\n '.env': renderEnv(answers),\n };\n}\n\nfunction renderPackageJson(version: string): string {\n const manifest = {\n name: 'authup-deployment',\n private: true,\n type: 'module',\n scripts: { start: 'authup start' },\n // exact, like the image tag the container targets write: the wizard scaffolds one release, not a range\n dependencies: { authup: version },\n };\n\n return `${JSON.stringify(manifest, null, 4)}\\n`;\n}\n\nfunction renderConfig(answers: Answers): string {\n const { db } = answers;\n const lines = [\n SCHEMA_COMMENT,\n `publicUrl: ${quoteYaml(answers.publicUrl)}`,\n ];\n\n if (db.type === 'better-sqlite3') {\n lines.push(\n '# Production refuses sqlite, so this file keeps the development default of env.',\n `# That is fine for a local trial only: ${DATABASE_DOCS_URL}`,\n '',\n '# db.sqlite is written into this directory.',\n );\n } else {\n lines.push(\n 'env: production',\n '',\n // A `db` block here would be used AS IS: server-core never merges DB_* over it, so the password could not\n // stay in .env. The whole connection rides the DB_* variables there instead (the documented env form).\n '# The database connection is the DB_* set in .env.',\n );\n }\n\n if (answers.redis) {\n lines.push('', `redis: ${quoteYaml(answers.redis.url)}`);\n }\n\n if (answers.smtp) {\n lines.push('', '# The connection URL is SMTP in .env.', 'smtp: true');\n }\n\n lines.push(\n '',\n '# Every downstream application origin; each may obtain a full-permission token.',\n '# trustedOrigins:',\n '# - https://app.example.com',\n );\n\n // The process listens where the url says when nothing is in front; behind a proxy the default stays.\n const exposure = describeExposure(answers.publicUrl);\n lines.push(\n '',\n 'core:',\n ...(exposure.hostPort !== LISTEN_PORT ? [` port: ${exposure.hostPort}`] : []),\n ' # The proxies in front whose X-Forwarded-For is trusted, or false; authup trusts every hop by default.',\n ` trustProxy: ${trustProxyValue(exposure)}`,\n ...(answers.registrationEnabled ? [' registrationEnabled: true'] : []),\n ...(answers.passwordRecoveryEnabled ? [' passwordRecoveryEnabled: true'] : []),\n ...(answers.emailVerificationEnabled ? [' emailVerificationEnabled: true'] : []),\n );\n\n return `${lines.join('\\n')}\\n`;\n}\n\nfunction renderEnv(answers: Answers): string {\n const lines = ['# Secrets. authup start reads this file from the directory it is started in. Keep it out of version control.'];\n\n const { db } = answers;\n if (db.type !== 'better-sqlite3') {\n lines.push(\n `DB_TYPE=${db.type}`,\n `DB_HOST=${quoteEnv(db.host)}`,\n `DB_PORT=${db.port}`,\n `DB_USERNAME=${quoteEnv(db.username)}`,\n `DB_PASSWORD=${quoteEnv(db.password)}`,\n `DB_DATABASE=${quoteEnv(db.database)}`,\n );\n }\n\n lines.push(`USER_ADMIN_PASSWORD=${quoteEnv(answers.adminPassword)}`);\n\n if (answers.smtp) {\n lines.push(`SMTP=${quoteEnv(answers.smtp.url)}`);\n }\n\n lines.push(\n '# Wraps the realm key store at rest (base64, 32 bytes). Write-once: back it up.',\n '# SECRETS_ENCRYPTION_KEY=',\n );\n\n return `${lines.join('\\n')}\\n`;\n}\n","/*\n * Copyright (c) 2026.\n * Author Peter Placzek (tada5hi)\n * For the full copyright and license information,\n * view the LICENSE file that was distributed with this source code.\n */\n\nimport { describeExposure, trustProxyValue } from '../deployment.ts';\nimport type { Answers, Rendered } from '../types.ts';\nimport {\n escapeComposeInterpolation,\n indent,\n quoteComposeEnv,\n quoteYaml,\n} from '../utils.ts';\n\nconst DOCS_URL = 'https://authup.org/guide/deployment';\n\nconst NGINX_CONF = `# Written by npm create authup: the console split's routing, ${DOCS_URL}/console-replicas\n# TLS, if any, terminates in front of this proxy; X-Forwarded-Proto is passed through as the scheme it sees.\nupstream authup_api { server authup:3000; }\nupstream authup_auth_console { server authup-console:3020; }\nupstream authup_admin_console { server authup-console:3021; }\nupstream authup_account_console { server authup-console:3022; }\n\nserver {\n listen 80;\n # nginx listens on 80 inside the container while the published port is another one: a redirect it builds itself\n # (the trailing slash it adds to /console/<name>) must stay relative or it would send the browser to port 80.\n absolute_redirect off;\n\n # The sign-in pair stays on the API. Exact matches, so they win over the prefix rules below.\n location = /console/admin/login/start { proxy_pass http://authup_api; }\n location = /console/admin/callback { proxy_pass http://authup_api; }\n location = /console/account/login/start { proxy_pass http://authup_api; }\n location = /console/account/callback { proxy_pass http://authup_api; }\n\n location /console/auth/ {\n rewrite ^/console/auth(/.*)$ $1 break;\n proxy_set_header Host $host;\n proxy_set_header X-Forwarded-Proto $scheme;\n proxy_set_header X-Forwarded-Tls-Client-Cert \"\";\n proxy_pass http://authup_auth_console;\n }\n\n location /console/admin/ {\n rewrite ^/console/admin(/.*)$ $1 break;\n proxy_set_header Host $host;\n proxy_set_header X-Forwarded-Proto $scheme;\n proxy_set_header X-Forwarded-Tls-Client-Cert \"\";\n proxy_pass http://authup_admin_console;\n }\n\n location /console/account/ {\n rewrite ^/console/account(/.*)$ $1 break;\n proxy_set_header Host $host;\n proxy_set_header X-Forwarded-Proto $scheme;\n proxy_set_header X-Forwarded-Tls-Client-Cert \"\";\n proxy_pass http://authup_account_console;\n }\n\n location / {\n proxy_set_header Host $host;\n proxy_set_header X-Real-IP $remote_addr;\n proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\n proxy_set_header X-Forwarded-Proto $scheme;\n proxy_set_header X-Forwarded-Tls-Client-Cert \"\";\n proxy_pass http://authup_api;\n }\n}\n`;\n\n\nconst ENGINES = {\n postgres: {\n image: 'postgres:17',\n port: 5432,\n dataPath: '/var/lib/postgresql/data',\n },\n mysql: {\n image: 'mysql:8.4',\n port: 3306,\n dataPath: '/var/lib/mysql',\n },\n} as const;\n\n// One list entry of a compose `environment` block; the whole KEY=value is one YAML scalar, so quoting it covers the\n// value, and an operator's `$` is doubled so compose does not interpolate it away.\nfunction env(key: string, value: string): string {\n return `- ${quoteYaml(`${key}=${escapeComposeInterpolation(value)}`)}`;\n}\n\n// The same entry carrying a deliberate `${VARIABLE}` reference into .env.\nfunction ref(key: string, variable: string): string {\n return `- ${quoteYaml(`${key}=\\${${variable}}`)}`;\n}\n\nfunction block(key: string, lines: string[]): string[] {\n return [`${key}:`, ...indent(lines.join('\\n'), 2).split('\\n')];\n}\n\nexport function renderCompose(answers: Answers, version: string): Rendered {\n const { db } = answers;\n if (db.type === 'better-sqlite3') {\n throw new Error('The compose target needs a server database (postgres or mysql): the image runs in production mode and refuses sqlite.');\n }\n\n const image = `image: authup/authup:${version}`;\n const restart = 'restart: unless-stopped';\n const engine = ENGINES[db.type];\n const exposure = describeExposure(answers.publicUrl);\n\n const dbEnv = [\n env('DB_TYPE', db.type),\n env('DB_HOST', db.bundled ? db.type : db.host),\n env('DB_PORT', String(db.bundled ? engine.port : db.port)),\n env('DB_USERNAME', db.username),\n ref('DB_PASSWORD', 'DB_PASSWORD'),\n env('DB_DATABASE', db.database),\n ];\n if (answers.redis) {\n dbEnv.push(env('REDIS', 'redis://redis:6379'));\n }\n\n const dependsOn: string[] = [];\n if (db.bundled) {\n // The one-off `migration run` and the services would otherwise race an engine still initializing its volume.\n dependsOn.push(`${db.type}:`, ' condition: service_healthy');\n }\n if (answers.redis) {\n dependsOn.push('redis:', ' condition: service_started');\n }\n const dependsOnBlock = dependsOn.length > 0 ? block('depends_on', dependsOn) : [];\n\n const authupEnv = [\n env('PUBLIC_URL', answers.publicUrl),\n '# the proxies in front whose X-Forwarded-For is trusted, or false; authup trusts every hop by default',\n env('TRUST_PROXY', trustProxyValue(exposure, answers.consoleSplit)),\n ...dbEnv,\n ref('USER_ADMIN_PASSWORD', 'USER_ADMIN_PASSWORD'),\n ];\n if (answers.smtp) {\n authupEnv.push(ref('SMTP', 'SMTP'));\n }\n if (answers.registrationEnabled) {\n authupEnv.push(env('REGISTRATION_ENABLED', 'true'));\n }\n if (answers.passwordRecoveryEnabled) {\n authupEnv.push(env('PASSWORD_RECOVERY_ENABLED', 'true'));\n }\n if (answers.emailVerificationEnabled) {\n authupEnv.push(env('EMAIL_VERIFICATION_ENABLED', 'true'));\n }\n authupEnv.push(\n '# every downstream application origin, comma-separated; each may obtain a full-permission token',\n '# - \"TRUSTED_ORIGINS=https://app.example.com\"',\n '# wraps the realm key store at rest (base64, 32 bytes); write-once, back it up',\n '# - \"SECRETS_ENCRYPTION_KEY=<base64 32 bytes>\"',\n );\n // A console-only split keeps one API process, which sweeps and migrates itself like a plain `start` does.\n if (answers.workerSplit) {\n authupEnv.push(\n '# the sweeps run in the worker below',\n env('WORKER_ENABLED', 'false'),\n '# run \"docker compose run --rm authup migration run\" once before starting',\n env('MIGRATION_ENABLED', 'false'),\n );\n }\n\n const services = block('authup', [\n image,\n restart,\n // under a console split the proxy below owns the published port\n ...(answers.consoleSplit ? [] : block('ports', [`- \"${exposure.hostPort}:3000\"`])),\n '# a configuration file and a provisioning directory are optional; mount them under /etc/authup',\n '# volumes:',\n '# - ./authup.yml:/etc/authup/authup.yml:ro',\n '# - ./provisioning:/etc/authup/provisioning:ro',\n ...dependsOnBlock,\n ...block('environment', authupEnv),\n `command: ${answers.consoleSplit ? 'start core' : 'start'}`,\n ]);\n\n if (db.bundled) {\n const credentials = db.type === 'postgres' ?\n [\n env('POSTGRES_USER', db.username),\n ref('POSTGRES_PASSWORD', 'DB_PASSWORD'),\n env('POSTGRES_DB', db.database),\n ] :\n [\n env('MYSQL_USER', db.username),\n ref('MYSQL_PASSWORD', 'DB_PASSWORD'),\n ref('MYSQL_ROOT_PASSWORD', 'DB_PASSWORD'),\n env('MYSQL_DATABASE', db.database),\n ];\n\n // Both official images run a temporary, network-less server while initializing an empty volume, so a probe\n // over TCP answers only once the real server is up (pg_isready over the socket would answer too early).\n const probe = db.type === 'postgres' ?\n `test: [\"CMD\", \"pg_isready\", \"-h\", \"localhost\", \"-U\", ${quoteYaml(escapeComposeInterpolation(db.username))}, \"-d\", ${quoteYaml(escapeComposeInterpolation(db.database))}]` :\n // mysqladmin ping exits 0 whenever the server takes the connection, credentials or not\n 'test: [\"CMD\", \"mysqladmin\", \"ping\", \"-h\", \"127.0.0.1\"]';\n\n services.push('', ...block(db.type, [\n `image: ${engine.image}`,\n restart,\n ...block('volumes', [`- ${db.type}_data:${engine.dataPath}`]),\n ...block('environment', credentials),\n ...block('healthcheck', [probe, 'interval: 5s', 'timeout: 5s', 'retries: 24']),\n ]));\n }\n\n if (answers.redis) {\n services.push('', ...block('redis', ['image: redis:7', restart]));\n }\n\n if (answers.workerSplit) {\n services.push(\n '',\n '# one instance is enough, whatever the API scales to',\n ...block('authup-worker', [\n image,\n restart,\n ...dependsOnBlock,\n // the worker needs no url of its own; without one the CLI derives it and warns on every start\n ...block('environment', [env('PUBLIC_URL', answers.publicUrl), ...dbEnv]),\n 'command: start worker',\n ]),\n );\n }\n\n if (answers.consoleSplit) {\n services.push(\n '',\n '# Two proxy rules, in this order: /console/<name>/login/start and',\n '# /console/<name>/callback (admin and account) go to the authup service;',\n '# everything else under /console/** goes here, with the /console/<name>',\n `# prefix stripped. See ${DOCS_URL}/console-replicas`,\n ...block('authup-console', [\n image,\n restart,\n ...block('environment', [\n env('PUBLIC_URL', answers.publicUrl),\n '# the auth console renders server-side and calls the API itself: reach it on this network',\n env('INTERNAL_URL', 'http://authup:3000'),\n ]),\n '# the image probes the API port, which this process never opens; the auth',\n '# console cannot be disabled, so it is always listening (3020 by default)',\n ...block('healthcheck', [\n 'test: [\"CMD\", \"wget\", \"--spider\", \"--proxy\", \"off\", \"http://127.0.0.1:3020/healthy\"]',\n 'interval: 10s',\n ]),\n 'command: start console',\n ]),\n '',\n '# The proxy owns the published port and carries the two console routing rules (nginx.conf).',\n ...block('nginx', [\n 'image: nginx:1.31-alpine',\n restart,\n ...block('ports', [`- \"${exposure.hostPort}:80\"`]),\n ...block('volumes', ['- ./nginx.conf:/etc/nginx/conf.d/default.conf:ro']),\n ...block('depends_on', ['authup:', ' condition: service_started', 'authup-console:', ' condition: service_started']),\n ]),\n );\n }\n\n const compose = [\n `# Written by npm create authup. Secrets live in .env next to this file: ${DOCS_URL}/docker-compose`,\n '',\n ];\n if (db.bundled) {\n compose.push(...block('volumes', [`${db.type}_data:`]), '');\n }\n compose.push(...block('services', services));\n\n const dotenv = [\n '# Secrets docker-compose.yml interpolates. Keep this file out of version control.',\n `DB_PASSWORD=${quoteComposeEnv(db.password)}`,\n `USER_ADMIN_PASSWORD=${quoteComposeEnv(answers.adminPassword)}`,\n ];\n if (answers.smtp) {\n dotenv.push(`SMTP=${quoteComposeEnv(answers.smtp.url)}`);\n }\n dotenv.push(\n '# Wraps the realm key store at rest (base64, 32 bytes). Write-once: back it up.',\n '# SECRETS_ENCRYPTION_KEY=',\n );\n\n return {\n 'docker-compose.yml': `${compose.join('\\n')}\\n`,\n '.env': `${dotenv.join('\\n')}\\n`,\n ...(answers.consoleSplit ? { 'nginx.conf': NGINX_CONF } : {}),\n };\n}\n","/*\n * Copyright (c) 2026.\n * Author Peter Placzek (tada5hi)\n * For the full copyright and license information,\n * view the LICENSE file that was distributed with this source code.\n */\n\nimport type { Renderer, Target } from '../types.ts';\nimport { renderBareMetal } from './bare-metal.ts';\nimport { renderCompose } from './compose.ts';\nimport { renderDocker } from './docker.ts';\nimport { renderHelm } from './helm.ts';\n\nexport const RENDERERS: Record<Target, Renderer> = {\n docker: renderDocker,\n compose: renderCompose,\n helm: renderHelm,\n 'bare-metal': renderBareMetal,\n};\n","/*\n * Copyright (c) 2026.\n * Author Peter Placzek (tada5hi)\n * For the full copyright and license information,\n * view the LICENSE file that was distributed with this source code.\n */\n\nimport { readFileSync } from 'node:fs';\n\n// dist/index.mjs and src/version.ts both sit one level below the package root, so one relative URL serves the built bin and vitest alike.\nexport const VERSION: string = JSON.parse(readFileSync(new URL('../package.json', import.meta.url), 'utf8')).version;\n","#!/usr/bin/env node\n\n/*\n * Copyright (c) 2026.\n * Author Peter Placzek (tada5hi)\n * For the full copyright and license information,\n * view the LICENSE file that was distributed with this source code.\n */\n\n/* eslint-disable no-console */\n\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport process from 'node:process';\nimport { parseArgs } from 'node:util';\nimport { createReadlineAsk } from './ask.ts';\nimport { SECRET_FILES, TARGETS } from './constants.ts';\nimport { describeExposure } from './deployment.ts';\nimport { collectAnswers } from './prompts.ts';\nimport { dockerRunCommand } from './targets/docker.ts';\nimport { HELM_COMMANDS } from './targets/helm.ts';\nimport { RENDERERS } from './targets/index.ts';\nimport type { Answers, Target } from './types.ts';\nimport { VERSION } from './version.ts';\n\nconst DOCS_URL = 'https://authup.org/guide/deployment/';\n\nconst USAGE = `Usage: npx create-authup [--force] [--help]\n npm create authup -- [--force] [--help]\n\nWrites the deployment files for one authup installation into the current directory.\n\nTargets: ${TARGETS.join(', ')}\n\nFlags:\n --force overwrite files that already exist\n --help print this text`;\n\nfunction nextSteps(answers: Answers): string[] {\n const steps: Record<Target, string[]> = {\n docker: [\n dockerRunCommand(VERSION, describeExposure(answers.publicUrl).hostPort),\n 'The database must already exist: the image runs in production mode and refuses sqlite.',\n ],\n compose: [\n ...(answers.workerSplit ? ['docker compose run --rm authup migration run'] : []),\n 'docker compose up -d',\n ...(answers.consoleSplit ? ['The nginx service publishes the port; TLS, if any, terminates in front of it.'] : []),\n ],\n helm: [...HELM_COMMANDS],\n 'bare-metal': ['npm install', 'npx authup config validate', 'npm start'],\n };\n\n return steps[answers.target];\n}\n\nasync function main(): Promise<void> {\n const { values } = parseArgs({\n options: {\n force: { type: 'boolean', default: false },\n help: { type: 'boolean', default: false },\n },\n allowPositionals: false,\n });\n if (values.help) {\n console.log(USAGE);\n return;\n }\n\n console.log(`create-authup ${VERSION}`);\n console.log(`Files are written into the current directory (${process.cwd()}).`);\n\n const { ask, close } = createReadlineAsk();\n let collected: Awaited<ReturnType<typeof collectAnswers>>;\n try {\n collected = await collectAnswers(ask);\n } finally {\n close();\n }\n const { answers, notes } = collected;\n\n const rendered = RENDERERS[answers.target](answers, VERSION);\n const files = Object.keys(rendered);\n const directories = files.filter((name) => {\n try {\n return fs.statSync(path.join(process.cwd(), name)).isDirectory();\n } catch {\n return false;\n }\n });\n if (directories.length > 0) {\n console.error(`Cannot write over a directory: ${directories.join(', ')}.`);\n process.exit(1);\n }\n\n const existing = files.filter((name) => fs.existsSync(path.join(process.cwd(), name)));\n if (existing.length > 0 && !values.force) {\n console.error(`Refusing to overwrite: ${existing.join(', ')}. Re-run with --force.`);\n process.exit(1);\n }\n\n for (const name of files) {\n const target = path.join(process.cwd(), name);\n const secret = SECRET_FILES.has(name);\n fs.writeFileSync(target, rendered[name], secret ? { encoding: 'utf8', mode: 0o600 } : 'utf8');\n if (secret) {\n // writeFileSync's mode applies to a file it creates, so an overwrite keeps whatever the old one had\n fs.chmodSync(target, 0o600);\n }\n }\n\n for (const note of notes) {\n console.log(note);\n }\n console.log(`Written: ${files.join(', ')}`);\n console.log('Next steps:');\n for (const step of nextSteps(answers)) {\n console.log(` ${step}`);\n }\n console.log(`Docs: ${DOCS_URL}`);\n}\n\nmain().catch((e) => {\n console.error(e instanceof Error ? e.message : String(e));\n process.exit(1);\n});\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-authup",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "1.0.0-beta.
|
|
4
|
+
"version": "1.0.0-beta.71",
|
|
5
5
|
"description": "Answer a few questions and get the deployment files for Authup: docker run, compose, Helm or bare metal.",
|
|
6
6
|
"license": "Apache-2.0",
|
|
7
7
|
"bin": {
|
|
@@ -47,8 +47,8 @@
|
|
|
47
47
|
"homepage": "https://github.com/authup/authup#readme",
|
|
48
48
|
"dependencies": {},
|
|
49
49
|
"devDependencies": {
|
|
50
|
-
"@authup/server-config": "^1.0.0-beta.
|
|
51
|
-
"@authup/server-config-kit": "^1.0.0-beta.
|
|
50
|
+
"@authup/server-config": "^1.0.0-beta.71",
|
|
51
|
+
"@authup/server-config-kit": "^1.0.0-beta.71",
|
|
52
52
|
"@types/node": "^26.6.5",
|
|
53
53
|
"ajv": "^8.20.0",
|
|
54
54
|
"cross-env": "^10.1.0",
|