Refactor component terminology to programs in config and manifest
- Updated the terminology from "components" to "programs" across the codebase, including in config loading, saving, and manifest specifications. - Introduced a new `stacks.py` file to handle lifecycle actions for development stacks, implementing handlers for Python and React Vite stacks. - Adjusted tests to reflect the new program structure and ensure proper functionality. - Revised documentation to align with the new terminology and structure, ensuring clarity on the purpose and configuration of programs, services, and jobs.
This commit is contained in:
@@ -1,17 +1,16 @@
|
||||
import { useParams } from "react-router-dom"
|
||||
import { Plug, Unplug } from "lucide-react"
|
||||
import { useComponent, useEventStream, useToolDetail, useToolAction } from "@/services/api/hooks"
|
||||
import { useProgram, useEventStream, useToolDetail } from "@/services/api/hooks"
|
||||
import { runnerLabel } from "@/lib/labels"
|
||||
import { DetailHeader } from "@/components/detail/DetailHeader"
|
||||
import { ConfigPanel } from "@/components/detail/ConfigPanel"
|
||||
import { ProgramActions } from "@/components/ProgramActions"
|
||||
|
||||
export function ComponentDetailPage() {
|
||||
useEventStream()
|
||||
const { name } = useParams<{ name: string }>()
|
||||
const { data: component, isLoading, error, refetch } = useComponent(name ?? "")
|
||||
const { data: component, isLoading, error, refetch } = useProgram(name ?? "")
|
||||
const isTool = component?.behavior === "tool"
|
||||
const { data: toolDetail } = useToolDetail(isTool ? (name ?? "") : "")
|
||||
const { mutate: toolAction, isPending: toolPending } = useToolAction()
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
@@ -23,7 +22,7 @@ export function ComponentDetailPage() {
|
||||
return (
|
||||
<div className="max-w-3xl mx-auto px-6 py-8">
|
||||
<DetailHeader backTo="/" backLabel="Back" name={name ?? ""} />
|
||||
<p className="text-red-400">Component not found</p>
|
||||
<p className="text-red-400">Program not found</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -32,33 +31,13 @@ export function ComponentDetailPage() {
|
||||
<div className="max-w-3xl mx-auto px-6 py-8">
|
||||
<DetailHeader
|
||||
backTo="/"
|
||||
backLabel="Back to Components"
|
||||
backLabel="Back to Programs"
|
||||
name={component.id}
|
||||
behavior={component.behavior}
|
||||
stack={component.stack}
|
||||
source={component.source}
|
||||
>
|
||||
{isTool && (
|
||||
<div className="flex items-center gap-2">
|
||||
{component.installed ? (
|
||||
<button
|
||||
onClick={() => toolAction({ name: component.id, action: "uninstall" })}
|
||||
disabled={toolPending}
|
||||
className="flex items-center gap-1.5 px-3 py-1.5 text-sm rounded border border-red-800 text-red-400 hover:bg-red-800/20 transition-colors disabled:opacity-40"
|
||||
>
|
||||
<Unplug size={14} /> Uninstall
|
||||
</button>
|
||||
) : (
|
||||
<button
|
||||
onClick={() => toolAction({ name: component.id, action: "install" })}
|
||||
disabled={toolPending}
|
||||
className="flex items-center gap-1.5 px-3 py-1.5 text-sm rounded border border-green-800 text-green-400 hover:bg-green-800/20 transition-colors disabled:opacity-40"
|
||||
>
|
||||
<Plug size={14} /> Install to PATH
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
<ProgramActions name={component.id} actions={component.actions} installed={component.installed} />
|
||||
</DetailHeader>
|
||||
|
||||
{component.description && (
|
||||
@@ -121,7 +100,7 @@ export function ComponentDetailPage() {
|
||||
</div>
|
||||
)}
|
||||
|
||||
<ConfigPanel component={component} configSection="components" onRefetch={refetch} />
|
||||
<ConfigPanel component={component} configSection="programs" onRefetch={refetch} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ export function ComponentRedirect() {
|
||||
}
|
||||
|
||||
if (error || !component) {
|
||||
return <Navigate to={`/components/${name}`} replace />
|
||||
return <Navigate to={`/programs/${name}`} replace />
|
||||
}
|
||||
|
||||
if (component.managed && !component.schedule) {
|
||||
@@ -20,8 +20,8 @@ export function ComponentRedirect() {
|
||||
}
|
||||
|
||||
if (component.managed && component.schedule) {
|
||||
return <Navigate to={`/scheduled/${name}`} replace />
|
||||
return <Navigate to={`/jobs/${name}`} replace />
|
||||
}
|
||||
|
||||
return <Navigate to={`/components/${name}`} replace />
|
||||
return <Navigate to={`/programs/${name}`} replace />
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { useMemo } from "react"
|
||||
import { useComponents, useStatus, useGateway, useNodes, useMeshStatus, useEventStream } from "@/services/api/hooks"
|
||||
import { useServices, useJobs, usePrograms, useStatus, useGateway, useNodes, useMeshStatus, useEventStream } from "@/services/api/hooks"
|
||||
import { GatewayPanel } from "@/components/GatewayPanel"
|
||||
import { MeshPanel } from "@/components/MeshPanel"
|
||||
import { NodeBar } from "@/components/NodeBar"
|
||||
@@ -11,20 +10,16 @@ import { SectionHeader } from "@/components/SectionHeader"
|
||||
|
||||
export function Dashboard() {
|
||||
useEventStream()
|
||||
const { data: components, isLoading } = useComponents()
|
||||
const { data: services, isLoading: loadingServices } = useServices()
|
||||
const { data: jobs, isLoading: loadingJobs } = useJobs()
|
||||
const { data: programs, isLoading: loadingPrograms } = usePrograms()
|
||||
const { data: statusResp } = useStatus()
|
||||
const { data: gateway } = useGateway()
|
||||
const { data: nodes } = useNodes()
|
||||
const { data: mesh } = useMeshStatus()
|
||||
|
||||
const { services, scheduled, catalog } = useMemo(() => {
|
||||
const svc = (components ?? []).filter((c) => c.category === "service")
|
||||
const sch = (components ?? []).filter((c) => c.category === "job")
|
||||
const cat = (components ?? []).filter((c) => c.category === "component")
|
||||
return { services: svc, scheduled: sch, catalog: cat }
|
||||
}, [components])
|
||||
|
||||
const statuses = statusResp?.statuses ?? []
|
||||
const isLoading = loadingServices || loadingJobs || loadingPrograms
|
||||
|
||||
return (
|
||||
<div className="max-w-6xl mx-auto px-6 py-8">
|
||||
@@ -51,16 +46,16 @@ export function Dashboard() {
|
||||
<p className="text-[var(--muted)]">Loading...</p>
|
||||
) : (
|
||||
<div className="space-y-10">
|
||||
{services.length > 0 && (
|
||||
{services && services.length > 0 && (
|
||||
<ServiceSection services={services} statuses={statuses} />
|
||||
)}
|
||||
{scheduled.length > 0 && (
|
||||
<ScheduledSection jobs={scheduled} statuses={statuses} />
|
||||
{jobs && jobs.length > 0 && (
|
||||
<ScheduledSection jobs={jobs} statuses={statuses} />
|
||||
)}
|
||||
{catalog.length > 0 && (
|
||||
{programs && programs.length > 0 && (
|
||||
<section>
|
||||
<SectionHeader section="component" />
|
||||
<ComponentTable components={catalog} />
|
||||
<SectionHeader section="program" />
|
||||
<ComponentTable components={programs} />
|
||||
</section>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { useRef } from "react"
|
||||
import { useParams } from "react-router-dom"
|
||||
import { Clock } from "lucide-react"
|
||||
import { useComponent, useStatus, useEventStream } from "@/services/api/hooks"
|
||||
import { LogViewer } from "@/components/LogViewer"
|
||||
import { Clock, Trash2 } from "lucide-react"
|
||||
import { useJob, useStatus, useEventStream } from "@/services/api/hooks"
|
||||
import { LogViewer, type LogViewerHandle } from "@/components/LogViewer"
|
||||
import { DetailHeader } from "@/components/detail/DetailHeader"
|
||||
import { ServiceControls } from "@/components/detail/ServiceControls"
|
||||
import { SystemdPanel } from "@/components/detail/SystemdPanel"
|
||||
@@ -9,8 +10,9 @@ import { ConfigPanel } from "@/components/detail/ConfigPanel"
|
||||
|
||||
export function ScheduledDetailPage() {
|
||||
useEventStream()
|
||||
const logRef = useRef<LogViewerHandle>(null)
|
||||
const { name } = useParams<{ name: string }>()
|
||||
const { data: component, isLoading, error, refetch } = useComponent(name ?? "")
|
||||
const { data: component, isLoading, error, refetch } = useJob(name ?? "")
|
||||
const { data: statusResp } = useStatus()
|
||||
const health = statusResp?.statuses.find((s) => s.id === name)
|
||||
|
||||
@@ -35,7 +37,7 @@ export function ScheduledDetailPage() {
|
||||
backTo="/"
|
||||
backLabel="Back to Jobs"
|
||||
name={component.id}
|
||||
behavior={component.behavior}
|
||||
behavior="tool"
|
||||
stack={component.stack}
|
||||
source={component.source}
|
||||
>
|
||||
@@ -73,8 +75,16 @@ export function ScheduledDetailPage() {
|
||||
|
||||
{component.managed && (
|
||||
<div className="mb-6">
|
||||
<h2 className="text-lg font-semibold mb-3">Logs</h2>
|
||||
<LogViewer name={component.id} />
|
||||
<div className="flex items-center justify-between mb-3">
|
||||
<h2 className="text-lg font-semibold">Logs</h2>
|
||||
<button
|
||||
onClick={() => logRef.current?.clear()}
|
||||
className="flex items-center gap-1.5 px-3 py-1.5 text-sm rounded border border-[var(--border)] text-[var(--muted)] hover:text-[var(--foreground)] hover:border-[var(--foreground)] transition-colors"
|
||||
>
|
||||
<Trash2 size={14} /> Clear
|
||||
</button>
|
||||
</div>
|
||||
<LogViewer ref={logRef} name={component.id} />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import { useRef } from "react"
|
||||
import { useParams } from "react-router-dom"
|
||||
import { Server, ExternalLink, Terminal } from "lucide-react"
|
||||
import { useComponent, useStatus, useEventStream, useCaddyfile } from "@/services/api/hooks"
|
||||
import { Server, ExternalLink, Terminal, Trash2 } from "lucide-react"
|
||||
import { useService, useStatus, useEventStream, useCaddyfile } from "@/services/api/hooks"
|
||||
import { runnerLabel } from "@/lib/labels"
|
||||
import { HealthBadge } from "@/components/HealthBadge"
|
||||
import { LogViewer } from "@/components/LogViewer"
|
||||
import { LogViewer, type LogViewerHandle } from "@/components/LogViewer"
|
||||
import { DetailHeader } from "@/components/detail/DetailHeader"
|
||||
import { ServiceControls } from "@/components/detail/ServiceControls"
|
||||
import { SystemdPanel } from "@/components/detail/SystemdPanel"
|
||||
@@ -11,8 +12,9 @@ import { ConfigPanel } from "@/components/detail/ConfigPanel"
|
||||
|
||||
export function ServiceDetailPage() {
|
||||
useEventStream()
|
||||
const logRef = useRef<LogViewerHandle>(null)
|
||||
const { name } = useParams<{ name: string }>()
|
||||
const { data: component, isLoading, error, refetch } = useComponent(name ?? "")
|
||||
const { data: component, isLoading, error, refetch } = useService(name ?? "")
|
||||
const { data: statusResp } = useStatus()
|
||||
const health = statusResp?.statuses.find((s) => s.id === name)
|
||||
const isGateway = name === "castle-gateway"
|
||||
@@ -41,7 +43,7 @@ export function ServiceDetailPage() {
|
||||
backTo="/"
|
||||
backLabel="Back to Services"
|
||||
name={component.id}
|
||||
behavior={component.behavior}
|
||||
behavior="daemon"
|
||||
stack={component.stack}
|
||||
source={component.source}
|
||||
>
|
||||
@@ -129,8 +131,16 @@ export function ServiceDetailPage() {
|
||||
|
||||
{component.managed && (
|
||||
<div className="mb-6">
|
||||
<h2 className="text-lg font-semibold mb-3">Logs</h2>
|
||||
<LogViewer name={component.id} />
|
||||
<div className="flex items-center justify-between mb-3">
|
||||
<h2 className="text-lg font-semibold">Logs</h2>
|
||||
<button
|
||||
onClick={() => logRef.current?.clear()}
|
||||
className="flex items-center gap-1.5 px-3 py-1.5 text-sm rounded border border-[var(--border)] text-[var(--muted)] hover:text-[var(--foreground)] hover:border-[var(--foreground)] transition-colors"
|
||||
>
|
||||
<Trash2 size={14} /> Clear
|
||||
</button>
|
||||
</div>
|
||||
<LogViewer ref={logRef} name={component.id} />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user