Theia rip-out: - Delete app/api/theia-auth/route.ts (Traefik ForwardAuth shim) - Delete app/api/projects/[projectId]/workspace/route.ts and app/api/projects/prewarm/route.ts (Cloud Run Theia provisioning) - Delete lib/cloud-run-workspace.ts and lib/coolify-workspace.ts - Strip provisionTheiaWorkspace + theiaWorkspaceUrl/theiaAppUuid/ theiaError from app/api/projects/create/route.ts response - Remove Theia callbackUrl branch in app/auth/page.tsx - Drop "Open in Theia" button + xterm/Theia PTY copy in build/page.tsx - Drop theiaWorkspaceUrl from deployment/page.tsx Project type - Strip Theia IDE line + theia-code-os from advisor + agent-chat context strings - Scrub Theia mention from lib/auth/workspace-auth.ts comment P5.1 (custom apex domains + DNS): - lib/coolify.ts + lib/opensrs.ts: nameserver normalization, OpenSRS XML auth, Cloud DNS plumbing - scripts/smoke-attach-e2e.ts: full prod GCP + sandbox OpenSRS + prod Coolify smoke covering register/zone/A/NS/PATCH/cleanup In-progress (Justine onboarding/build, MVP setup, agent telemetry): - New (justine)/stories, project (home) layouts, mvp-setup, run, tasks routes + supporting components - Project shell + sidebar + nav refactor for the Stackless palette - Agent session API hardening (sessions, events, stream, approve, retry, stop) + atlas-chat, advisor, design-surfaces refresh - New scripts/sync-db-url-from-coolify.mjs + scripts/prisma-db-push.mjs + docker-compose.local-db.yml for local Prisma workflows - lib/dev-bypass.ts, lib/chat-context-refs.ts, lib/prd-sections.ts - Misc: stories CSS, debug/prisma route, modal-theme, BuildLivePlanPanel Made-with: Cursor
146 lines
4.2 KiB
JavaScript
146 lines
4.2 KiB
JavaScript
#!/usr/bin/env node
|
|
/**
|
|
* Pull postgres external URL from Coolify API and write DATABASE_URL + POSTGRES_URL in .env.local.
|
|
*
|
|
* Loads (in order): ../.coolify.env, .env, .env.local (Coolify wins for COOLIFY_* from parent file).
|
|
*
|
|
* Identify DB by (first match):
|
|
* COOLIFY_DATABASE_UUID — exact
|
|
* COOLIFY_DATABASE_NAME — default: vibn-postgres
|
|
* argv[2] — uuid or name substring
|
|
*
|
|
* Requires: COOLIFY_URL, COOLIFY_API_TOKEN
|
|
*/
|
|
import { config } from "dotenv";
|
|
import fs from "node:fs";
|
|
import path from "node:path";
|
|
import { fileURLToPath } from "node:url";
|
|
|
|
const root = path.join(path.dirname(fileURLToPath(import.meta.url)), "..");
|
|
const repoRoot = path.join(root, "..");
|
|
|
|
config({ path: path.join(repoRoot, ".coolify.env") });
|
|
config({ path: path.join(root, ".env") });
|
|
config({ path: path.join(root, ".env.local"), override: true });
|
|
|
|
const COOLIFY_URL = (process.env.COOLIFY_URL ?? "").replace(/\/$/, "");
|
|
const token = process.env.COOLIFY_API_TOKEN ?? "";
|
|
const arg = process.argv[2];
|
|
|
|
function fail(msg) {
|
|
console.error(msg);
|
|
process.exit(1);
|
|
}
|
|
|
|
if (!COOLIFY_URL || !token) {
|
|
fail("Missing COOLIFY_URL or COOLIFY_API_TOKEN (e.g. set in master-ai/.coolify.env).");
|
|
}
|
|
|
|
function normalizeDatabaseUrl(url) {
|
|
if (!url || typeof url !== "string") return "";
|
|
return url.replace(/^postgres:\/\//i, "postgresql://");
|
|
}
|
|
|
|
async function coolifyFetch(path) {
|
|
const res = await fetch(`${COOLIFY_URL}/api/v1${path}`, {
|
|
headers: {
|
|
Authorization: `Bearer ${token}`,
|
|
"Content-Type": "application/json",
|
|
},
|
|
});
|
|
if (!res.ok) {
|
|
const text = await res.text();
|
|
fail(`Coolify API ${res.status} ${path}: ${text.slice(0, 500)}`);
|
|
}
|
|
return res.json();
|
|
}
|
|
|
|
function pickDatabase(list) {
|
|
const uuid = process.env.COOLIFY_DATABASE_UUID?.trim();
|
|
const nameDefault = process.env.COOLIFY_DATABASE_NAME || "vibn-postgres";
|
|
|
|
if (uuid) {
|
|
const d = list.find((x) => x.uuid === uuid);
|
|
if (d) return d;
|
|
fail(`No database with COOLIFY_DATABASE_UUID=${uuid}.`);
|
|
}
|
|
|
|
if (arg?.trim()) {
|
|
const a = arg.trim().toLowerCase();
|
|
const byUuid = list.find((x) => x.uuid === arg.trim());
|
|
if (byUuid) return byUuid;
|
|
const byName = list.find(
|
|
(x) =>
|
|
String(x.name ?? "")
|
|
.toLowerCase()
|
|
.includes(a) ||
|
|
String(x.uuid ?? "")
|
|
.toLowerCase()
|
|
.includes(a)
|
|
);
|
|
if (byName) return byName;
|
|
fail(`No database matching "${arg}".`);
|
|
}
|
|
|
|
const byName = list.find(
|
|
(x) => String(x.name ?? "").toLowerCase() === nameDefault.toLowerCase()
|
|
);
|
|
if (byName) return byName;
|
|
|
|
fail(
|
|
`No database named "${nameDefault}". Set COOLIFY_DATABASE_UUID or pass uuid/name as argument.`
|
|
);
|
|
}
|
|
|
|
async function main() {
|
|
const list = await coolifyFetch("/databases");
|
|
if (!Array.isArray(list) || list.length === 0) {
|
|
fail("Coolify returned no databases.");
|
|
}
|
|
|
|
const picked = pickDatabase(list);
|
|
const detail = await coolifyFetch(`/databases/${picked.uuid}`);
|
|
const raw = detail.external_db_url;
|
|
const databaseUrl = normalizeDatabaseUrl(raw);
|
|
|
|
if (!databaseUrl) {
|
|
fail(
|
|
"No external_db_url from Coolify. Enable public exposure and set a host port on the Postgres service, then restart."
|
|
);
|
|
}
|
|
|
|
const envLocalPath = path.join(root, ".env.local");
|
|
if (!fs.existsSync(envLocalPath)) {
|
|
fail(`Missing ${envLocalPath}; create it first (copy from .env.example).`);
|
|
}
|
|
|
|
let body = fs.readFileSync(envLocalPath, "utf8");
|
|
const lines = body.split(/\r?\n/);
|
|
const setLine = (key, value) => {
|
|
const next = `${key}=${value}`;
|
|
let seen = false;
|
|
for (let i = 0; i < lines.length; i++) {
|
|
if (lines[i].startsWith(`${key}=`)) {
|
|
lines[i] = next;
|
|
seen = true;
|
|
break;
|
|
}
|
|
}
|
|
if (!seen) lines.push(next);
|
|
};
|
|
|
|
setLine("DATABASE_URL", databaseUrl);
|
|
setLine("POSTGRES_URL", databaseUrl);
|
|
|
|
fs.writeFileSync(envLocalPath, lines.join("\n") + (body.endsWith("\n") ? "\n" : ""), "utf8");
|
|
console.log(
|
|
`Updated DATABASE_URL and POSTGRES_URL in .env.local from Coolify (${picked.name}, ${picked.uuid}).`
|
|
);
|
|
console.log(`Public port from API: ${detail.public_port ?? "(n/a)"}; is_public: ${detail.is_public ?? "(n/a)"}`);
|
|
}
|
|
|
|
main().catch((e) => {
|
|
console.error(e);
|
|
process.exit(1);
|
|
});
|