From c854b0bf30bc22f631c792a6d40d811f0e47d6ad Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Tue, 2 Jun 2026 15:25:00 +0200 Subject: [PATCH] =?UTF-8?q?Scanner=20Verwaltung=20f=C3=BCr=20Ger=C3=A4te-A?= =?UTF-8?q?genten=20erg=C3=A4nzen?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../0052_instance_agent_scan_defaults.sql | 3 + backend/db/schema/instance_agents.ts | 7 + backend/src/routes/instanceAgents.ts | 22 +- frontend/components/MainNav.vue | 5 + frontend/composables/useAdmin.ts | 52 ++- frontend/pages/administration/scanners.vue | 406 ++++++++++++++++++ 6 files changed, 491 insertions(+), 4 deletions(-) create mode 100644 backend/db/migrations/0052_instance_agent_scan_defaults.sql create mode 100644 frontend/pages/administration/scanners.vue diff --git a/backend/db/migrations/0052_instance_agent_scan_defaults.sql b/backend/db/migrations/0052_instance_agent_scan_defaults.sql new file mode 100644 index 0000000..33c7e06 --- /dev/null +++ b/backend/db/migrations/0052_instance_agent_scan_defaults.sql @@ -0,0 +1,3 @@ +ALTER TABLE "instance_agents" ADD COLUMN "preferred_scanner_name" text; +--> statement-breakpoint +ALTER TABLE "instance_agents" ADD COLUMN "scan_defaults" jsonb DEFAULT '{"format":"pdf","resolution":300,"mode":"Color","source":null}'::jsonb NOT NULL; diff --git a/backend/db/schema/instance_agents.ts b/backend/db/schema/instance_agents.ts index adea099..af7844e 100644 --- a/backend/db/schema/instance_agents.ts +++ b/backend/db/schema/instance_agents.ts @@ -29,6 +29,13 @@ export const instanceAgents = pgTable("instance_agents", { capabilities: jsonb("capabilities").notNull().default({ scan: true, print: false }), scannerNames: jsonb("scanner_names").notNull().default([]), printerNames: jsonb("printer_names").notNull().default([]), + preferredScannerName: text("preferred_scanner_name"), + scanDefaults: jsonb("scan_defaults").notNull().default({ + format: "pdf", + resolution: 300, + mode: "Color", + source: null, + }), lastSeenAt: timestamp("last_seen_at", { withTimezone: true }), lastDebugInfo: jsonb("last_debug_info"), diff --git a/backend/src/routes/instanceAgents.ts b/backend/src/routes/instanceAgents.ts index b516f8b..cec3422 100644 --- a/backend/src/routes/instanceAgents.ts +++ b/backend/src/routes/instanceAgents.ts @@ -13,6 +13,8 @@ const updateAgentSchema = z.object({ name: z.string().min(1).optional(), description: z.string().optional().nullable(), active: z.boolean().optional(), + preferredScannerName: z.string().optional().nullable(), + scanDefaults: z.record(z.string(), z.any()).optional(), }) const createScanJobSchema = z.object({ @@ -52,6 +54,8 @@ export default async function instanceAgentRoutes(server: FastifyInstance) { capabilities: instanceAgents.capabilities, scannerNames: instanceAgents.scannerNames, printerNames: instanceAgents.printerNames, + preferredScannerName: instanceAgents.preferredScannerName, + scanDefaults: instanceAgents.scanDefaults, lastSeenAt: instanceAgents.lastSeenAt, lastDebugInfo: instanceAgents.lastDebugInfo, }) @@ -81,6 +85,8 @@ export default async function instanceAgentRoutes(server: FastifyInstance) { description: instanceAgents.description, tokenPrefix: instanceAgents.tokenPrefix, active: instanceAgents.active, + preferredScannerName: instanceAgents.preferredScannerName, + scanDefaults: instanceAgents.scanDefaults, createdAt: instanceAgents.createdAt, }) @@ -106,6 +112,8 @@ export default async function instanceAgentRoutes(server: FastifyInstance) { name: instanceAgents.name, description: instanceAgents.description, active: instanceAgents.active, + preferredScannerName: instanceAgents.preferredScannerName, + scanDefaults: instanceAgents.scanDefaults, updatedAt: instanceAgents.updatedAt, }) @@ -127,7 +135,12 @@ export default async function instanceAgentRoutes(server: FastifyInstance) { } const [agent] = await server.db - .select({ id: instanceAgents.id, active: instanceAgents.active }) + .select({ + id: instanceAgents.id, + active: instanceAgents.active, + preferredScannerName: instanceAgents.preferredScannerName, + scanDefaults: instanceAgents.scanDefaults, + }) .from(instanceAgents) .where(eq(instanceAgents.id, body.agentId)) .limit(1) @@ -142,9 +155,12 @@ export default async function instanceAgentRoutes(server: FastifyInstance) { tenantId: requestedTenantId, agentId: body.agentId, requestedBy: req.user?.user_id, - scannerName: body.scannerName, + scannerName: body.scannerName || agent.preferredScannerName, requestedFilename: body.requestedFilename, - settings: body.settings || {}, + settings: { + ...((agent.scanDefaults || {}) as Record), + ...(body.settings || {}), + }, target: body.target || {}, }) .returning() diff --git a/frontend/components/MainNav.vue b/frontend/components/MainNav.vue index afe9654..dcdb26a 100644 --- a/frontend/components/MainNav.vue +++ b/frontend/components/MainNav.vue @@ -364,6 +364,11 @@ const links = computed(() => { to: "/administration/tenants", icon: "i-heroicons-building-office-2", }, + { + label: "Scanner", + to: "/administration/scanners", + icon: "i-heroicons-printer", + }, { label: "Systemstatus", to: "/administration/system", diff --git a/frontend/composables/useAdmin.ts b/frontend/composables/useAdmin.ts index af6b7e6..355dd85 100644 --- a/frontend/composables/useAdmin.ts +++ b/frontend/composables/useAdmin.ts @@ -83,11 +83,39 @@ export type SystemStatus = { }> } +export type InstanceAgent = { + id: string + createdAt: string + updatedAt: string + name: string + description?: string | null + tokenPrefix: string + active: boolean + capabilities: Record + scannerNames: string[] + printerNames: string[] + preferredScannerName?: string | null + scanDefaults: { + format?: string + resolution?: number + mode?: string + source?: string | null + [key: string]: any + } + lastSeenAt?: string | null + lastDebugInfo?: Record | null +} + +export type CreateInstanceAgentResult = { + agent: InstanceAgent + token: string +} + export const useAdmin = () => { const { $api } = useNuxtApp() const getOverview = async (): Promise => { - const response = await $api("/api/admin/overview") + const response = await $api("/api/admin/overview") as any return { users: response?.users || [], @@ -162,9 +190,31 @@ export const useAdmin = () => { return await $api("/api/admin/system-status") } + const getInstanceAgents = async (): Promise<{ agents: InstanceAgent[] }> => { + const response = await $api("/api/instance-agents") as any + return { agents: response?.agents || [] } + } + + const createInstanceAgent = async (body: Record): Promise => { + return await $api("/api/instance-agents", { + method: "POST", + body, + }) + } + + const updateInstanceAgent = async (id: string, body: Record): Promise<{ agent: InstanceAgent }> => { + return await $api(`/api/instance-agents/${id}`, { + method: "PATCH", + body, + }) + } + return { getOverview, getSystemStatus, + getInstanceAgents, + createInstanceAgent, + updateInstanceAgent, createUser, createUserForProfile, updateUser, diff --git a/frontend/pages/administration/scanners.vue b/frontend/pages/administration/scanners.vue new file mode 100644 index 0000000..3a57dc6 --- /dev/null +++ b/frontend/pages/administration/scanners.vue @@ -0,0 +1,406 @@ + + +