Geräte-Agent für lokale Scan-Aufträge anlegen

This commit is contained in:
2026-06-02 12:59:04 +02:00
parent e9504e21e7
commit a26ff30cd8
16 changed files with 647 additions and 0 deletions

View File

@@ -0,0 +1,85 @@
import { mkdirSync } from "node:fs"
import path from "node:path"
import { AgentConfig, ScanJob, ScanResult } from "../types.js"
import { commandExists, runCommand } from "../commands.js"
const mimeTypes = {
pdf: "application/pdf",
png: "image/png",
tiff: "image/tiff",
}
const stringSetting = (settings: Record<string, unknown> | undefined, key: string) => {
const value = settings?.[key]
return typeof value === "string" && value.trim() ? value.trim() : undefined
}
const numberSetting = (settings: Record<string, unknown> | undefined, key: string) => {
const value = settings?.[key]
if (typeof value === "number" && Number.isFinite(value)) return value
if (typeof value === "string" && value.trim()) {
const parsed = Number(value)
if (Number.isFinite(parsed)) return parsed
}
return undefined
}
export const hasSane = () => commandExists("scanimage")
export const listScanners = async () => {
if (!await hasSane()) return []
const result = await runCommand("scanimage", ["-L"], { timeoutMs: 10_000 })
if (result.code !== 0) return []
return result.stdout
.split(/\r?\n/)
.map((line) => line.trim())
.filter((line) => line.startsWith("device `"))
.map((line) => line.match(/device `([^']+)'/)?.[1])
.filter((device): device is string => Boolean(device))
}
export const runScan = async (config: AgentConfig, job: ScanJob): Promise<ScanResult> => {
if (!await hasSane()) {
throw new Error("scanimage ist nicht installiert oder nicht im PATH")
}
mkdirSync(config.workDir, { recursive: true })
const settings = job.settings || {}
const format = stringSetting(settings, "format") as AgentConfig["scanFormat"] | undefined || config.scanFormat
const resolution = numberSetting(settings, "resolution") || config.scanResolution
const mode = stringSetting(settings, "mode") || config.scanMode
const source = stringSetting(settings, "source") || config.scanSource
const scannerName = job.scannerName || config.scannerName
const filename = job.requestedFilename || `${job.id}.${format}`
const outputPath = path.join(config.workDir, filename)
const args = [
"--format",
format,
"--resolution",
String(resolution),
"--mode",
mode,
"--output-file",
outputPath,
]
if (source) args.push("--source", source)
if (scannerName) args.push("--device-name", scannerName)
const result = await runCommand("scanimage", args, { timeoutMs: 5 * 60 * 1000 })
if (result.code !== 0) {
throw new Error(result.stderr || `scanimage wurde mit Code ${result.code} beendet`)
}
return {
path: outputPath,
filename,
mimeType: mimeTypes[format] || "application/octet-stream",
}
}