+
{row.getValue("name")}
@@ -115,7 +116,9 @@ export const columns: ColumnDef
[] = [
header: () => Endpoint
,
cell: ({ row }) => {
return (
- {row.original.endpoint}
+
+ {row.original.endpoint}
+
);
},
},
@@ -123,7 +126,11 @@ export const columns: ColumnDef[] = [
accessorKey: "type",
header: () => Type
,
cell: ({ row }) => {
- return {row.original.type}
;
+ return (
+
+ {row.original.type}
+
+ );
},
},
{
@@ -133,7 +140,7 @@ export const columns: ColumnDef[] = [
header: ({ column }) => {
return (
column.toggleSorting(column.getIsSorted() === "asc")}
>
Update Date
@@ -195,22 +202,50 @@ export const columns: ColumnDef[] = [
Edit
-
+ snapshot: {
+ fieldType: "snapshot",
+ },
+ models: {
+ fieldType: "models",
+ },
+ }}
+ />
+ ) : (
+
+ )}
);
},
@@ -273,8 +308,16 @@ export function MachineList({ data }: { data: Machine[] }) {
fieldType: "fallback",
inputProps: {
disabled: true,
+ showLabel: false,
+ type: "hidden",
},
},
+ snapshot: {
+ fieldType: "snapshot",
+ },
+ models: {
+ fieldType: "models",
+ },
}}
/>
diff --git a/web/src/components/VersionSelect.tsx b/web/src/components/VersionSelect.tsx
index 9da1669..3ba2dd9 100644
--- a/web/src/components/VersionSelect.tsx
+++ b/web/src/components/VersionSelect.tsx
@@ -191,9 +191,11 @@ export function RunWorkflowButton({
- Run inputs
+ Confirm run
- Run your workflow with custom inputs
+ {schema
+ ? "Run your workflow with custom inputs"
+ : "Confirm to run your workflow"}
{/* */}
@@ -203,6 +205,7 @@ export function RunWorkflowButton({
values={values}
onValuesChange={setValues}
onSubmit={runWorkflow}
+ className="px-1"
>
diff --git a/web/src/components/custom-form/ModelPickerView.tsx b/web/src/components/custom-form/ModelPickerView.tsx
new file mode 100644
index 0000000..5c1abe8
--- /dev/null
+++ b/web/src/components/custom-form/ModelPickerView.tsx
@@ -0,0 +1,157 @@
+"use client";
+
+import type { AutoFormInputComponentProps } from "../ui/auto-form/types";
+import { Button } from "@/components/ui/button";
+import {
+ Command,
+ CommandEmpty,
+ CommandGroup,
+ CommandInput,
+ CommandItem,
+ CommandList,
+} from "@/components/ui/command";
+import {
+ Popover,
+ PopoverContent,
+ PopoverTrigger,
+} from "@/components/ui/popover";
+import { ScrollArea } from "@/components/ui/scroll-area";
+import { cn } from "@/lib/utils";
+import { Check, ChevronsUpDown } from "lucide-react";
+import * as React from "react";
+import { useRef } from "react";
+import { z } from "zod";
+
+const Model = z.object({
+ name: z.string(),
+ type: z.string(),
+ base: z.string(),
+ save_path: z.string(),
+ description: z.string(),
+ reference: z.string(),
+ filename: z.string(),
+ url: z.string(),
+});
+
+const ModelList = z.array(Model);
+
+export const ModelListWrapper = z.object({
+ models: ModelList,
+});
+
+export function ModelPickerView({
+ field,
+}: Pick) {
+ const value = (field.value as z.infer) ?? [];
+
+ const [open, setOpen] = React.useState(false);
+
+ const [modelList, setModelList] =
+ React.useState>();
+
+ // const [selectedModels, setSelectedModels] = React.useState<
+ // z.infer
+ // >(field.value ?? []);
+
+ React.useEffect(() => {
+ const controller = new AbortController();
+ fetch(
+ "https://raw.githubusercontent.com/ltdrdata/ComfyUI-Manager/main/model-list.json",
+ {
+ signal: controller.signal,
+ }
+ )
+ .then((x) => x.json())
+ .then((a) => {
+ setModelList(ModelListWrapper.parse(a));
+ });
+
+ return () => {
+ controller.abort();
+ };
+ }, []);
+
+ function toggleModel(model: z.infer) {
+ const prevSelectedModels = value;
+ if (
+ prevSelectedModels.some(
+ (selectedModel) =>
+ selectedModel.url + selectedModel.name === model.url + model.name
+ )
+ ) {
+ field.onChange(
+ prevSelectedModels.filter(
+ (selectedModel) =>
+ selectedModel.url + selectedModel.name !== model.url + model.name
+ )
+ );
+ } else {
+ field.onChange([...prevSelectedModels, model]);
+ }
+ }
+
+ // React.useEffect(() => {
+ // field.onChange(selectedModels);
+ // }, [selectedModels]);
+
+ const containerRef = useRef(null);
+
+ return (
+
+
+
+
+ Select models... ({value.length} selected)
+
+
+
+
+
+
+ No framework found.
+
+
+ {modelList?.models.map((model) => (
+ {
+ toggleModel(model);
+ // Update field.onChange to pass the array of selected models
+ }}
+ >
+ {model.name}
+ selectedModel.url === model.url
+ )
+ ? "opacity-100"
+ : "opacity-0"
+ )}
+ />
+
+ ))}
+
+
+
+
+
+ {field.value && (
+
+
+
+ {JSON.stringify(field.value, null, 2)}
+
+
+
+ )}
+
+ );
+}
diff --git a/web/src/components/custom-form/SnapshotPickerView.tsx b/web/src/components/custom-form/SnapshotPickerView.tsx
new file mode 100644
index 0000000..675393c
--- /dev/null
+++ b/web/src/components/custom-form/SnapshotPickerView.tsx
@@ -0,0 +1,125 @@
+"use client";
+
+import type { AutoFormInputComponentProps } from "../ui/auto-form/types";
+import { Button } from "@/components/ui/button";
+import {
+ Command,
+ CommandEmpty,
+ CommandGroup,
+ CommandInput,
+ CommandItem,
+} from "@/components/ui/command";
+import {
+ Popover,
+ PopoverContent,
+ PopoverTrigger,
+} from "@/components/ui/popover";
+import { ScrollArea } from "@/components/ui/scroll-area";
+import { cn } from "@/lib/utils";
+import { findAllDeployments } from "@/server/curdDeploments";
+import { Check, ChevronsUpDown } from "lucide-react";
+import * as React from "react";
+
+export function SnapshotPickerView({
+ field,
+}: Pick) {
+ const [open, setOpen] = React.useState(false);
+ const [selected, setSelected] = React.useState(null);
+
+ const [frameworks, setFramework] = React.useState<
+ {
+ id: string;
+ label: string;
+ value: string;
+ }[]
+ >();
+
+ React.useEffect(() => {
+ findAllDeployments().then((a) => {
+ console.log(a);
+
+ const frameworks = a
+ .map((item) => {
+ if (
+ item.deployments.length == 0 ||
+ item.deployments[0].version.snapshot == null
+ )
+ return null;
+
+ return {
+ id: item.deployments[0].version.id,
+ label: `${item.name} - ${item.deployments[0].environment}`,
+ value: JSON.stringify(item.deployments[0].version.snapshot),
+ };
+ })
+ .filter((item): item is NonNullable => item != null);
+
+ setFramework(frameworks);
+ });
+ }, []);
+
+ function findItem(value: string) {
+ // console.log(frameworks);
+
+ return frameworks?.find((item) => item.id === value);
+ }
+
+ return (
+
+
+
+
+ {selected ? findItem(selected)?.label : "Select snapshot..."}
+
+
+
+
+
+
+ No framework found.
+
+ {frameworks?.map((framework) => (
+ {
+ setSelected(currentValue);
+ const json =
+ frameworks?.find((item) => item.id === currentValue)
+ ?.value ?? null;
+ field.onChange(json ? JSON.parse(json) : null);
+ setOpen(false);
+ }}
+ >
+ {framework.label}
+
+
+ ))}
+
+
+
+
+ {field.value && (
+
+
+
+ {JSON.stringify(field.value, null, 2)}
+
+
+
+ )}
+
+ );
+}
diff --git a/web/src/components/custom-form/model-picker.tsx b/web/src/components/custom-form/model-picker.tsx
new file mode 100644
index 0000000..0d2d0fe
--- /dev/null
+++ b/web/src/components/custom-form/model-picker.tsx
@@ -0,0 +1,39 @@
+import type { AutoFormInputComponentProps } from "../ui/auto-form/types";
+import {
+ FormControl,
+ FormDescription,
+ FormItem,
+ FormLabel,
+ FormMessage,
+} from "../ui/form";
+import { LoadingIcon } from "@/components/LoadingIcon";
+import { ModelPickerView } from "@/components/custom-form/ModelPickerView";
+// import { CaretSortIcon, CheckIcon } from "@radix-ui/react-icons";
+import * as React from "react";
+import { Suspense } from "react";
+
+export default function AutoFormModelsPicker({
+ label,
+ isRequired,
+ field,
+ fieldConfigItem,
+ zodItem,
+}: AutoFormInputComponentProps) {
+ return (
+
+
+ {label}
+ {isRequired && * }
+
+
+ }>
+
+
+
+ {fieldConfigItem.description && (
+ {fieldConfigItem.description}
+ )}
+
+
+ );
+}
diff --git a/web/src/components/custom-form/snapshot-picker.tsx b/web/src/components/custom-form/snapshot-picker.tsx
new file mode 100644
index 0000000..e825173
--- /dev/null
+++ b/web/src/components/custom-form/snapshot-picker.tsx
@@ -0,0 +1,39 @@
+import type { AutoFormInputComponentProps } from "../ui/auto-form/types";
+import {
+ FormControl,
+ FormDescription,
+ FormItem,
+ FormLabel,
+ FormMessage,
+} from "../ui/form";
+import { SnapshotPickerView } from "./SnapshotPickerView";
+import { LoadingIcon } from "@/components/LoadingIcon";
+// import { CaretSortIcon, CheckIcon } from "@radix-ui/react-icons";
+import * as React from "react";
+import { Suspense } from "react";
+
+export default function AutoFormSnapshotPicker({
+ label,
+ isRequired,
+ field,
+ fieldConfigItem,
+ zodItem,
+}: AutoFormInputComponentProps) {
+ return (
+
+
+ {label}
+ {isRequired && * }
+
+
+ }>
+
+
+
+ {fieldConfigItem.description && (
+ {fieldConfigItem.description}
+ )}
+
+
+ );
+}
diff --git a/web/src/components/ui/auto-form/config.ts b/web/src/components/ui/auto-form/config.ts
index b94b9b0..4fdb9ea 100644
--- a/web/src/components/ui/auto-form/config.ts
+++ b/web/src/components/ui/auto-form/config.ts
@@ -6,6 +6,8 @@ import AutoFormNumber from "./fields/number";
import AutoFormRadioGroup from "./fields/radio-group";
import AutoFormSwitch from "./fields/switch";
import AutoFormTextarea from "./fields/textarea";
+import AutoFormModelsPicker from "@/components/custom-form/model-picker";
+import AutoFormSnapshotPicker from "@/components/custom-form/snapshot-picker";
export const INPUT_COMPONENTS = {
checkbox: AutoFormCheckbox,
@@ -16,6 +18,10 @@ export const INPUT_COMPONENTS = {
textarea: AutoFormTextarea,
number: AutoFormNumber,
fallback: AutoFormInput,
+
+ // Customs
+ snapshot: AutoFormSnapshotPicker,
+ models: AutoFormModelsPicker,
};
/**
diff --git a/web/src/components/ui/auto-form/fields/input.tsx b/web/src/components/ui/auto-form/fields/input.tsx
index 242556b..dbc7fee 100644
--- a/web/src/components/ui/auto-form/fields/input.tsx
+++ b/web/src/components/ui/auto-form/fields/input.tsx
@@ -6,7 +6,7 @@ import {
FormMessage,
} from "../../form";
import { Input } from "../../input";
-import { AutoFormInputComponentProps } from "../types";
+import type { AutoFormInputComponentProps } from "../types";
export default function AutoFormInput({
label,
diff --git a/web/src/components/ui/auto-form/index.tsx b/web/src/components/ui/auto-form/index.tsx
index fa5d7e3..c2b7505 100644
--- a/web/src/components/ui/auto-form/index.tsx
+++ b/web/src/components/ui/auto-form/index.tsx
@@ -6,6 +6,7 @@ import type { FieldConfig } from "./types";
import type { ZodObjectOrWrapped } from "./utils";
import { getDefaultValues, getObjectFormSchema } from "./utils";
import AutoFormObject from "@/components/ui/auto-form/fields/object";
+import { ScrollArea } from "@/components/ui/scroll-area";
import { cn } from "@/lib/utils";
import { zodResolver } from "@hookform/resolvers/zod";
import type { DefaultValues } from "react-hook-form";
@@ -68,11 +69,15 @@ function AutoForm({
}}
className={cn("space-y-5", className)}
>
-
+
+
+
{children}
diff --git a/web/src/components/ui/auto-form/types.ts b/web/src/components/ui/auto-form/types.ts
index d0e2ab6..50d55bc 100644
--- a/web/src/components/ui/auto-form/types.ts
+++ b/web/src/components/ui/auto-form/types.ts
@@ -1,6 +1,6 @@
-import { ControllerRenderProps, FieldValues } from "react-hook-form";
-import * as z from "zod";
-import { INPUT_COMPONENTS } from "./config";
+import type { INPUT_COMPONENTS } from "./config";
+import type { ControllerRenderProps, FieldValues } from "react-hook-form";
+import type * as z from "zod";
export type FieldConfigItem = {
description?: React.ReactNode;
diff --git a/web/src/components/ui/command.tsx b/web/src/components/ui/command.tsx
new file mode 100644
index 0000000..c2d1b36
--- /dev/null
+++ b/web/src/components/ui/command.tsx
@@ -0,0 +1,154 @@
+"use client";
+
+import { Dialog, DialogContent } from "@/components/ui/dialog";
+import { cn } from "@/lib/utils";
+import { type DialogProps } from "@radix-ui/react-dialog";
+import { Command as CommandPrimitive } from "cmdk";
+import { Search } from "lucide-react";
+import * as React from "react";
+
+const Command = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+));
+Command.displayName = CommandPrimitive.displayName;
+
+interface CommandDialogProps extends DialogProps {}
+
+const CommandDialog = ({ children, ...props }: CommandDialogProps) => {
+ return (
+
+
+
+ {children}
+
+
+
+ );
+};
+
+const CommandInput = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+
+
+
+));
+
+CommandInput.displayName = CommandPrimitive.Input.displayName;
+
+const CommandList = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+));
+
+CommandList.displayName = CommandPrimitive.List.displayName;
+
+const CommandEmpty = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>((props, ref) => (
+
+));
+
+CommandEmpty.displayName = CommandPrimitive.Empty.displayName;
+
+const CommandGroup = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+));
+
+CommandGroup.displayName = CommandPrimitive.Group.displayName;
+
+const CommandSeparator = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+));
+CommandSeparator.displayName = CommandPrimitive.Separator.displayName;
+
+const CommandItem = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+));
+
+CommandItem.displayName = CommandPrimitive.Item.displayName;
+
+const CommandShortcut = ({
+ className,
+ ...props
+}: React.HTMLAttributes) => {
+ return (
+
+ );
+};
+CommandShortcut.displayName = "CommandShortcut";
+
+export {
+ Command,
+ CommandDialog,
+ CommandInput,
+ CommandList,
+ CommandEmpty,
+ CommandGroup,
+ CommandItem,
+ CommandShortcut,
+ CommandSeparator,
+};
diff --git a/web/src/components/ui/input.tsx b/web/src/components/ui/input.tsx
index 677d05f..78d3d19 100644
--- a/web/src/components/ui/input.tsx
+++ b/web/src/components/ui/input.tsx
@@ -1,6 +1,5 @@
-import * as React from "react"
-
-import { cn } from "@/lib/utils"
+import { cn } from "@/lib/utils";
+import * as React from "react";
export interface InputProps
extends React.InputHTMLAttributes {}
@@ -17,9 +16,9 @@ const Input = React.forwardRef(
ref={ref}
{...props}
/>
- )
+ );
}
-)
-Input.displayName = "Input"
+);
+Input.displayName = "Input";
-export { Input }
+export { Input };
diff --git a/web/src/components/ui/popover.tsx b/web/src/components/ui/popover.tsx
index a0ec48b..ef29e37 100644
--- a/web/src/components/ui/popover.tsx
+++ b/web/src/components/ui/popover.tsx
@@ -1,13 +1,12 @@
-"use client"
+"use client";
-import * as React from "react"
-import * as PopoverPrimitive from "@radix-ui/react-popover"
+import { cn } from "@/lib/utils";
+import * as PopoverPrimitive from "@radix-ui/react-popover";
+import * as React from "react";
-import { cn } from "@/lib/utils"
+const Popover = PopoverPrimitive.Root;
-const Popover = PopoverPrimitive.Root
-
-const PopoverTrigger = PopoverPrimitive.Trigger
+const PopoverTrigger = PopoverPrimitive.Trigger;
const PopoverContent = React.forwardRef<
React.ElementRef,
@@ -22,10 +21,24 @@ const PopoverContent = React.forwardRef<
"z-50 w-72 rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
className
)}
+ // https://github.com/shadcn-ui/ui/pull/2123/files#diff-e43c79299129c57a9055c3d6a20ff7bbeea4035dd6aa80eebe381b29f82f90a8
+ onWheel={(e) => {
+ e.stopPropagation();
+ const isScrollingDown = e.deltaY > 0;
+ if (isScrollingDown) {
+ e.currentTarget.dispatchEvent(
+ new KeyboardEvent("keydown", { key: "ArrowDown" })
+ );
+ } else {
+ e.currentTarget.dispatchEvent(
+ new KeyboardEvent("keydown", { key: "ArrowUp" })
+ );
+ }
+ }}
{...props}
/>
-))
-PopoverContent.displayName = PopoverPrimitive.Content.displayName
+));
+PopoverContent.displayName = PopoverPrimitive.Content.displayName;
-export { Popover, PopoverTrigger, PopoverContent }
+export { Popover, PopoverTrigger, PopoverContent };
diff --git a/web/src/db/schema.ts b/web/src/db/schema.ts
index 694b999..8b7b884 100644
--- a/web/src/db/schema.ts
+++ b/web/src/db/schema.ts
@@ -37,6 +37,7 @@ export const workflowTable = dbSchema.table("workflows", {
export const workflowRelations = relations(workflowTable, ({ many }) => ({
versions: many(workflowVersionTable),
+ deployments: many(deploymentsTable),
}));
export const workflowType = z.any();
@@ -203,6 +204,7 @@ export const machinesTable = dbSchema.table("machines", {
type: machinesType("type").notNull().default("classic"),
status: machinesStatus("status").notNull().default("ready"),
snapshot: jsonb("snapshot").$type(),
+ models: jsonb("models").$type(),
build_log: text("build_log"),
});
@@ -255,6 +257,10 @@ export const deploymentsRelations = relations(deploymentsTable, ({ one }) => ({
fields: [deploymentsTable.workflow_version_id],
references: [workflowVersionTable.id],
}),
+ workflow: one(workflowTable, {
+ fields: [deploymentsTable.workflow_id],
+ references: [workflowTable.id],
+ }),
}));
export const apiKeyTable = dbSchema.table("api_keys", {
diff --git a/web/src/server/addMachineSchema.ts b/web/src/server/addMachineSchema.ts
index 9d36624..639def4 100644
--- a/web/src/server/addMachineSchema.ts
+++ b/web/src/server/addMachineSchema.ts
@@ -17,4 +17,5 @@ export const addCustomMachineSchema = insertCustomMachineSchema.pick({
name: true,
type: true,
snapshot: true,
+ models: true,
});
diff --git a/web/src/server/curdDeploments.ts b/web/src/server/curdDeploments.ts
index 74dc3f2..ff0e89c 100644
--- a/web/src/server/curdDeploments.ts
+++ b/web/src/server/curdDeploments.ts
@@ -1,9 +1,9 @@
"use server";
import { db } from "@/db/db";
-import { deploymentsTable } from "@/db/schema";
+import { deploymentsTable, workflowTable } from "@/db/schema";
import { auth } from "@clerk/nextjs";
-import { and, eq } from "drizzle-orm";
+import { and, eq, isNull } from "drizzle-orm";
import { revalidatePath } from "next/cache";
import "server-only";
@@ -47,3 +47,36 @@ export async function createDeployments(
message: `Successfully created deployment for ${environment}`,
};
}
+
+export async function findAllDeployments() {
+ const { userId, orgId } = auth();
+ if (!userId) throw new Error("No user id");
+
+ const deployments = await db.query.workflowTable.findMany({
+ where: and(
+ orgId
+ ? eq(workflowTable.org_id, orgId)
+ : and(eq(workflowTable.user_id, userId), isNull(workflowTable.org_id))
+ ),
+ columns: {
+ name: true,
+ },
+ with: {
+ deployments: {
+ columns: {
+ environment: true,
+ },
+ with: {
+ version: {
+ columns: {
+ id: true,
+ snapshot: true,
+ },
+ },
+ },
+ },
+ },
+ });
+
+ return deployments;
+}
diff --git a/web/src/server/curdMachine.ts b/web/src/server/curdMachine.ts
index 48e886a..3dec0aa 100644
--- a/web/src/server/curdMachine.ts
+++ b/web/src/server/curdMachine.ts
@@ -6,6 +6,7 @@ import type {
} from "./addMachineSchema";
import { withServerPromise } from "./withServerPromise";
import { db } from "@/db/db";
+import type { MachineType } from "@/db/schema";
import { machinesTable } from "@/db/schema";
import { auth } from "@clerk/nextjs";
import { and, eq, isNull } from "drizzle-orm";
@@ -25,7 +26,11 @@ export async function getMachines() {
and(
orgId
? eq(machinesTable.org_id, orgId)
- : eq(machinesTable.user_id, userId),
+ : // make sure org_id is null
+ and(
+ eq(machinesTable.user_id, userId),
+ isNull(machinesTable.org_id)
+ ),
eq(machinesTable.disabled, false)
)
);
@@ -67,10 +72,59 @@ export const addMachine = withServerPromise(
}
);
+export const updateCustomMachine = withServerPromise(
+ async ({
+ id,
+ ...data
+ }: z.infer & {
+ id: string;
+ }) => {
+ const { userId } = auth();
+ if (!userId) return { error: "No user id" };
+
+ const currentMachine = await db.query.machinesTable.findFirst({
+ where: eq(machinesTable.id, id),
+ });
+
+ if (!currentMachine) return { error: "Machine not found" };
+
+ // Check if snapshot or models have changed
+ const snapshotChanged =
+ JSON.stringify(data.snapshot) !== JSON.stringify(currentMachine.snapshot);
+ const modelsChanged =
+ JSON.stringify(data.models) !== JSON.stringify(currentMachine.models);
+
+ // return {
+ // message: `snapshotChanged: ${snapshotChanged}, modelsChanged: ${modelsChanged}`,
+ // };
+
+ await db.update(machinesTable).set(data).where(eq(machinesTable.id, id));
+
+ // If there are changes
+ if (snapshotChanged || modelsChanged) {
+ // Update status to building
+ await db
+ .update(machinesTable)
+ .set({
+ status: "building",
+ endpoint: "not-ready",
+ })
+ .where(eq(machinesTable.id, id));
+
+ // Perform custom build if there are changes
+ await buildMachine(data, currentMachine);
+ redirect(`/machines/${id}`);
+ } else {
+ revalidatePath("/machines");
+ }
+
+ return { message: "Machine Updated" };
+ }
+);
+
export const addCustomMachine = withServerPromise(
async (data: z.infer) => {
const { userId, orgId } = auth();
- const headersList = headers();
if (!userId) return { error: "No user id" };
@@ -88,51 +142,56 @@ export const addCustomMachine = withServerPromise(
const b = a[0];
- // const origin = new URL(request.url).origin;
- const domain = headersList.get("x-forwarded-host") || "";
- const protocol = headersList.get("x-forwarded-proto") || "";
- // console.log("domain", domain);
- // console.log("domain", `${protocol}://${domain}/api/machine-built`);
- // return { message: "Machine Building" };
-
- if (domain === "") {
- throw new Error("No domain");
- }
-
- // Call remote builder
- const result = await fetch(`${process.env.MODAL_BUILDER_URL!}/create`, {
- method: "POST",
- headers: {
- "Content-Type": "application/json",
- },
- body: JSON.stringify({
- machine_id: b.id,
- name: b.id,
- snapshot: JSON.parse(data.snapshot as string),
- callback_url: `${protocol}://${domain}/api/machine-built`,
- }),
- });
-
- if (!result.ok) {
- const error_log = await result.text();
- await db
- .update(machinesTable)
- .set({
- ...data,
- status: "error",
- build_log: error_log,
- })
- .where(eq(machinesTable.id, b.id));
- throw new Error(`Error: ${result.statusText} ${error_log}`);
- }
-
+ await buildMachine(data, b);
redirect(`/machines/${b.id}`);
-
// revalidatePath("/machines");
return { message: "Machine Building" };
}
);
+async function buildMachine(
+ data: z.infer,
+ b: MachineType
+) {
+ const headersList = headers();
+
+ const domain = headersList.get("x-forwarded-host") || "";
+ const protocol = headersList.get("x-forwarded-proto") || "";
+
+ if (domain === "") {
+ throw new Error("No domain");
+ }
+
+ // Call remote builder
+ const result = await fetch(`${process.env.MODAL_BUILDER_URL!}/create`, {
+ method: "POST",
+ headers: {
+ "Content-Type": "application/json",
+ },
+ body: JSON.stringify({
+ machine_id: b.id,
+ name: b.id,
+ snapshot: data.snapshot, //JSON.parse( as string),
+ callback_url: `${protocol}://${domain}/api/machine-built`,
+ models: data.models, //JSON.parse(data.models as string),
+ gpu: "T4",
+ }),
+ });
+
+ if (!result.ok) {
+ const error_log = await result.text();
+ await db
+ .update(machinesTable)
+ .set({
+ ...data,
+ status: "error",
+ build_log: error_log,
+ })
+ .where(eq(machinesTable.id, b.id));
+ throw new Error(`Error: ${result.statusText} ${error_log}`);
+ }
+}
+
export const updateMachine = withServerPromise(
async ({
id,