Commit06a502cunknown_key
feat(integrations): surface APP_BASE_URL + SELF_HOST_REPO in admin UI
feat(integrations): surface APP_BASE_URL + SELF_HOST_REPO in admin UI
Two env vars that operators currently have to SSH to set, but the wrong
value silently breaks the platform:
APP_BASE_URL — used to build OAuth redirect URIs. If not set or wrong
the Google/GitHub "Sign in with…" flow fails with
redirect_uri_mismatch. Operators couldn't fix it
without SSH'ing into the box and editing
/etc/gluecron.env + systemctl restart.
SELF_HOST_REPO — used by /admin/ops (auto-merge enable button) and
/admin/self-host. Last commit (5c7fbd2) added env-var
override; this surfaces the value in the UI so it's
the actual user-facing knob.
New "Platform" section in /admin/integrations sits ABOVE AI / Email /
SCM since wrong values here cascade into everything else. Loaded into
process.env on save by the existing boot hook — no restart needed.3 files changed+46−506a502c6c1f23fcc8520cf15fbc190e1d0fd1b1d
3 changed files+46−5
Modifiedsrc/__tests__/admin-integrations.test.ts+13−4View fileUnifiedSplit
@@ -283,9 +283,12 @@ describe("system-config — INTEGRATION_FIELDS surface", () => {
283283
284284 it("does NOT expose env-only keys (chicken-and-egg / infra)", () => {
285285 const keys = INTEGRATION_FIELDS.map((f) => f.key);
286 // DATABASE_URL / BUILD_SHA / PORT / GIT_REPOS_PATH stay env-only —
287 // changing them at runtime would break the running process. APP_BASE_URL
288 // and SELF_HOST_REPO are intentionally surfaced so operators can fix
289 // OAuth redirect_uri_mismatch + the self-host repo lookup without SSH.
286290 for (const forbidden of [
287291 "DATABASE_URL",
288 "SELF_HOST_REPO",
289292 "BUILD_SHA",
290293 "BUILD_TIME",
291294 "PORT",
@@ -301,9 +304,15 @@ describe("system-config — INTEGRATION_FIELDS surface", () => {
301304 expect(f.label.length).toBeGreaterThan(0);
302305 expect(typeof f.helper).toBe("string");
303306 expect(f.helper.length).toBeGreaterThan(0);
304 expect(["ai", "email", "scm", "security", "observability", "webhook"]).toContain(
305 f.group
306 );
307 expect([
308 "platform",
309 "ai",
310 "email",
311 "scm",
312 "security",
313 "observability",
314 "webhook",
315 ]).toContain(f.group);
307316 }
308317 });
309318});
Modifiedsrc/lib/system-config.ts+26−1View fileUnifiedSplit
@@ -160,10 +160,35 @@ export interface IntegrationField {
160160 helper: string;
161161 helperLink?: { href: string; text: string };
162162 isSecret: boolean;
163 group: "ai" | "email" | "scm" | "security" | "observability" | "webhook";
163 group:
164 | "platform"
165 | "ai"
166 | "email"
167 | "scm"
168 | "security"
169 | "observability"
170 | "webhook";
164171}
165172
166173export const INTEGRATION_FIELDS: IntegrationField[] = [
174 {
175 key: "APP_BASE_URL",
176 envFallback: "APP_BASE_URL",
177 label: "Public base URL",
178 helper:
179 "The HTTPS URL users hit (e.g. https://gluecron.com). Used to build OAuth redirect URIs — wrong/missing → Google/GitHub sign-in fails with redirect_uri_mismatch.",
180 isSecret: false,
181 group: "platform",
182 },
183 {
184 key: "SELF_HOST_REPO",
185 envFallback: "SELF_HOST_REPO",
186 label: "Self-host repo (owner/name)",
187 helper:
188 "Which repo the /admin/ops + /admin/self-host tools operate on. Defaults to ccantynz-alt/Gluecron.com.",
189 isSecret: false,
190 group: "platform",
191 },
167192 {
168193 key: "ANTHROPIC_API_KEY",
169194 envFallback: "ANTHROPIC_API_KEY",
Modifiedsrc/routes/admin-integrations.tsx+7−0View fileUnifiedSplit
@@ -397,6 +397,12 @@ interface GroupDef {
397397}
398398
399399const GROUPS: Record<string, GroupDef> = {
400 platform: {
401 id: "platform",
402 title: "Platform",
403 blurb:
404 "Public URL + self-host repo name. APP_BASE_URL must be right or OAuth fails with redirect_uri_mismatch.",
405 },
400406 ai: {
401407 id: "ai",
402408 title: "AI",
@@ -468,6 +474,7 @@ integrations.get("/admin/integrations", async (c) => {
468474 }
469475
470476 const groupOrder: Array<keyof typeof GROUPS> = [
477 "platform",
471478 "ai",
472479 "email",
473480 "scm",
474481