CodeIssuesDiscussionsWikiPull RequestsProjectsCommitsActionsReleasesContributorsPulse● GatesSecuritySettingsDeploymentsPipelineInsightsAgents✨ Explain✨ Ask AI✨ Workspace✨ Spec✨ Tests▓ Debt Map✨ NL Search🏛 Archaeology
Blame · Line-by-line history
install.ts
Each line is annotated with the commit that last touched it. Click any SHA to jump to that commit and see the surrounding change.
| 46d6165 | 1 | /** |
| 2 | * Block L2 — one-command install. | |
| 3 | * | |
| 4 | * GET /install -> the bash installer (scripts/install.sh). | |
| 5 | * | |
| 6 | * Curl-able: `curl -sSL https://gluecron.com/install | bash`. | |
| 7 | * | |
| 8 | * The script is read from disk once at module load and cached in memory; we | |
| 9 | * also serve it with `Cache-Control: public, max-age=300` so any CDN in front | |
| 10 | * of us can absorb the load. Mirrors the static-string pattern used by | |
| 11 | * `src/routes/pwa.ts`. | |
| 12 | */ | |
| 13 | ||
| 14 | import { Hono } from "hono"; | |
| 15 | import { readFileSync } from "fs"; | |
| 16 | import { join } from "path"; | |
| 17 | ||
| 18 | const install = new Hono(); | |
| 19 | ||
| 20 | // Resolve at module-load time. We try the on-disk script first; if anything | |
| 21 | // goes sideways (missing file in a stripped container image, weird CWD, etc.) | |
| 22 | // we fall back to a stub that points the user at the canonical URL so the | |
| 23 | // endpoint always returns *something* shell-safe. | |
| 24 | const FALLBACK_SCRIPT = `#!/usr/bin/env bash | |
| 25 | echo "Gluecron install script unavailable on this host." >&2 | |
| 26 | echo "Fetch it directly from https://gluecron.com/install" >&2 | |
| 27 | exit 1 | |
| 28 | `; | |
| 29 | ||
| 30 | function loadScript(): string { | |
| 31 | // Walk a few candidate locations so this works in dev, tests, and the | |
| 32 | // fly.io container where CWD may be /app. | |
| 33 | const candidates = [ | |
| 34 | join(process.cwd(), "scripts", "install.sh"), | |
| 35 | join(import.meta.dir, "..", "..", "scripts", "install.sh"), | |
| 36 | ]; | |
| 37 | for (const p of candidates) { | |
| 38 | try { | |
| 39 | const buf = readFileSync(p, "utf8"); | |
| 40 | if (buf && buf.length > 0) return buf; | |
| 41 | } catch { | |
| 42 | // try the next candidate | |
| 43 | } | |
| 44 | } | |
| 45 | return FALLBACK_SCRIPT; | |
| 46 | } | |
| 47 | ||
| 48 | export const INSTALL_SCRIPT_SRC = loadScript(); | |
| 49 | ||
| 50 | install.get("/install", (c) => { | |
| 51 | c.header("content-type", "text/x-shellscript; charset=utf-8"); | |
| 52 | c.header("cache-control", "public, max-age=300"); | |
| 53 | // Make `curl https://gluecron.com/install -o install.sh` give a sensible | |
| 54 | // default filename without forcing it for piped installs. | |
| 55 | c.header("content-disposition", 'inline; filename="install.sh"'); | |
| 56 | return c.body(INSTALL_SCRIPT_SRC); | |
| 57 | }); | |
| 58 | ||
| 59 | export default install; |