wip: domains page
This commit is contained in:
parent
59a66491b1
commit
c2b84fbe9f
|
@ -2,6 +2,7 @@
|
|||
|
||||
import { formatDistanceToNowStrict } from "date-fns";
|
||||
import { ClipboardIcon } from "lucide-react";
|
||||
import { useEffect, useState } from "react";
|
||||
import { CgSpinner } from "react-icons/cg";
|
||||
import { FaGear } from "react-icons/fa6";
|
||||
import { toast } from "sonner";
|
||||
|
@ -17,16 +18,28 @@ import { TableCell, TableRow } from "~/components/ui/table";
|
|||
import { type RouterOutputs } from "~/trpc/shared";
|
||||
|
||||
export function ContainerEntry({
|
||||
data: { container, previous, task, slot },
|
||||
data: { container, previous, task },
|
||||
}: {
|
||||
data: RouterOutputs["projects"]["services"]["containers"]["latest"][number];
|
||||
}) {
|
||||
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 (
|
||||
<>
|
||||
<TableRow key={slot}>
|
||||
<TableRow className="!border-b-0">
|
||||
<TableCell
|
||||
className="cursor-pointer font-mono text-sm text-muted-foreground"
|
||||
onClick={() => {
|
||||
|
@ -65,30 +78,17 @@ export function ContainerEntry({
|
|||
strokeWidth={1.5}
|
||||
className="py-auto mr-2 inline-block animate-spin stroke-muted-foreground"
|
||||
/>
|
||||
Redeploying
|
||||
{previous[0]?.container?.error && (
|
||||
<span>
|
||||
{" "}
|
||||
(previous container errored with:{" "}
|
||||
{previous[0]?.container?.error})
|
||||
</span>
|
||||
)}
|
||||
Container Deploying
|
||||
</span>
|
||||
) : (
|
||||
task.taskState ?? task.taskMessage ?? "unknown"
|
||||
)}
|
||||
|
||||
{container?.error && (
|
||||
<span className="text-red-500">
|
||||
{" "}
|
||||
(errored with: {container.error})
|
||||
<span className="capitalize">
|
||||
{task.taskState ?? task.taskMessage ?? "unknown"}
|
||||
</span>
|
||||
)}
|
||||
</TableCell>
|
||||
|
||||
<TableCell>{uptimeText ?? "N/A"}</TableCell>
|
||||
<TableCell>{mainContainer?.node ?? "unknown"}</TableCell>
|
||||
<TableCell>
|
||||
{formatDistanceToNowStrict(mainContainer?.containerCreatedAt ?? 0)}
|
||||
</TableCell>
|
||||
<TableCell>{mainContainer?.cpu ?? "?"}</TableCell>
|
||||
<TableCell>{mainContainer?.memory ?? "?"}</TableCell>
|
||||
<TableCell>
|
||||
|
@ -110,6 +110,23 @@ export function ContainerEntry({
|
|||
</DropdownMenu>
|
||||
</TableCell>
|
||||
</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>
|
||||
<TableRow>
|
||||
<TableHead className="w-36">Container ID</TableHead>
|
||||
<TableHead className="w-full">Status</TableHead>
|
||||
<TableHead>Node</TableHead>
|
||||
<TableHead className="w-[80%]">Status</TableHead>
|
||||
<TableHead>Uptime</TableHead>
|
||||
<TableHead>CPU Usage</TableHead>
|
||||
<TableHead>Memory Usage</TableHead>
|
||||
<TableHead>Node</TableHead>
|
||||
<TableHead>CPU</TableHead>
|
||||
<TableHead>Memory</TableHead>
|
||||
<TableHead>Network (RX/TX)</TableHead>
|
||||
<TableHead className="text-right">Actions</TableHead>
|
||||
</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: {
|
||||
driver: "json-file",
|
||||
options: {
|
||||
"max-size": service.loggingMaxSize,
|
||||
"max-size":
|
||||
service.loggingMaxSize === "-1" ? null : service.loggingMaxSize,
|
||||
"max-file": service.loggingMaxFiles,
|
||||
},
|
||||
},
|
||||
|
|
Loading…
Reference in a new issue