diff --git a/pkgs/webview-ui/app/src/api/inventory.ts b/pkgs/webview-ui/app/src/api/inventory.ts index 9ad4aa468..9554988d7 100644 --- a/pkgs/webview-ui/app/src/api/inventory.ts +++ b/pkgs/webview-ui/app/src/api/inventory.ts @@ -1,40 +1,101 @@ -import { callApi, ClanService, ServiceNames, Services } from "."; -import { Schema as Inventory } from "@/api/Inventory"; +import { QueryClient } from "@tanstack/solid-query"; +import { callApi, ClanServiceInstance, ServiceNames, Services } from "."; -export async function get_inventory(base_path: string) { - const r = await callApi("get_inventory", { - base_path, +export async function get_inventory(client: QueryClient, base_path: string) { + const data = await client.ensureQueryData({ + queryKey: [base_path, "inventory"], + queryFn: () => { + console.log("Refreshing inventory"); + return callApi("get_inventory", { base_path }); + }, + revalidateIfStale: true, + staleTime: 60 * 1000, }); - if (r.status == "error") { - throw new Error("Failed to get inventory"); - } - const inventory: Inventory = r.data; - return inventory; + + return data; } -export const single_instance_name = ( +export const generate_instance_name = ( machine_name: string, service_name: T, -) => `${machine_name}_${service_name}_0` as const; +) => [machine_name, service_name, 1].filter(Boolean).join("_"); -function get_service(base_path: string, service: T) { - return callApi("get_inventory", { base_path }).then((r) => { - if (r.status == "error") { - return null; - } - const inventory: Inventory = r.data; +export const get_first_instance_name = async ( + client: QueryClient, + base_path: string, + service_name: T, +): Promise => { + const r = await get_inventory(client, base_path); + if (r.status === "success") { + const service = r.data.services?.[service_name]; + if (!service) return null; + return Object.keys(service)[0] || null; + } + return null; +}; - const serviceInstance = inventory.services?.[service]; - return serviceInstance; - }); +async function get_service( + client: QueryClient, + base_path: string, + service_name: T, +) { + const r = await get_inventory(client, base_path); + if (r.status === "success") { + const service = r.data.services?.[service_name]; + return service as Services[T]; + } + return null; } export async function get_single_service( + client: QueryClient, + base_path: string, + service_name: T, +) { + const instance_key = await get_first_instance_name( + client, + base_path, + service_name, + ); + + if (!instance_key) { + return {}; + } + const service: Services[T] | null = await get_service( + client, + base_path, + service_name, + ); + if (service) { + const clanServiceInstance = service[instance_key]; + return clanServiceInstance || {}; + } + return {}; +} + +export async function set_single_service( + client: QueryClient, base_path: string, machine_name: string, service_name: T, + service_config: ClanServiceInstance, ) { - const instance_key = single_instance_name(machine_name, service_name); - const service = await get_service(base_path, "admin"); - return service?.[instance_key]; + const instance_key = + (await get_first_instance_name(client, base_path, service_name)) || + generate_instance_name(machine_name, service_name); + const r = await get_inventory(client, base_path); + if (r.status === "success") { + const inventory = r.data; + inventory.services = inventory.services || {}; + inventory.services[service_name] = inventory.services[service_name] || {}; + // @ts-expect-error: This doesn't check + inventory.services[service_name][instance_key] = service_config; + console.log("saving inventory", inventory); + return callApi("set_inventory", { + inventory, + message: `update_single_service ${service_name}`, + flake_dir: base_path, + }); + } + return r; } diff --git a/pkgs/webview-ui/app/src/index.tsx b/pkgs/webview-ui/app/src/index.tsx index ab94b097a..abbc4137e 100644 --- a/pkgs/webview-ui/app/src/index.tsx +++ b/pkgs/webview-ui/app/src/index.tsx @@ -16,7 +16,7 @@ import { HostList } from "./routes/hosts/view"; import { Welcome } from "./routes/welcome"; import { Toaster } from "solid-toast"; -const client = new QueryClient(); +export const client = new QueryClient(); const root = document.getElementById("app"); diff --git a/pkgs/webview-ui/app/src/routes/clans/details.tsx b/pkgs/webview-ui/app/src/routes/clans/details.tsx index 62e73726e..6f4c4596f 100644 --- a/pkgs/webview-ui/app/src/routes/clans/details.tsx +++ b/pkgs/webview-ui/app/src/routes/clans/details.tsx @@ -24,7 +24,7 @@ import { } from "@modular-forms/solid"; import { TextInput } from "@/src/components/TextInput"; import toast from "solid-toast"; -import { get_single_service } from "@/src/api/inventory"; +import { get_single_service, set_single_service } from "@/src/api/inventory"; interface AdminModuleFormProps { admin: AdminData; @@ -190,20 +190,34 @@ const AdminModuleForm = (props: AdminModuleFormProps) => { const handleSubmit = async (values: AdminSettings) => { console.log("submitting", values, getValues(formStore)); - // const r = await callApi("set_admin_service", { - // base_url: props.base_url, - // allowed_keys: values.allowedKeys.reduce( - // (acc, curr) => ({ ...acc, [curr.name]: curr.value }), - // {} - // ), - // }); - // if (r.status === "success") { - // toast.success("Successfully updated admin settings"); - // } - // if (r.status === "error") { - // toast.error(`Failed to update admin settings: ${r.errors[0].message}`); - toast.error(`Failed to update admin settings: feature disabled`); - // } + const r = await set_single_service( + queryClient, + props.base_url, + "", + "admin", + { + meta: { + name: "admin", + }, + roles: { + default: { + tags: ["all"], + }, + }, + config: { + allowedKeys: values.allowedKeys.reduce( + (acc, curr) => ({ ...acc, [curr.name]: curr.value }), + {}, + ), + }, + }, + ); + if (r.status === "success") { + toast.success("Successfully updated admin settings"); + } + if (r.status === "error") { + toast.error(`Failed to update admin settings: ${r.errors[0].message}`); + } queryClient.invalidateQueries({ queryKey: [props.base_url, "get_admin_service"], }); @@ -340,10 +354,11 @@ type AdminData = ClanServiceInstance<"admin">; export const ClanDetails = () => { const params = useParams(); + const queryClient = useQueryClient(); const clan_dir = window.atob(params.id); // Fetch general meta data const clanQuery = createQuery(() => ({ - queryKey: [clan_dir, "meta"], + queryKey: [clan_dir, "inventory", "meta"], queryFn: async () => { const result = await callApi("show_clan_meta", { uri: clan_dir }); if (result.status === "error") throw new Error("Failed to fetch data"); @@ -352,11 +367,11 @@ export const ClanDetails = () => { })); // Fetch admin settings const adminQuery = createQuery(() => ({ - queryKey: [clan_dir, "get_admin_service"], + queryKey: [clan_dir, "inventory", "services", "admin"], queryFn: async () => { - const result = await get_single_service(clan_dir, "", "admin"); + const result = await get_single_service(queryClient, clan_dir, "admin"); if (!result) throw new Error("Failed to fetch data"); - return result || null; + return result; }, })); @@ -371,7 +386,7 @@ export const ClanDetails = () => { } > - + General data not available}> {(d) => } @@ -386,7 +401,7 @@ export const ClanDetails = () => { } > - + Admin data not available}> {(d) => }