making strides in the device and link domain setup

This commit is contained in:
user
2026-03-27 23:58:06 +02:00
parent 8c45efc92e
commit c7c303a934
17 changed files with 1202 additions and 198 deletions

View File

@@ -0,0 +1,88 @@
import { getDeviceController } from "@pkg/logic/domains/device/controller";
import { createDeviceSchema, updateDeviceSchema } from "@pkg/logic/domains/device/data";
import { getFlowExecCtxForRemoteFuncs, unauthorized } from "$lib/core/server.utils";
import { command, getRequestEvent, query } from "$app/server";
import * as v from "valibot";
const dc = getDeviceController();
export const listDevicesSQ = query(async () => {
const event = getRequestEvent();
const fctx = await getFlowExecCtxForRemoteFuncs(event.locals);
if (!fctx.userId) return unauthorized(fctx);
const res = await dc.list(fctx);
return res.isOk()
? { data: res.value, error: null }
: { data: null, error: res.error };
});
export const getDeviceByIdSQ = query(
v.object({ id: v.number() }),
async (input) => {
const event = getRequestEvent();
const fctx = await getFlowExecCtxForRemoteFuncs(event.locals);
if (!fctx.userId) return unauthorized(fctx);
const res = await dc.getById(fctx, input.id);
return res.isOk()
? { data: res.value, error: null }
: { data: null, error: res.error };
},
);
export const createDeviceSC = command(createDeviceSchema, async (payload) => {
const event = getRequestEvent();
const fctx = await getFlowExecCtxForRemoteFuncs(event.locals);
if (!fctx.userId) return unauthorized(fctx);
const res = await dc.create(fctx, payload);
return res.isOk()
? { data: res.value, error: null }
: { data: null, error: res.error };
});
export const updateDeviceSC = command(
v.object({ id: v.number(), data: updateDeviceSchema }),
async (payload) => {
const event = getRequestEvent();
const fctx = await getFlowExecCtxForRemoteFuncs(event.locals);
if (!fctx.userId) return unauthorized(fctx);
const res = await dc.update(fctx, payload.id, payload.data);
return res.isOk()
? { data: res.value, error: null }
: { data: null, error: res.error };
},
);
export const deleteDeviceSC = command(
v.object({ id: v.number() }),
async (payload) => {
const event = getRequestEvent();
const fctx = await getFlowExecCtxForRemoteFuncs(event.locals);
if (!fctx.userId) return unauthorized(fctx);
const res = await dc.delete(fctx, payload.id);
return res.isOk()
? { data: res.value, error: null }
: { data: null, error: res.error };
},
);
export const setDeviceStatusSC = command(
v.object({
id: v.number(),
status: v.picklist(["online", "offline", "busy", "error"]),
}),
async (payload) => {
const event = getRequestEvent();
const fctx = await getFlowExecCtxForRemoteFuncs(event.locals);
if (!fctx.userId) return unauthorized(fctx);
const res = await dc.setStatus(fctx, payload.id, payload.status as any);
return res.isOk()
? { data: res.value, error: null }
: { data: null, error: res.error };
},
);

View File

@@ -0,0 +1,152 @@
import {
listDevicesSQ,
createDeviceSC,
deleteDeviceSC,
setDeviceStatusSC,
} from "./device.remote";
import { toast } from "svelte-sonner";
type Device = {
id: number;
title: string;
version: string;
status: string;
isActive: boolean;
containerId: string | null;
host: string;
wsPort: string | null;
createdAt: Date;
updatedAt: Date;
};
type CreateDeviceInput = {
title: string;
version: string;
host: string;
containerId?: string;
wsPort?: string;
isActive?: boolean;
};
class DeviceViewModel {
devices = $state<Device[]>([]);
loading = $state(false);
creating = $state(false);
deletingId = $state<number | null>(null);
showCreateDialog = $state(false);
async fetchDevices() {
this.loading = true;
try {
const result = await listDevicesSQ();
if (result?.error || !result?.data) {
toast.error(
result?.error?.message || "Failed to fetch devices",
{
description:
result?.error?.description || "Please try again",
},
);
return;
}
this.devices = result.data as Device[];
} catch (error) {
toast.error("Failed to fetch devices", {
description:
error instanceof Error
? error.message
: "Please try again",
});
} finally {
this.loading = false;
}
}
async createDevice(data: CreateDeviceInput): Promise<boolean> {
this.creating = true;
try {
const result = await createDeviceSC(data);
if (result?.error) {
toast.error(
result.error.message || "Failed to create device",
{
description:
result.error.description || "Please try again",
},
);
return false;
}
toast.success("Device created");
this.showCreateDialog = false;
await this.fetchDevices();
return true;
} catch (error) {
toast.error("Failed to create device", {
description:
error instanceof Error
? error.message
: "Please try again",
});
return false;
} finally {
this.creating = false;
}
}
async deleteDevice(id: number) {
this.deletingId = id;
try {
const result = await deleteDeviceSC({ id });
if (result?.error) {
toast.error(
result.error.message || "Failed to delete device",
{
description:
result.error.description || "Please try again",
},
);
return;
}
toast.success("Device deleted");
await this.fetchDevices();
} catch (error) {
toast.error("Failed to delete device", {
description:
error instanceof Error
? error.message
: "Please try again",
});
} finally {
this.deletingId = null;
}
}
async setStatus(id: number, status: string) {
try {
const result = await setDeviceStatusSC({
id,
status: status as any,
});
if (result?.error) {
toast.error(
result.error.message || "Failed to update status",
{
description:
result.error.description || "Please try again",
},
);
return;
}
await this.fetchDevices();
} catch (error) {
toast.error("Failed to update status", {
description:
error instanceof Error
? error.message
: "Please try again",
});
}
}
}
export const deviceVM = new DeviceViewModel();

