Add Coolify REST API server with Scalar docs and UI integration
Express API server on :3100 exposing all Coolify operations: - CRUD for apps, env vars, servers - Full upsert pipeline (create/update + env + route + deploy) - Drift detection, Traefik route management via SSH - Scalar API docs at /reference, OpenAPI 3.1 spec UI: New Coolify page with app cards, deploy/delete actions, env var expansion. Sidebar nav + React Query hooks + fetch client. Both UI and LLM/CLI use the same HTTP endpoints. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
92
app/renderer/src/components/coolify/coolify-app-card.jsx
Normal file
92
app/renderer/src/components/coolify/coolify-app-card.jsx
Normal file
@@ -0,0 +1,92 @@
|
||||
import { useState } from 'react';
|
||||
import { Rocket, Trash2, ChevronDown, ChevronRight, ExternalLink } from 'lucide-react';
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { StatusDot } from '@/components/shared/status-dot';
|
||||
|
||||
const buildpackColor = {
|
||||
dockercompose: 'secondary',
|
||||
nixpacks: 'outline',
|
||||
dockerfile: 'outline',
|
||||
};
|
||||
|
||||
function statusColor(status) {
|
||||
if (!status) return 'gray';
|
||||
const s = status.toLowerCase();
|
||||
if (s.includes('running') || s.includes('healthy')) return 'green';
|
||||
if (s.includes('stopped') || s.includes('exited')) return 'red';
|
||||
if (s.includes('building') || s.includes('starting')) return 'yellow';
|
||||
return 'gray';
|
||||
}
|
||||
|
||||
export function CoolifyAppCard({ app, onDeploy, onDelete }) {
|
||||
const [expanded, setExpanded] = useState(false);
|
||||
|
||||
return (
|
||||
<Card className="overflow-hidden">
|
||||
<CardHeader className="p-4 pb-2">
|
||||
<div className="flex items-center justify-between">
|
||||
<CardTitle className="text-sm font-medium flex items-center gap-2">
|
||||
<StatusDot color={statusColor(app.status)} />
|
||||
{app.name}
|
||||
</CardTitle>
|
||||
<Badge variant={buildpackColor[app.build_pack] || 'outline'} className="text-[10px]">
|
||||
{app.build_pack}
|
||||
</Badge>
|
||||
</div>
|
||||
</CardHeader>
|
||||
|
||||
<CardContent className="p-4 pt-0 space-y-2">
|
||||
{/* Port + domain */}
|
||||
<div className="flex items-center gap-3 text-xs text-muted-foreground">
|
||||
{app._host_port && <span>:{app._host_port}</span>}
|
||||
{app.git_branch && <span>{app.git_branch}</span>}
|
||||
{app.fqdn && (
|
||||
<a
|
||||
href={app.fqdn}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="flex items-center gap-1 text-primary hover:underline"
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
{app.fqdn.replace(/^https?:\/\//, '')}
|
||||
<ExternalLink className="h-3 w-3" />
|
||||
</a>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Actions */}
|
||||
<div className="flex items-center gap-2 pt-1">
|
||||
<Button variant="ghost" size="sm" className="h-7 text-xs gap-1" onClick={() => onDeploy(app)}>
|
||||
<Rocket className="h-3 w-3" /> Deploy
|
||||
</Button>
|
||||
<Button variant="ghost" size="sm" className="h-7 text-xs gap-1 text-destructive" onClick={() => onDelete(app)}>
|
||||
<Trash2 className="h-3 w-3" /> Delete
|
||||
</Button>
|
||||
<button
|
||||
className="ml-auto text-xs text-muted-foreground hover:text-foreground flex items-center gap-1"
|
||||
onClick={() => setExpanded(!expanded)}
|
||||
>
|
||||
{expanded ? <ChevronDown className="h-3 w-3" /> : <ChevronRight className="h-3 w-3" />}
|
||||
Envs
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Expandable env vars */}
|
||||
{expanded && app._envs && (
|
||||
<div className="rounded-md bg-secondary/50 p-2 text-[11px] font-mono space-y-0.5 max-h-40 overflow-auto">
|
||||
{app._envs.length === 0 && <span className="text-muted-foreground">No env vars</span>}
|
||||
{app._envs.map((env) => (
|
||||
<div key={env.id || env.key} className="flex gap-2">
|
||||
<span className="text-primary">{env.key}</span>
|
||||
<span className="text-muted-foreground">=</span>
|
||||
<span className="truncate">{env.value}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user