133 lines
5.0 KiB
JavaScript
133 lines
5.0 KiB
JavaScript
#!/usr/bin/env node
|
|
import os from "node:os"
|
|
import path from "node:path"
|
|
import { rm } from "node:fs/promises"
|
|
import { FedeoApi } from "./api.js"
|
|
import { loadConfig } from "./config.js"
|
|
import { log } from "./logger.js"
|
|
import { listPrinters } from "./print/cups.js"
|
|
import { hasSane, listScanners, runScan } from "./scan/sane.js"
|
|
import { AgentHeartbeat } from "./types.js"
|
|
|
|
const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms))
|
|
|
|
const stringifyError = (error: unknown) => {
|
|
if (error instanceof Error) return error.message
|
|
return String(error)
|
|
}
|
|
|
|
const main = async () => {
|
|
const config = loadConfig()
|
|
const api = new FedeoApi(config)
|
|
|
|
log.info("FEDEO Geräte-Agent startet", {
|
|
platform: process.platform,
|
|
workDir: config.workDir,
|
|
pollSeconds: config.pollSeconds,
|
|
})
|
|
|
|
while (true) {
|
|
try {
|
|
const scannerNames = await listScanners().catch((error) => {
|
|
log.warn("Scanner-Erkennung fehlgeschlagen", stringifyError(error))
|
|
return []
|
|
})
|
|
const printerNames = await listPrinters().catch(() => [])
|
|
const scanAvailable = await hasSane()
|
|
|
|
const heartbeatPayload: AgentHeartbeat = {
|
|
capabilities: {
|
|
scan: scanAvailable,
|
|
print: printerNames.length > 0,
|
|
platform: process.platform,
|
|
},
|
|
scannerNames,
|
|
printerNames,
|
|
debugInfo: {
|
|
hostname: os.hostname(),
|
|
release: os.release(),
|
|
arch: os.arch(),
|
|
node: process.version,
|
|
uptimeSeconds: Math.round(os.uptime()),
|
|
},
|
|
}
|
|
const heartbeat = await api.heartbeat(heartbeatPayload)
|
|
|
|
if (heartbeat.pendingScanJobs > 0) {
|
|
log.info(`${heartbeat.pendingScanJobs} Scan-Auftrag/Aufträge warten`)
|
|
}
|
|
|
|
const next = await api.nextScanJob()
|
|
if (!next.job) {
|
|
await sleep(config.pollSeconds * 1000)
|
|
continue
|
|
}
|
|
|
|
log.info("Scan-Auftrag wird ausgeführt", {
|
|
jobId: next.job.id,
|
|
tenantId: next.job.tenantId,
|
|
scannerName: next.job.scannerName || config.scannerName || "default",
|
|
})
|
|
|
|
// Keep online status fresh during long scans without probing an occupied USB scanner.
|
|
let heartbeatRunning = false
|
|
const heartbeatTimer = setInterval(async () => {
|
|
if (heartbeatRunning) return
|
|
heartbeatRunning = true
|
|
try {
|
|
await api.heartbeat(heartbeatPayload)
|
|
} catch (error) {
|
|
log.warn("Heartbeat während Scan fehlgeschlagen", stringifyError(error))
|
|
} finally {
|
|
heartbeatRunning = false
|
|
}
|
|
}, 30_000)
|
|
|
|
try {
|
|
await api.updateScanJobStatus(next.job.id, "running")
|
|
const jobId = next.job.id
|
|
const reportProgress = async (message: string) => {
|
|
await api.updateScanJobStatus(jobId, "running", message).catch((error) => {
|
|
log.warn("Scan-Status konnte nicht übertragen werden", stringifyError(error))
|
|
})
|
|
}
|
|
const scanResult = await runScan(config, next.job, reportProgress)
|
|
await reportProgress(`${scanResult.pageCount} Seiten gescannt. ${scanResult.warnings.join(" ")}`.trim())
|
|
try {
|
|
await api.uploadScan(jobId, scanResult)
|
|
} catch (error) {
|
|
throw new Error(`${stringifyError(error)} Scan-Datei bleibt zur Wiederherstellung unter ${scanResult.path}.`)
|
|
}
|
|
|
|
await rm(path.dirname(scanResult.path), { recursive: true, force: true }).catch((error) => {
|
|
log.warn("Scan gespeichert; temporäre Dateien konnten nicht entfernt werden", stringifyError(error))
|
|
})
|
|
|
|
log.info("Scan-Auftrag abgeschlossen", {
|
|
jobId: next.job.id,
|
|
file: scanResult.filename,
|
|
pages: scanResult.pageCount,
|
|
})
|
|
} catch (error) {
|
|
const message = stringifyError(error)
|
|
log.error("Scan-Auftrag fehlgeschlagen", {
|
|
jobId: next.job.id,
|
|
message,
|
|
})
|
|
|
|
await api.updateScanJobStatus(next.job.id, "failed", message)
|
|
} finally {
|
|
clearInterval(heartbeatTimer)
|
|
}
|
|
} catch (error) {
|
|
log.error("Agent-Schleife fehlgeschlagen", stringifyError(error))
|
|
await sleep(config.pollSeconds * 1000)
|
|
}
|
|
}
|
|
}
|
|
|
|
main().catch((error) => {
|
|
log.error("Agent konnte nicht gestartet werden", stringifyError(error))
|
|
process.exit(1)
|
|
})
|