View File

@@ -0,0 +1,108 @@
import { getLinkController } from "@pkg/logic/domains/link/controller";
import { updateLinkSchema } from "@pkg/logic/domains/link/data";
import { getFlowExecCtxForRemoteFuncs, unauthorized } from "$lib/core/server.utils";
import { command, getRequestEvent, query } from "$app/server";
import * as v from "valibot";
const lc = getLinkController();
export const listLinksSQ = query(async () => {
const event = getRequestEvent();
const fctx = await getFlowExecCtxForRemoteFuncs(event.locals);
if (!fctx.userId) return unauthorized(fctx);
const res = await lc.list(fctx);
return res.isOk()
? { data: res.value, error: null }
: { data: null, error: res.error };
});
export const getLinkByIdSQ = query(
v.object({ id: v.number() }),
async (input) => {
const event = getRequestEvent();
const fctx = await getFlowExecCtxForRemoteFuncs(event.locals);
if (!fctx.userId) return unauthorized(fctx);
const res = await lc.getById(fctx, input.id);
return res.isOk()
? { data: res.value, error: null }
: { data: null, error: res.error };
},
);
export const createLinkSC = command(
v.object({
linkedDeviceId: v.optional(v.nullable(v.number())),
expiresAt: v.optional(v.nullable(v.date())),
}),
async (payload) => {
const event = getRequestEvent();
const fctx = await getFlowExecCtxForRemoteFuncs(event.locals);
if (!fctx.userId) return unauthorized(fctx);
const res = await lc.create(fctx, payload);
return res.isOk()
? { data: res.value, error: null }
: { data: null, error: res.error };
},
);
export const updateLinkSC = command(
v.object({ id: v.number(), data: updateLinkSchema }),
async (payload) => {
const event = getRequestEvent();
const fctx = await getFlowExecCtxForRemoteFuncs(event.locals);
if (!fctx.userId) return unauthorized(fctx);
const res = await lc.update(fctx, payload.id, payload.data);
return res.isOk()
? { data: res.value, error: null }
: { data: null, error: res.error };
},
);
export const revokeLinkSC = command(
v.object({ id: v.number() }),
async (payload) => {
const event = getRequestEvent();
const fctx = await getFlowExecCtxForRemoteFuncs(event.locals);
if (!fctx.userId) return unauthorized(fctx);
const res = await lc.revoke(fctx, payload.id);
return res.isOk()
? { data: res.value, error: null }
: { data: null, error: res.error };
},
);
export const deleteLinkSC = command(
v.object({ id: v.number() }),
async (payload) => {
const event = getRequestEvent();
const fctx = await getFlowExecCtxForRemoteFuncs(event.locals);
if (!fctx.userId) return unauthorized(fctx);
const res = await lc.delete(fctx, payload.id);
return res.isOk()
? { data: res.value, error: null }
: { data: null, error: res.error };
},
);
export const assignDeviceSC = command(
v.object({
id: v.number(),
deviceId: v.nullable(v.number()),
}),
async (payload) => {
const event = getRequestEvent();
const fctx = await getFlowExecCtxForRemoteFuncs(event.locals);
if (!fctx.userId) return unauthorized(fctx);
const res = await lc.assignDevice(fctx, payload.id, payload.deviceId);
return res.isOk()
? { data: res.value, error: null }
: { data: null, error: res.error };
},
);