"use client"; import { Suspense, useState, useEffect } from "react"; import { useParams, useSearchParams, useRouter } from "next/navigation"; // ── Types ───────────────────────────────────────────────────────────────────── interface InfraApp { name: string; domain?: string | null; coolifyServiceUuid?: string | null; } interface ProjectData { giteaRepo?: string; giteaRepoUrl?: string; apps?: InfraApp[]; } // ── Tab definitions ─────────────────────────────────────────────────────────── const TABS = [ { id: "builds", label: "Builds", icon: "⬡" }, { id: "databases", label: "Databases", icon: "◫" }, { id: "services", label: "Services", icon: "◎" }, { id: "environment", label: "Environment", icon: "≡" }, { id: "domains", label: "Domains", icon: "◬" }, { id: "logs", label: "Logs", icon: "≈" }, ] as const; type TabId = typeof TABS[number]["id"]; // ── Shared empty state ──────────────────────────────────────────────────────── function ComingSoonPanel({ icon, title, description }: { icon: string; title: string; description: string }) { return (
{icon}
{title}
{description}
Coming soon
); } // ── Builds tab ──────────────────────────────────────────────────────────────── function BuildsTab({ project }: { project: ProjectData | null }) { const apps = project?.apps ?? []; if (apps.length === 0) { return ( ); } return (
Deployed Apps
{apps.map(app => (
{app.name}
{app.domain && (
{app.domain}
)}
Running
))}
); } // ── Databases tab ───────────────────────────────────────────────────────────── function DatabasesTab() { return ( ); } // ── Services tab ────────────────────────────────────────────────────────────── function ServicesTab() { return ( ); } // ── Environment tab ─────────────────────────────────────────────────────────── function EnvironmentTab({ project }: { project: ProjectData | null }) { return (
Environment Variables & Secrets
{/* Header row */}
KeyValue
{/* Placeholder rows */} {["DATABASE_URL", "NEXTAUTH_SECRET", "GITEA_API_TOKEN"].map(k => (
{k} ••••••••
))}
Variables are encrypted at rest and auto-injected into deployed containers. Secrets are never exposed in logs.
); } // ── Domains tab ─────────────────────────────────────────────────────────────── function DomainsTab({ project }: { project: ProjectData | null }) { const apps = (project?.apps ?? []).filter(a => a.domain); return (
Domains & SSL
{apps.length > 0 ? (
{apps.map(app => (
{app.domain}
{app.name}
SSL active
))}
) : (
No custom domains configured
Deploy an app first, then point a domain here.
)}
); } // ── Logs tab ────────────────────────────────────────────────────────────────── function LogsTab({ project }: { project: ProjectData | null }) { const apps = project?.apps ?? []; if (apps.length === 0) { return ( ); } return (
Runtime Logs
{"# Logs will stream here once connected to Coolify"}
{"→ Select a service to tail its log output"}
); } // ── Inner page ──────────────────────────────────────────────────────────────── function InfrastructurePageInner() { const params = useParams(); const searchParams = useSearchParams(); const router = useRouter(); const projectId = params.projectId as string; const workspace = params.workspace as string; const activeTab = (searchParams.get("tab") ?? "builds") as TabId; const [project, setProject] = useState(null); useEffect(() => { fetch(`/api/projects/${projectId}/apps`) .then(r => r.json()) .then(d => setProject({ apps: d.apps ?? [], giteaRepo: d.giteaRepo, giteaRepoUrl: d.giteaRepoUrl })) .catch(() => {}); }, [projectId]); const setTab = (id: TabId) => { router.push(`/${workspace}/project/${projectId}/infrastructure?tab=${id}`, { scroll: false }); }; return (
{/* ── Left sub-nav ── */}
Infrastructure
{TABS.map(tab => { const active = activeTab === tab.id; return ( ); })}
{/* ── Content ── */}
{activeTab === "builds" && } {activeTab === "databases" && } {activeTab === "services" && } {activeTab === "environment" && } {activeTab === "domains" && } {activeTab === "logs" && }
); } // ── Export ──────────────────────────────────────────────────────────────────── export default function InfrastructurePage() { return ( Loading…}> ); }