A kind selector (Bambu, Klipper, PrusaLink, OctoPrint) decides which fields appear: serial and access code for Bambu, an API key for PrusaLink and OctoPrint, neither for Klipper. Discovered printers of any of these kinds now fill the form in.
129 lines
3.9 KiB
Vue
129 lines
3.9 KiB
Vue
<script setup lang="ts">
|
|
import {
|
|
AddPrinter,
|
|
GatewaySetup,
|
|
ManagedPrinters,
|
|
OnboardingForm,
|
|
type AppFarm,
|
|
type AppFarmDiscovery,
|
|
type AppOrg,
|
|
type AppPrinterState,
|
|
type NewPrinter,
|
|
} from "@continuum/ui";
|
|
|
|
const apiFetch = useApiFetch();
|
|
const { wsBase } = useRuntimeConfig().public;
|
|
|
|
const { data: orgs, refresh: refreshOrgs } = await useAsyncData("setup-orgs", () => apiFetch<AppOrg[]>("/orgs"));
|
|
const { data: farms, refresh: refreshFarms } = await useAsyncData("setup-farms", () => apiFetch<AppFarm[]>("/farms"));
|
|
const { data: printers, refresh: refreshPrinters } = await useAsyncData("setup-printers", () =>
|
|
apiFetch<AppPrinterState[]>("/printers"),
|
|
);
|
|
|
|
// The gateway's presence and what it found, per farm - the same endpoint the
|
|
// fleet page reads. Polled while this page is open so "gateway connected"
|
|
// turns true as soon as someone finishes configuring it.
|
|
const status = ref<Record<string, AppFarmDiscovery>>({});
|
|
async function refreshStatus() {
|
|
for (const farm of farms.value ?? []) {
|
|
try {
|
|
status.value[farm.id] = await apiFetch<AppFarmDiscovery>(`/farms/${farm.id}/discovered-printers`);
|
|
} catch {
|
|
// Keep the last answer; a failed poll is not "offline".
|
|
}
|
|
}
|
|
}
|
|
let poll: ReturnType<typeof setInterval> | undefined;
|
|
onMounted(() => {
|
|
void refreshStatus();
|
|
poll = setInterval(() => void refreshStatus(), 5_000);
|
|
});
|
|
onBeforeUnmount(() => clearInterval(poll));
|
|
|
|
/**
|
|
* Creates the first farm. Reuses the caller's organisation if they already
|
|
* have one (a person who was invited has an org and no farm), and creates one
|
|
* named after the farm otherwise.
|
|
*/
|
|
async function createFarm(farmName: string) {
|
|
try {
|
|
let orgId = orgs.value?.[0]?.id;
|
|
if (!orgId) {
|
|
const org = await apiFetch<AppOrg>("/orgs", { method: "POST", body: { name: farmName } });
|
|
orgId = org.id;
|
|
}
|
|
await apiFetch("/farms", { method: "POST", body: { name: farmName, orgId } });
|
|
} catch (err) {
|
|
throw new Error(apiErrorMessage(err, "the farm could not be created"));
|
|
}
|
|
await Promise.all([refreshOrgs(), refreshFarms()]);
|
|
await refreshStatus();
|
|
}
|
|
|
|
async function mintToken(farmId: string): Promise<string> {
|
|
try {
|
|
const result = await apiFetch<{ edgeProxyToken: string }>(`/farms/${farmId}/edge-proxy-token`, {
|
|
method: "POST",
|
|
});
|
|
return result.edgeProxyToken;
|
|
} catch (err) {
|
|
throw new Error(apiErrorMessage(err, "the token could not be created"));
|
|
}
|
|
}
|
|
|
|
async function addPrinter(farmId: string, printer: NewPrinter) {
|
|
try {
|
|
await apiFetch(`/farms/${farmId}/printers`, {
|
|
method: "POST",
|
|
body: printer,
|
|
});
|
|
} catch (err) {
|
|
throw new Error(apiErrorMessage(err, "the printer could not be added"));
|
|
}
|
|
await refreshPrinters();
|
|
}
|
|
|
|
async function removePrinter(printerId: string) {
|
|
try {
|
|
await apiFetch(`/printers/${printerId}`, { method: "DELETE" });
|
|
} catch (err) {
|
|
throw new Error(apiErrorMessage(err, "the printer could not be removed"));
|
|
}
|
|
await refreshPrinters();
|
|
}
|
|
|
|
const printersOf = (farmId: string) => (printers.value ?? []).filter((p) => p.farmId === farmId);
|
|
</script>
|
|
|
|
<template>
|
|
<div class="setup">
|
|
<h1>Setup</h1>
|
|
|
|
<OnboardingForm v-if="!farms?.length" :create="createFarm" />
|
|
|
|
<section v-for="farm in farms ?? []" :key="farm.id" class="setup__farm">
|
|
<h2>{{ farm.name }}</h2>
|
|
<GatewaySetup
|
|
:farm="farm"
|
|
:gateway-online="status[farm.id]?.gatewayOnline ?? null"
|
|
:uplink-url="`${wsBase}/ws/edge/v1`"
|
|
:mint="() => mintToken(farm.id)"
|
|
/>
|
|
<AddPrinter
|
|
:gateway-online="status[farm.id]?.gatewayOnline ?? null"
|
|
:candidates="status[farm.id]?.discovery?.printers ?? []"
|
|
:add="(printer: NewPrinter) => addPrinter(farm.id, printer)"
|
|
/>
|
|
<ManagedPrinters :printers="printersOf(farm.id)" :remove="removePrinter" />
|
|
</section>
|
|
</div>
|
|
</template>
|
|
|
|
<style scoped>
|
|
.setup__farm {
|
|
border-top: 1px solid #1f232c;
|
|
margin-top: 1.5rem;
|
|
padding-top: 1rem;
|
|
}
|
|
</style>
|