wip: domains page
This commit is contained in:
parent
59a66491b1
commit
c2b84fbe9f
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
import { formatDistanceToNowStrict } from "date-fns";
|
import { formatDistanceToNowStrict } from "date-fns";
|
||||||
import { ClipboardIcon } from "lucide-react";
|
import { ClipboardIcon } from "lucide-react";
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
import { CgSpinner } from "react-icons/cg";
|
import { CgSpinner } from "react-icons/cg";
|
||||||
import { FaGear } from "react-icons/fa6";
|
import { FaGear } from "react-icons/fa6";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
|
@ -17,16 +18,28 @@ import { TableCell, TableRow } from "~/components/ui/table";
|
||||||
import { type RouterOutputs } from "~/trpc/shared";
|
import { type RouterOutputs } from "~/trpc/shared";
|
||||||
|
|
||||||
export function ContainerEntry({
|
export function ContainerEntry({
|
||||||
data: { container, previous, task, slot },
|
data: { container, previous, task },
|
||||||
}: {
|
}: {
|
||||||
data: RouterOutputs["projects"]["services"]["containers"]["latest"][number];
|
data: RouterOutputs["projects"]["services"]["containers"]["latest"][number];
|
||||||
}) {
|
}) {
|
||||||
const mainContainer = container ?? previous[0]?.container;
|
const mainContainer = container ?? previous[0]?.container;
|
||||||
const isRedeploying = container === undefined;
|
const isRedeploying =
|
||||||
|
container === undefined || task.taskState === "starting";
|
||||||
|
|
||||||
|
const [uptimeText, setUptimeText] = useState<string | null>(null);
|
||||||
|
useEffect(() => {
|
||||||
|
if (!mainContainer) return;
|
||||||
|
const interval = setInterval(() => {
|
||||||
|
setUptimeText(
|
||||||
|
formatDistanceToNowStrict(mainContainer.containerCreatedAt),
|
||||||
|
);
|
||||||
|
}, 1000);
|
||||||
|
return () => clearInterval(interval);
|
||||||
|
}, [mainContainer]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<TableRow key={slot}>
|
<TableRow className="!border-b-0">
|
||||||
<TableCell
|
<TableCell
|
||||||
className="cursor-pointer font-mono text-sm text-muted-foreground"
|
className="cursor-pointer font-mono text-sm text-muted-foreground"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
|
@ -65,30 +78,17 @@ export function ContainerEntry({
|
||||||
strokeWidth={1.5}
|
strokeWidth={1.5}
|
||||||
className="py-auto mr-2 inline-block animate-spin stroke-muted-foreground"
|
className="py-auto mr-2 inline-block animate-spin stroke-muted-foreground"
|
||||||
/>
|
/>
|
||||||
Redeploying
|
Container Deploying
|
||||||
{previous[0]?.container?.error && (
|
|
||||||
<span>
|
|
||||||
{" "}
|
|
||||||
(previous container errored with:{" "}
|
|
||||||
{previous[0]?.container?.error})
|
|
||||||
</span>
|
|
||||||
)}
|
|
||||||
</span>
|
</span>
|
||||||
) : (
|
) : (
|
||||||
task.taskState ?? task.taskMessage ?? "unknown"
|
<span className="capitalize">
|
||||||
)}
|
{task.taskState ?? task.taskMessage ?? "unknown"}
|
||||||
|
|
||||||
{container?.error && (
|
|
||||||
<span className="text-red-500">
|
|
||||||
{" "}
|
|
||||||
(errored with: {container.error})
|
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
|
|
||||||
|
<TableCell>{uptimeText ?? "N/A"}</TableCell>
|
||||||
<TableCell>{mainContainer?.node ?? "unknown"}</TableCell>
|
<TableCell>{mainContainer?.node ?? "unknown"}</TableCell>
|
||||||
<TableCell>
|
|
||||||
{formatDistanceToNowStrict(mainContainer?.containerCreatedAt ?? 0)}
|
|
||||||
</TableCell>
|
|
||||||
<TableCell>{mainContainer?.cpu ?? "?"}</TableCell>
|
<TableCell>{mainContainer?.cpu ?? "?"}</TableCell>
|
||||||
<TableCell>{mainContainer?.memory ?? "?"}</TableCell>
|
<TableCell>{mainContainer?.memory ?? "?"}</TableCell>
|
||||||
<TableCell>
|
<TableCell>
|
||||||
|
@ -110,6 +110,23 @@ export function ContainerEntry({
|
||||||
</DropdownMenu>
|
</DropdownMenu>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
|
|
||||||
|
{container?.error !== undefined ||
|
||||||
|
previous[0]?.container?.error !== undefined ? (
|
||||||
|
<TableRow className="hover:bg-inherit">
|
||||||
|
<TableCell></TableCell>
|
||||||
|
<TableCell colSpan={8} className="mb-4 text-red-500">
|
||||||
|
{container?.error
|
||||||
|
? "Container exited with:"
|
||||||
|
: "Previous container exited with:"}{" "}
|
||||||
|
<br />
|
||||||
|
<pre className="whitespace-pre-wrap font-mono text-xs">
|
||||||
|
{container?.error ?? previous[0]?.container?.error}
|
||||||
|
</pre>
|
||||||
|
<br />
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
) : undefined}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,11 +30,11 @@ export default function Containers() {
|
||||||
<TableHeader>
|
<TableHeader>
|
||||||
<TableRow>
|
<TableRow>
|
||||||
<TableHead className="w-36">Container ID</TableHead>
|
<TableHead className="w-36">Container ID</TableHead>
|
||||||
<TableHead className="w-full">Status</TableHead>
|
<TableHead className="w-[80%]">Status</TableHead>
|
||||||
<TableHead>Node</TableHead>
|
|
||||||
<TableHead>Uptime</TableHead>
|
<TableHead>Uptime</TableHead>
|
||||||
<TableHead>CPU Usage</TableHead>
|
<TableHead>Node</TableHead>
|
||||||
<TableHead>Memory Usage</TableHead>
|
<TableHead>CPU</TableHead>
|
||||||
|
<TableHead>Memory</TableHead>
|
||||||
<TableHead>Network (RX/TX)</TableHead>
|
<TableHead>Network (RX/TX)</TableHead>
|
||||||
<TableHead className="text-right">Actions</TableHead>
|
<TableHead className="text-right">Actions</TableHead>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
|
|
|
@ -0,0 +1,68 @@
|
||||||
|
"use client";
|
||||||
|
|
||||||
|
import { useFieldArray } from "react-hook-form";
|
||||||
|
import { z } from "zod";
|
||||||
|
import { Form } from "~/components/ui/form";
|
||||||
|
import { FormSubmit, SimpleFormField, useForm } from "~/hooks/forms";
|
||||||
|
|
||||||
|
const formValidator = z.object({
|
||||||
|
domains: z.array(
|
||||||
|
z.object({
|
||||||
|
domain: z
|
||||||
|
.string()
|
||||||
|
.regex(
|
||||||
|
/(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z0-9][a-z0-9-]{0,61}[a-z0-9]/,
|
||||||
|
{ message: "Invalid domain name" },
|
||||||
|
),
|
||||||
|
|
||||||
|
internalPort: z.number().int().min(1).max(65535),
|
||||||
|
https: z.boolean(),
|
||||||
|
forceSSL: z.boolean(),
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
});
|
||||||
|
|
||||||
|
export default function DomainsList(
|
||||||
|
{
|
||||||
|
// service,
|
||||||
|
}: {
|
||||||
|
// service: RouterOutputs["projects"]["services"]["get"];
|
||||||
|
},
|
||||||
|
) {
|
||||||
|
const form = useForm(formValidator, {
|
||||||
|
defaultValues: {},
|
||||||
|
});
|
||||||
|
const { fields, append, prepend, remove, swap, move, insert } = useFieldArray(
|
||||||
|
{
|
||||||
|
control: form.control,
|
||||||
|
name: "domains",
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Form {...form}>
|
||||||
|
<form
|
||||||
|
onSubmit={form.handleSubmit(async (data) => {
|
||||||
|
console.log(data);
|
||||||
|
})}
|
||||||
|
className="grid grid-cols-2 gap-4"
|
||||||
|
>
|
||||||
|
<h1 className="col-span-2">Domains</h1>
|
||||||
|
|
||||||
|
{fields.map((field, index) => (
|
||||||
|
<div className="flex gap-4" key={field.id}>
|
||||||
|
<SimpleFormField
|
||||||
|
control={form.control}
|
||||||
|
name={`domains.${index}.domain`}
|
||||||
|
friendlyName="Domain"
|
||||||
|
required
|
||||||
|
className="flex-1"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
|
||||||
|
<FormSubmit form={form} className="col-span-2" />
|
||||||
|
</form>
|
||||||
|
</Form>
|
||||||
|
);
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
export default function DomainsPage() {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<Form
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
|
@ -110,7 +110,8 @@ export async function buildDockerStackFile(
|
||||||
logging: {
|
logging: {
|
||||||
driver: "json-file",
|
driver: "json-file",
|
||||||
options: {
|
options: {
|
||||||
"max-size": service.loggingMaxSize,
|
"max-size":
|
||||||
|
service.loggingMaxSize === "-1" ? null : service.loggingMaxSize,
|
||||||
"max-file": service.loggingMaxFiles,
|
"max-file": service.loggingMaxFiles,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
Loading…
Reference in a new issue