Update configuration and introduce object ID handling utilities
Some checks failed
farmcontrol/farmcontrol-server/pipeline/head There was a failure building this commit

- Changed the API URL in both `config.json` and `src/config.js` for the development environment.
- Added a new `objectid.js` file containing utility functions for handling object IDs, including `stringifyIds` and `refId`.
- Updated the `PrinterDatabase`, `PrinterClient`, and `PrinterManager` classes to utilize the new object ID handling functions, ensuring consistent ID management across the application.
- Enhanced the `SocketClient` to incorporate a custom parser for handling string IDs in socket communications.
This commit is contained in:
Tom Butcher 2026-11-07 18:53:01 +00:00
parent 158eb39f06
commit 2522e2f9ce
8 changed files with 195 additions and 60 deletions

View File

@ -2,7 +2,7 @@
"development": { "development": {
"logLevel": "debug", "logLevel": "debug",
"url": "https://dev-wss.tombutcher.work", "url": "https://dev-wss.tombutcher.work",
"apiUrl": "https://dev.tombutcher.work/api", "apiUrl": "https://dev.tombutcher.work/fcapi",
"host": { "host": {
"id": "691a1db49ce913faf0e51284", "id": "691a1db49ce913faf0e51284",
"authCode": "6wdCQPKMr_zuFaJe3-AY99uRuAEZRgU2lLNTuAFPW5hPvzzTrSra3rh3woZLj-eE" "authCode": "6wdCQPKMr_zuFaJe3-AY99uRuAEZRgU2lLNTuAFPW5hPvzzTrSra3rh3woZLj-eE"

View File

@ -136,7 +136,7 @@ const DEFAULT_CONFIG = {
development: { development: {
logLevel: "debug", logLevel: "debug",
url: "https://dev-wss.tombutcher.work", url: "https://dev-wss.tombutcher.work",
apiUrl: "https://dev.tombutcher.work/api", apiUrl: "https://dev.tombutcher.work/fcapi",
host: { host: {
id: "", id: "",
authCode: "", authCode: "",

98
src/objectid.js Normal file
View File

@ -0,0 +1,98 @@
function bytesOf(value) {
if (value == null) return null;
if (typeof Buffer !== 'undefined' && Buffer.isBuffer(value)) {
return value.length === 12 ? value : null;
}
if (value instanceof Uint8Array) {
return value.byteLength === 12 ? Buffer.from(value) : null;
}
if (
value &&
value.type === 'Buffer' &&
Array.isArray(value.data) &&
value.data.length === 12
) {
return Buffer.from(value.data);
}
if (typeof value !== 'object' || Array.isArray(value) || value instanceof Date) {
return null;
}
const keys = Object.keys(value);
if (keys.length !== 12) return null;
const bytes = Buffer.alloc(12);
for (let index = 0; index < 12; index += 1) {
if (!Object.prototype.hasOwnProperty.call(value, index)) return null;
const byte = Number(value[index]);
if (!Number.isInteger(byte) || byte < 0 || byte > 255) return null;
bytes[index] = byte;
}
return bytes;
}
const PURE_REF_KEYS = new Set(['_id', 'id', 'buffer', '$oid']);
function isPureRef(value) {
if (value == null || typeof value !== 'object') return false;
if (typeof value.toHexString === 'function') return true;
if (Array.isArray(value) || value instanceof Date) return false;
const keys = Object.keys(value);
return keys.length > 0 && keys.every(key => PURE_REF_KEYS.has(key)) && refId(value) != null;
}
function isBinaryPayload(value) {
if (value == null || typeof value !== 'object') return false;
if (typeof Buffer !== 'undefined' && Buffer.isBuffer(value)) return true;
if (value instanceof Uint8Array || value instanceof ArrayBuffer) return true;
const name = value.constructor?.name;
return name === 'Blob' || name === 'File';
}
// Object ids are always hex strings on the wire. File buffers are left alone.
export function stringifyIds(value) {
if (value == null) return value;
if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') {
return value;
}
if (typeof value.toHexString === 'function') return value.toHexString();
if (value instanceof Date || typeof value === 'function') return value;
if (isBinaryPayload(value)) return value;
if (Array.isArray(value)) return value.map(item => stringifyIds(item));
if (typeof value === 'object') {
if (isPureRef(value)) {
const id = refId(value);
if (typeof id === 'string') return id;
}
const copy = {};
for (const [key, child] of Object.entries(value)) {
copy[key] = stringifyIds(child);
}
return copy;
}
return value;
}
export function refId(value) {
if (value == null || value === '') return null;
if (typeof value === 'string') return value;
if (typeof value === 'object' && typeof value.toHexString === 'function') {
return value.toHexString();
}
if (typeof value !== 'object' || Array.isArray(value) || value instanceof Date) {
return null;
}
if (typeof value.$oid === 'string') return refId(value.$oid);
const keys = Object.keys(value);
if (
keys.length > 0 &&
keys.every(key => key === 'buffer' || key === 'id' || key === 'type' || key === 'data')
) {
const bytes = bytesOf(value.buffer) || bytesOf(value.id) || bytesOf(value);
if (bytes) return bytes.toString('hex');
}
if (value._id != null) return refId(value._id);
return null;
}

View File

@ -5,6 +5,7 @@ import { loadConfig } from "../config.js";
import { sendIPC } from "../desktop/notify.js"; import { sendIPC } from "../desktop/notify.js";
import chalk from "chalk"; import chalk from "chalk";
import { formatState } from "../commandlineutils.js"; import { formatState } from "../commandlineutils.js";
import { refId } from "../objectid.js";
const config = loadConfig(); const config = loadConfig();
const logger = log4js.getLogger("Printer Database"); const logger = log4js.getLogger("Printer Database");
logger.level = config.logLevel; logger.level = config.logLevel;
@ -13,7 +14,7 @@ export class PrinterDatabase {
constructor(socketClient, printer) { constructor(socketClient, printer) {
this.socketClient = socketClient; this.socketClient = socketClient;
this.printer = printer; this.printer = printer;
this.id = this.printer._id; this.id = refId(this.printer._id);
// Initialize cache with 30 second TTL // Initialize cache with 30 second TTL
this.printerCache = new NodeCache({ stdTTL: 30 }); this.printerCache = new NodeCache({ stdTTL: 30 });
} }
@ -26,7 +27,7 @@ export class PrinterDatabase {
"queue", "queue",
]; ];
const populateKey = populate.sort().join(","); const populateKey = populate.sort().join(",");
const cacheKey = `printer_${this.printer._id}_${populateKey}`; const cacheKey = `printer_${this.id}_${populateKey}`;
// Check if printer is in cache // Check if printer is in cache
const cachedPrinter = this.printerCache.get(cacheKey); const cachedPrinter = this.printerCache.get(cacheKey);
@ -38,7 +39,7 @@ export class PrinterDatabase {
// Fetch from socketClient if not in cache // Fetch from socketClient if not in cache
const object = await this.socketClient.getObject({ const object = await this.socketClient.getObject({
_id: this.printer._id, _id: this.id,
objectType: "printer", objectType: "printer",
populate: populate, populate: populate,
}); });
@ -60,21 +61,25 @@ export class PrinterDatabase {
"queue", "queue",
]; ];
const object = await this.socketClient.editObject({ const object = await this.socketClient.editObject({
_id: this.printer._id, _id: this.id,
objectType: "printer", objectType: "printer",
updateData: updateData, updateData: updateData,
populate: populate, populate: populate,
}); });
// Update cache with the edited printer (overwrite existing cache) if (!object || object.error) {
throw new Error(object?.error || "Failed to edit printer");
}
// Drop the cache so the next read comes back with populated jobs.
// The patch itself may only contain ids.
const populateKey = populate.sort().join(","); const populateKey = populate.sort().join(",");
const cacheKey = `printer_${this.printer._id}_${populateKey}`; const cacheKey = `printer_${this.id}_${populateKey}`;
this.printerCache.del(cacheKey); // Delete existing cache entry this.printerCache.del(cacheKey);
this.printerCache.set(cacheKey, { ...object, ...updateData }); // Set new cache entry this.printer = { ...this.printer, ...object };
this.printer = { ...object, ...updateData };
logger.debug(`Updated printer cache for ${this.id}`); logger.debug(`Updated printer cache for ${this.id}`);
return object; return this.printer;
} }
async setPrinterConnectedAt() { async setPrinterConnectedAt() {
@ -162,6 +167,7 @@ export class PrinterDatabase {
async updateSubJobState(subJobId, state) { async updateSubJobState(subJobId, state) {
try { try {
subJobId = refId(subJobId);
const updatedSubJob = await this.socketClient.editObject({ const updatedSubJob = await this.socketClient.editObject({
_id: subJobId, _id: subJobId,
objectType: "subJob", objectType: "subJob",
@ -181,14 +187,20 @@ export class PrinterDatabase {
async updateJobState(jobId) { async updateJobState(jobId) {
try { try {
jobId = refId(jobId);
logger.debug(`Updating job state for ${jobId}`); logger.debug(`Updating job state for ${jobId}`);
const subJobs = await this.socketClient.listObjects({ const subJobs = await this.socketClient.listObjects({
objectType: "subJob", objectType: "subJob",
filter: { job: { _id: jobId } }, filter: { job: jobId },
cached: false, cached: false,
}); });
if (!Array.isArray(subJobs) || subJobs.length === 0) {
logger.warn(`No subjobs found for job ${jobId}`);
return null;
}
const subJobStates = subJobs.map((subJob) => subJob.state); const subJobStates = subJobs.map((subJob) => subJob.state);
const stateCounts = { const stateCounts = {
printing: 0, printing: 0,
@ -201,6 +213,7 @@ export class PrinterDatabase {
}; };
subJobStates.forEach((state) => { subJobStates.forEach((state) => {
if (!state?.type || stateCounts[state.type] == null) return;
stateCounts[state.type]++; stateCounts[state.type]++;
}); });
@ -260,7 +273,7 @@ export class PrinterDatabase {
moonrakerJobId, moonrakerJobId,
}); });
const updatedSubJob = await this.socketClient.editObject({ const updatedSubJob = await this.socketClient.editObject({
_id: subJobId, _id: refId(subJobId),
objectType: "subJob", objectType: "subJob",
updateData: { moonrakerJobId: moonrakerJobId }, updateData: { moonrakerJobId: moonrakerJobId },
}); });
@ -321,8 +334,8 @@ export class PrinterDatabase {
jobId: job?._id || null, jobId: job?._id || null,
}); });
const updatedPrinter = await this.editPrinter({ const updatedPrinter = await this.editPrinter({
currentSubJob: subJob, currentSubJob: subJob ? refId(subJob) : null,
currentJob: job, currentJob: job ? refId(job) : null,
}); });
return updatedPrinter; return updatedPrinter;
} catch (error) { } catch (error) {
@ -530,13 +543,13 @@ export class PrinterDatabase {
subJob = null, subJob = null,
job = null, job = null,
) { ) {
const subJobId = subJob._id || subJob.id; const subJobId = refId(subJob);
const jobId = job._id || job.id; const jobId = refId(job);
try { try {
const filamentStockStockEvents = await this.socketClient.listObjects({ const filamentStockStockEvents = await this.socketClient.listObjects({
objectType: "stockEvent", objectType: "stockEvent",
filter: { filter: {
"parent._id": filamentStock._id, "parent._id": refId(filamentStock),
parentType: "filamentStock", parentType: "filamentStock",
}, },
populate: ["owner"], populate: ["owner"],
@ -544,7 +557,7 @@ export class PrinterDatabase {
const stockEvents = filamentStockStockEvents.filter( const stockEvents = filamentStockStockEvents.filter(
(event) => (event) =>
event.owner._id.toString() === subJobId.toString() && refId(event.owner) === subJobId &&
event.ownerType === "subJob", event.ownerType === "subJob",
); );
@ -607,7 +620,7 @@ export class PrinterDatabase {
// 1. Update SubJob with Moonraker ID and State // 1. Update SubJob with Moonraker ID and State
await this.socketClient.editObject({ await this.socketClient.editObject({
_id: subJob._id, _id: refId(subJob),
objectType: "subJob", objectType: "subJob",
updateData: { updateData: {
moonrakerJobId: moonrakerJobId, moonrakerJobId: moonrakerJobId,
@ -616,10 +629,10 @@ export class PrinterDatabase {
}); });
// 2. Add to Printer's Queue // 2. Add to Printer's Queue
const newQueue = [...this.printer.queue, subJob]; const newQueue = [...(this.printer.queue || []), subJob];
const updatedPrinter = await this.editPrinter({ const updatedPrinter = await this.editPrinter({
queue: newQueue, queue: newQueue.map((queued) => refId(queued)),
}); });
this.printer.queue = newQueue; this.printer.queue = newQueue;
@ -638,8 +651,9 @@ export class PrinterDatabase {
subJobs.map((subJob) => subJob._id).join(", "), subJobs.map((subJob) => subJob._id).join(", "),
); );
const updatedPrinter = await this.editPrinter({ const updatedPrinter = await this.editPrinter({
queue: subJobs, queue: subJobs.map((subJob) => refId(subJob)),
}); });
this.printer.queue = subJobs;
return updatedPrinter; return updatedPrinter;
} catch (error) { } catch (error) {
logger.error( logger.error(
@ -669,7 +683,7 @@ export class PrinterDatabase {
logger.debug(`Getting job by ID ${jobId} for printer ${this.id}`); logger.debug(`Getting job by ID ${jobId} for printer ${this.id}`);
const job = await this.socketClient.getObject({ const job = await this.socketClient.getObject({
objectType: "job", objectType: "job",
_id: jobId, _id: refId(jobId),
}); });
return job; return job;
} catch (error) { } catch (error) {
@ -697,7 +711,7 @@ export class PrinterDatabase {
); );
const gcodeFile = await this.socketClient.getObject({ const gcodeFile = await this.socketClient.getObject({
objectType: "gcodeFile", objectType: "gcodeFile",
_id: subJob.gcodeFile._id, _id: refId(subJob.gcodeFile),
populate: ["parts.part", "parts.partSku"], populate: ["parts.part", "parts.partSku"],
}); });
const partItems = gcodeFile.parts; const partItems = gcodeFile.parts;

View File

@ -9,6 +9,7 @@ import { startWaiting, stopWaiting } from "../spinner.js";
import axios from "axios"; import axios from "axios";
import FormData from "form-data"; import FormData from "form-data";
import _ from "lodash"; import _ from "lodash";
import { refId } from "../objectid.js";
// Load configuration // Load configuration
const config = loadConfig(); const config = loadConfig();
@ -17,7 +18,7 @@ logger.level = config.logLevel;
export class PrinterClient { export class PrinterClient {
constructor(printer, printerManager) { constructor(printer, printerManager) {
this.id = printer._id; this.id = refId(printer._id);
this.printer = printer; this.printer = printer;
this.name = printer.name; this.name = printer.name;
this.printerManager = printerManager; this.printerManager = printerManager;
@ -360,7 +361,7 @@ export class PrinterClient {
async getCurrentFilament() { async getCurrentFilament() {
logger.info(`Getting current filament for printer: ${this.printer.name}`); logger.info(`Getting current filament for printer: ${this.printer.name}`);
if (!this.currentFilamentStock || !this.currentFilamentStock?._id) { if (!refId(this.currentFilamentStock)) {
logger.warn( logger.warn(
`No current filament stock found for printer: ${this.printer.name}`, `No current filament stock found for printer: ${this.printer.name}`,
); );
@ -368,7 +369,7 @@ export class PrinterClient {
} }
const result = await this.socketClient.getObject({ const result = await this.socketClient.getObject({
objectType: "filamentStock", objectType: "filamentStock",
_id: this.currentFilamentStock._id, _id: refId(this.currentFilamentStock),
populate: ["filamentSku"], populate: ["filamentSku"],
}); });
this.currentFilamentSku = result.filamentSku; this.currentFilamentSku = result.filamentSku;
@ -376,13 +377,13 @@ export class PrinterClient {
} }
async loadCurrentFilament() { async loadCurrentFilament() {
if (!this.currentFilamentStock?.filament?._id) { if (!refId(this.currentFilamentStock?.filament)) {
this.currentFilament = null; this.currentFilament = null;
return null; return null;
} }
this.currentFilament = await this.socketClient.getObject({ this.currentFilament = await this.socketClient.getObject({
objectType: "filament", objectType: "filament",
_id: this.currentFilamentStock.filament._id, _id: refId(this.currentFilamentStock.filament),
}); });
return this.currentFilament; return this.currentFilament;
} }
@ -830,13 +831,13 @@ export class PrinterClient {
if (stateChanged == true) { if (stateChanged == true) {
if (printerFinished == true && isCurrentJobSubJob == true) { if (printerFinished == true && isCurrentJobSubJob == true) {
logger.info( logger.info(
`Subjob ${this.currentSubJob._id} completed, posting part stock items for printer ${this.id}`, `Subjob ${refId(this.currentSubJob)} completed, posting part stock items for printer ${this.id}`,
); );
await this.database.setSubJobFinishedAt( await this.database.setSubJobFinishedAt(
this.currentSubJob._id, refId(this.currentSubJob),
new Date(), new Date(),
); );
await this.database.postSubJobPartStockItems(this.currentSubJob._id); await this.database.postSubJobPartStockItems(refId(this.currentSubJob));
} }
// Update printer state first // Update printer state first
await this.updatePrinterState(); await this.updatePrinterState();
@ -848,13 +849,13 @@ export class PrinterClient {
if (this.currentSubJob.startedAt == null) { if (this.currentSubJob.startedAt == null) {
this.currentSubJob.startedAt = new Date(); this.currentSubJob.startedAt = new Date();
await this.database.setSubJobStartedAt( await this.database.setSubJobStartedAt(
this.currentSubJob._id, refId(this.currentSubJob),
new Date(), new Date(),
); );
} }
if (this.currentJob.startedAt == null) { if (this.currentJob.startedAt == null) {
this.currentJob.startedAt = new Date(); this.currentJob.startedAt = new Date();
await this.database.setJobStartedAt(this.currentJob._id, new Date()); await this.database.setJobStartedAt(refId(this.currentJob), new Date());
} }
this.currentFilamentUsed = 0; this.currentFilamentUsed = 0;
} }
@ -866,10 +867,10 @@ export class PrinterClient {
progress: this.state.progress, progress: this.state.progress,
}; };
await this.database.updateSubJobState( await this.database.updateSubJobState(
this.currentSubJob._id, refId(this.currentSubJob),
subJobState, subJobState,
); );
await this.database.updateJobState(this.currentJob._id); await this.database.updateJobState(refId(this.currentJob));
} }
if (progressChanged) { if (progressChanged) {
@ -997,28 +998,28 @@ export class PrinterClient {
if (syncedTypes.includes(stateType)) { if (syncedTypes.includes(stateType)) {
if (isCurrentJobSubJob == true) { if (isCurrentJobSubJob == true) {
await this.database.updateSubJobState(this.currentSubJob._id, { await this.database.updateSubJobState(refId(this.currentSubJob), {
type: stateType, type: stateType,
}); });
this.currentSubJob.state.type = stateType; this.currentSubJob.state.type = stateType;
this.currentJob.state = await this.database.updateJobState( this.currentJob.state = await this.database.updateJobState(
this.currentJob._id, refId(this.currentJob),
); );
} }
} }
if (failedTypes.includes(stateType)) { if (failedTypes.includes(stateType)) {
for (const subJob of unqueuedSubJobs) { for (const subJob of unqueuedSubJobs) {
await this.database.updateSubJobState(subJob._id, { await this.database.updateSubJobState(refId(subJob), {
type: "failed", type: "failed",
}); });
await this.database.updateJobState(subJob.job._id); await this.database.updateJobState(refId(subJob.job));
} }
if (isCurrentJobSubJob == true) { if (isCurrentJobSubJob == true) {
await this.database.updateSubJobState(this.currentSubJob._id, { await this.database.updateSubJobState(refId(this.currentSubJob), {
type: "failed", type: "failed",
}); });
await this.database.updateJobState(this.currentJob._id); await this.database.updateJobState(refId(this.currentJob));
this.currentSubJob = null; this.currentSubJob = null;
this.currentJob = null; this.currentJob = null;
await this.database.setCurrentJobAndSubJob(null, null); await this.database.setCurrentJobAndSubJob(null, null);
@ -1027,14 +1028,14 @@ export class PrinterClient {
if (completedTypes.includes(stateType)) { if (completedTypes.includes(stateType)) {
const otherSubJobs = unqueuedSubJobs const otherSubJobs = unqueuedSubJobs
.filter((subJob) => subJob._id != this.currentSubJob?._id) .filter((subJob) => refId(subJob) != refId(this.currentSubJob))
.filter((subJob) => subJob.state.type == "queued"); .filter((subJob) => subJob.state.type == "queued");
if (otherSubJobs.length > 0) { if (otherSubJobs.length > 0) {
for (const subJob of otherSubJobs) { for (const subJob of otherSubJobs) {
await this.database.updateSubJobState(subJob._id, { await this.database.updateSubJobState(refId(subJob), {
type: "failed", type: "failed",
}); });
await this.database.updateJobState(subJob.job._id); await this.database.updateJobState(refId(subJob.job));
} }
} }
@ -1180,11 +1181,11 @@ export class PrinterClient {
Math.max(remainingTime, 0), Math.max(remainingTime, 0),
); );
await this.database.updateSubJobState(subJob._id, { await this.database.updateSubJobState(refId(subJob), {
type: "deploying", type: "deploying",
progress: 0, progress: 0,
}); });
await this.database.updateJobState(subJob.job._id); await this.database.updateJobState(refId(subJob.job));
} }
async processDeploySubJobQueue() { async processDeploySubJobQueue() {
@ -1211,10 +1212,10 @@ export class PrinterClient {
`Error deploying sub job ${subJob._id} to printer ${this.id}:`, `Error deploying sub job ${subJob._id} to printer ${this.id}:`,
error, error,
); );
await this.database.updateSubJobState(subJob._id, { await this.database.updateSubJobState(refId(subJob), {
type: "failed", type: "failed",
}); });
await this.database.updateJobState(subJob.job._id); await this.database.updateJobState(refId(subJob.job));
} }
} }
} }
@ -1226,29 +1227,29 @@ export class PrinterClient {
"to printer:", "to printer:",
this.id, this.id,
"with gcode file:", "with gcode file:",
`${subJob.gcodeFile._id}`, `${refId(subJob.gcodeFile)}`,
); );
const gcodeFile = await this.socketClient.getObject({ const gcodeFile = await this.socketClient.getObject({
objectType: "gcodeFile", objectType: "gcodeFile",
_id: subJob.gcodeFile._id, _id: refId(subJob.gcodeFile),
}); });
if (!gcodeFile) { if (!gcodeFile) {
throw new Error("G-code file not found"); throw new Error("G-code file not found");
} }
const fileId = (gcodeFile.file?._id || gcodeFile.file).toString(); const fileId = refId(gcodeFile.file);
let deploymentActive = true; let deploymentActive = true;
const updateDeployProgress = async (progress) => { const updateDeployProgress = async (progress) => {
if (!deploymentActive) { if (!deploymentActive) {
return; return;
} }
await this.database.updateSubJobState(subJob._id, { await this.database.updateSubJobState(refId(subJob), {
type: "deploying", type: "deploying",
progress, progress,
}); });
await this.database.updateJobState(subJob.job._id); await this.database.updateJobState(refId(subJob.job));
}; };
const file = await this.socketClient.fileManager.getFile( const file = await this.socketClient.fileManager.getFile(
@ -1300,10 +1301,10 @@ export class PrinterClient {
subJob._id, subJob._id,
mostRecentQueuedJob.job_id, mostRecentQueuedJob.job_id,
); );
await this.database.updateSubJobState(subJob._id, { await this.database.updateSubJobState(refId(subJob), {
type: "queued", type: "queued",
}); });
await this.database.updateJobState(subJob.job._id); await this.database.updateJobState(refId(subJob.job));
this.queue.push({ this.queue.push({
...updatedSubJob, ...updatedSubJob,

View File

@ -4,6 +4,7 @@ import { loadConfig } from "../config.js";
import log4js from "log4js"; import log4js from "log4js";
import { sendIPC } from "../desktop/notify.js"; import { sendIPC } from "../desktop/notify.js";
import { startWaiting, stopWaiting } from "../spinner.js"; import { startWaiting, stopWaiting } from "../spinner.js";
import { refId } from "../objectid.js";
// Load configuration // Load configuration
const config = loadConfig(); const config = loadConfig();
@ -36,7 +37,7 @@ export class PrinterManager {
var removedPrintersCount = 0; var removedPrintersCount = 0;
// Remove printer clients that are no longer in the printers list // Remove printer clients that are no longer in the printers list
const printerIds = this.printers.map((printer) => printer._id); const printerIds = this.printers.map((printer) => refId(printer._id));
for (const [printerId, printerClient] of this.printerClients.entries()) { for (const [printerId, printerClient] of this.printerClients.entries()) {
if (!printerIds.includes(printerId)) { if (!printerIds.includes(printerId)) {
// Close the connection before removing // Close the connection before removing
@ -53,7 +54,7 @@ export class PrinterManager {
var addedPrintersCount = 0; var addedPrintersCount = 0;
// Add new printer clients for printers not in the printerClients map // Add new printer clients for printers not in the printerClients map
for (const printer of this.printers) { for (const printer of this.printers) {
const printerId = printer._id; const printerId = refId(printer._id);
if (!this.printerClients.has(printerId)) { if (!this.printerClients.has(printerId)) {
const printerClient = new PrinterClient(printer, this); const printerClient = new PrinterClient(printer, this);
await printerClient.connect(); await printerClient.connect();

View File

@ -10,6 +10,7 @@ import { HostManager } from "../host/hostmanager.js";
import { FileManager } from "../files/filemanager.js"; import { FileManager } from "../files/filemanager.js";
import { DocumentPrinterManager } from "../documentprinter/documentprintermanager.js"; import { DocumentPrinterManager } from "../documentprinter/documentprintermanager.js";
import { startWaiting, stopWaiting } from "../spinner.js"; import { startWaiting, stopWaiting } from "../spinner.js";
import { stringIdParser } from "./stringidparser.js";
const config = loadConfig(); const config = loadConfig();
@ -72,6 +73,7 @@ export class SocketClient {
await stopWaiting(); await stopWaiting();
startWaiting(`Connecting to Socket.IO server: ${config.url}`, logger); startWaiting(`Connecting to Socket.IO server: ${config.url}`, logger);
this.socket = io(config.url, { this.socket = io(config.url, {
parser: stringIdParser,
auth: { type: "host" }, auth: { type: "host" },
reconnection: false, reconnection: false,
timeout: 3000, // 3 second timeout timeout: 3000, // 3 second timeout

View File

@ -0,0 +1,19 @@
import { createRequire } from "node:module";
import { stringifyIds } from "../objectid.js";
const require = createRequire(import.meta.url);
const { Encoder, Decoder } = createRequire(require.resolve("socket.io-client"))(
"socket.io-parser",
);
class StringIdEncoder extends Encoder {
encode(packet) {
if (packet?.data == null) return super.encode(packet);
return super.encode({ ...packet, data: stringifyIds(packet.data) });
}
}
export const stringIdParser = {
Encoder: StringIdEncoder,
Decoder,
};