Update configuration and enhance CLI logging functionality
Some checks failed
farmcontrol/farmcontrol-server/pipeline/head There was a failure building this commit
Some checks failed
farmcontrol/farmcontrol-server/pipeline/head There was a failure building this commit
- Change log level in development config from "debug" to "info" for improved clarity. - Add new dependencies: chalk and cli-spinners for enhanced CLI output. - Introduce command line utility functions for better logging and spinner management. - Refactor various components to utilize the new CLI logging methods, improving user feedback during operations. - Update package and lock files to reflect new dependencies.
This commit is contained in:
parent
20080929f9
commit
ee9895d562
@ -26,6 +26,8 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"axios": "^1.16.1",
|
"axios": "^1.16.1",
|
||||||
"canvas": "^3.2.3",
|
"canvas": "^3.2.3",
|
||||||
|
"chalk": "^6.0.0",
|
||||||
|
"cli-spinners": "^3.4.0",
|
||||||
"etcd3": "^1.1.2",
|
"etcd3": "^1.1.2",
|
||||||
"express": "^5.2.1",
|
"express": "^5.2.1",
|
||||||
"form-data": "^4.0.5",
|
"form-data": "^4.0.5",
|
||||||
@ -50,9 +52,9 @@
|
|||||||
"antd": "^5.29.2",
|
"antd": "^5.29.2",
|
||||||
"concurrently": "^9.2.1",
|
"concurrently": "^9.2.1",
|
||||||
"cross-env": "^10.1.0",
|
"cross-env": "^10.1.0",
|
||||||
"esbuild": "^0.25.12",
|
|
||||||
"electron": "^38.7.1",
|
"electron": "^38.7.1",
|
||||||
"electron-builder": "^26.0.12",
|
"electron-builder": "^26.0.12",
|
||||||
|
"esbuild": "^0.25.12",
|
||||||
"jest": "^30.4.2",
|
"jest": "^30.4.2",
|
||||||
"nodemon": "^3.1.14",
|
"nodemon": "^3.1.14",
|
||||||
"pkg": "^5.8.1",
|
"pkg": "^5.8.1",
|
||||||
|
|||||||
18
pnpm-lock.yaml
generated
18
pnpm-lock.yaml
generated
@ -14,6 +14,12 @@ importers:
|
|||||||
canvas:
|
canvas:
|
||||||
specifier: ^3.2.3
|
specifier: ^3.2.3
|
||||||
version: 3.2.3
|
version: 3.2.3
|
||||||
|
chalk:
|
||||||
|
specifier: ^6.0.0
|
||||||
|
version: 6.0.0
|
||||||
|
cli-spinners:
|
||||||
|
specifier: ^3.4.0
|
||||||
|
version: 3.4.0
|
||||||
etcd3:
|
etcd3:
|
||||||
specifier: ^1.1.2
|
specifier: ^1.1.2
|
||||||
version: 1.1.2
|
version: 1.1.2
|
||||||
@ -1765,6 +1771,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==}
|
resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
|
|
||||||
|
chalk@6.0.0:
|
||||||
|
resolution: {integrity: sha512-2uNTXIuTTxk7ciZgAU1BQcgnchcG0xXnrs6jzkQfj9SsRa9M2s5zE8WT96hS6KmG4MzWHSrvH43DF1m4XRkrFg==}
|
||||||
|
engines: {node: '>=22'}
|
||||||
|
|
||||||
char-regex@1.0.2:
|
char-regex@1.0.2:
|
||||||
resolution: {integrity: sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==}
|
resolution: {integrity: sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
@ -1802,6 +1812,10 @@ packages:
|
|||||||
classnames@2.5.1:
|
classnames@2.5.1:
|
||||||
resolution: {integrity: sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==}
|
resolution: {integrity: sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==}
|
||||||
|
|
||||||
|
cli-spinners@3.4.0:
|
||||||
|
resolution: {integrity: sha512-bXfOC4QcT1tKXGorxL3wbJm6XJPDqEnij2gQ2m7ESQuE+/z9YFIWnl/5RpTiKWbMq3EVKR4fRLJGn6DVfu0mpw==}
|
||||||
|
engines: {node: '>=18.20'}
|
||||||
|
|
||||||
cliui@7.0.4:
|
cliui@7.0.4:
|
||||||
resolution: {integrity: sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==}
|
resolution: {integrity: sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==}
|
||||||
|
|
||||||
@ -6402,6 +6416,8 @@ snapshots:
|
|||||||
ansi-styles: 4.3.0
|
ansi-styles: 4.3.0
|
||||||
supports-color: 7.2.0
|
supports-color: 7.2.0
|
||||||
|
|
||||||
|
chalk@6.0.0: {}
|
||||||
|
|
||||||
char-regex@1.0.2: {}
|
char-regex@1.0.2: {}
|
||||||
|
|
||||||
chokidar@3.6.0:
|
chokidar@3.6.0:
|
||||||
@ -6445,6 +6461,8 @@ snapshots:
|
|||||||
|
|
||||||
classnames@2.5.1: {}
|
classnames@2.5.1: {}
|
||||||
|
|
||||||
|
cli-spinners@3.4.0: {}
|
||||||
|
|
||||||
cliui@7.0.4:
|
cliui@7.0.4:
|
||||||
dependencies:
|
dependencies:
|
||||||
string-width: 4.2.3
|
string-width: 4.2.3
|
||||||
|
|||||||
203
src/commandlineutils.js
Normal file
203
src/commandlineutils.js
Normal file
@ -0,0 +1,203 @@
|
|||||||
|
import axios from "axios";
|
||||||
|
import chalk from "chalk";
|
||||||
|
import { LOCAL_SERVER_PORT } from "./localserver/localserver.js";
|
||||||
|
import {
|
||||||
|
createSpinner,
|
||||||
|
registerSpinner,
|
||||||
|
stopWaiting as stopSpinnerWaiting,
|
||||||
|
} from "./spinner.js";
|
||||||
|
|
||||||
|
const LOG_SECTION_WIDTH = 55;
|
||||||
|
|
||||||
|
let sharedCli = null;
|
||||||
|
|
||||||
|
export function getCli() {
|
||||||
|
return sharedCli;
|
||||||
|
}
|
||||||
|
|
||||||
|
export { startWaiting, stopWaiting } from "./spinner.js";
|
||||||
|
|
||||||
|
export function formatState(stateType) {
|
||||||
|
switch (stateType) {
|
||||||
|
case "online":
|
||||||
|
return chalk.green("Online");
|
||||||
|
case "offline":
|
||||||
|
return chalk.dim("Offline");
|
||||||
|
case "error":
|
||||||
|
return chalk.red("Error");
|
||||||
|
case "inactive":
|
||||||
|
return chalk.dim("Inactive");
|
||||||
|
case "connecting":
|
||||||
|
return chalk.yellow("Connecting");
|
||||||
|
case "printing":
|
||||||
|
return chalk.blue("Printing");
|
||||||
|
case "paused":
|
||||||
|
return chalk.yellow("Paused");
|
||||||
|
case "cancelled":
|
||||||
|
return chalk.red("Cancelled");
|
||||||
|
case "completed":
|
||||||
|
return chalk.green("Completed");
|
||||||
|
default:
|
||||||
|
return chalk.dim("Unknown");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createCliLogger(logger) {
|
||||||
|
const spinner = createSpinner();
|
||||||
|
registerSpinner(spinner);
|
||||||
|
|
||||||
|
function withSpinnerStopped(fn) {
|
||||||
|
return async (...args) => {
|
||||||
|
await stopSpinnerWaiting();
|
||||||
|
return fn(...args);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function logSectionHeader(title) {
|
||||||
|
const padding = LOG_SECTION_WIDTH - title.length;
|
||||||
|
const left = Math.floor(padding / 2);
|
||||||
|
const right = padding - left;
|
||||||
|
logger.info(
|
||||||
|
chalk.dim.cyan("-".repeat(left)) +
|
||||||
|
chalk.bold.white(title) +
|
||||||
|
chalk.dim.cyan("-".repeat(right)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function logSectionFooter() {
|
||||||
|
logger.info(chalk.dim.cyan("-".repeat(LOG_SECTION_WIDTH)));
|
||||||
|
}
|
||||||
|
|
||||||
|
const cli = {
|
||||||
|
waiting(message) {
|
||||||
|
spinner.start(message, logger);
|
||||||
|
},
|
||||||
|
|
||||||
|
async stopWaiting() {
|
||||||
|
await stopSpinnerWaiting();
|
||||||
|
},
|
||||||
|
|
||||||
|
blank: withSpinnerStopped(() => {
|
||||||
|
process.stdout.write(" \n");
|
||||||
|
}),
|
||||||
|
|
||||||
|
success: withSpinnerStopped((message, ...rest) => {
|
||||||
|
logger.info(chalk.bold.white(`✔ `) + chalk.bold.green(message), ...rest);
|
||||||
|
}),
|
||||||
|
|
||||||
|
failure: withSpinnerStopped((message, ...rest) => {
|
||||||
|
logger.error(chalk.bold.white(`✘ `) + chalk.bold.red(message), ...rest);
|
||||||
|
}),
|
||||||
|
|
||||||
|
warn: withSpinnerStopped((message, ...rest) => {
|
||||||
|
logger.warn(chalk.yellow(message), ...rest);
|
||||||
|
}),
|
||||||
|
|
||||||
|
info: withSpinnerStopped((message, ...rest) => {
|
||||||
|
logger.info(message, ...rest);
|
||||||
|
}),
|
||||||
|
|
||||||
|
secure: withSpinnerStopped((message) => {
|
||||||
|
logger.info(chalk.cyan(`🔒 ${message}`));
|
||||||
|
}),
|
||||||
|
|
||||||
|
label: withSpinnerStopped((key, value) => {
|
||||||
|
logger.info(chalk.bold(key), value);
|
||||||
|
}),
|
||||||
|
|
||||||
|
yesNo(value) {
|
||||||
|
return value ? chalk.green("Yes") : chalk.yellow("No");
|
||||||
|
},
|
||||||
|
|
||||||
|
formatState,
|
||||||
|
|
||||||
|
sectionHeader: withSpinnerStopped(logSectionHeader),
|
||||||
|
sectionFooter: withSpinnerStopped(logSectionFooter),
|
||||||
|
};
|
||||||
|
|
||||||
|
sharedCli = cli;
|
||||||
|
return cli;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getArgValue(flag) {
|
||||||
|
const withEquals = process.argv.find((arg) => arg.startsWith(`${flag}=`));
|
||||||
|
if (withEquals) {
|
||||||
|
return withEquals.slice(flag.length + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
const index = process.argv.indexOf(flag);
|
||||||
|
if (index !== -1 && index + 1 < process.argv.length) {
|
||||||
|
return process.argv[index + 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const isHeadless = process.argv.includes("--headless");
|
||||||
|
export const isInfo = process.argv.includes("--info");
|
||||||
|
export const otpCode = getArgValue("--otp") || undefined;
|
||||||
|
|
||||||
|
export function printerHostInfo(host, cli) {
|
||||||
|
let ref = host?._reference || "Unknown";
|
||||||
|
|
||||||
|
if (ref !== "Unknown") {
|
||||||
|
ref = `HST:${ref}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
cli.label("Reference:", ref);
|
||||||
|
cli.label("Name:", host?.name || "Unknown");
|
||||||
|
|
||||||
|
if (host?.tags?.length > 0) {
|
||||||
|
cli.label("Tags:", chalk.dim(`- `) + chalk.blue(`${host.tags[0]}`));
|
||||||
|
for (let i = 0; i < host.tags.length - 1; i++) {
|
||||||
|
const tag = host.tags[i + 1];
|
||||||
|
cli.info(chalk.dim(` - `) + chalk.blue(`${tag}`));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
cli.label("Tags:", chalk.dim("n/a"));
|
||||||
|
}
|
||||||
|
|
||||||
|
cli.label("State:", cli.formatState(host?.state?.type));
|
||||||
|
}
|
||||||
|
|
||||||
|
export function printAuthCommand(cli) {
|
||||||
|
cli.info(
|
||||||
|
"Run '" +
|
||||||
|
chalk.bold.white("farmcontrol-server") +
|
||||||
|
" " +
|
||||||
|
chalk.bold.magentaBright("--otp") +
|
||||||
|
" " +
|
||||||
|
chalk.dim.white("<otp>") +
|
||||||
|
"' to authenticate.",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function checkRunning() {
|
||||||
|
try {
|
||||||
|
const response = await axios.get(
|
||||||
|
`http://127.0.0.1:${LOCAL_SERVER_PORT}/info`,
|
||||||
|
{ timeout: 1000 },
|
||||||
|
);
|
||||||
|
if (response.data?.running === true) {
|
||||||
|
return response.data;
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function authenticateWithOtp(code) {
|
||||||
|
try {
|
||||||
|
const response = await axios.post(
|
||||||
|
`http://127.0.0.1:${LOCAL_SERVER_PORT}/otpAuth`,
|
||||||
|
{ otp: code },
|
||||||
|
{ timeout: 30000 },
|
||||||
|
);
|
||||||
|
return response.data;
|
||||||
|
} catch (err) {
|
||||||
|
if (err.response?.data) {
|
||||||
|
return err.response.data;
|
||||||
|
}
|
||||||
|
throw new Error("Failed to authenticate with OTP");
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -3,6 +3,7 @@ import fs from "fs";
|
|||||||
import path from "path";
|
import path from "path";
|
||||||
import { fileURLToPath } from "url";
|
import { fileURLToPath } from "url";
|
||||||
import log4js from "log4js";
|
import log4js from "log4js";
|
||||||
|
import { startWaiting, stopWaiting } from "./spinner.js";
|
||||||
|
|
||||||
// Determine environment
|
// Determine environment
|
||||||
const __filename = fileURLToPath(import.meta.url);
|
const __filename = fileURLToPath(import.meta.url);
|
||||||
@ -22,7 +23,7 @@ function getProductionConfigPath() {
|
|||||||
return path.join(
|
return path.join(
|
||||||
process.env.PROGRAMDATA || "C:\\ProgramData",
|
process.env.PROGRAMDATA || "C:\\ProgramData",
|
||||||
"farmcontrol-server",
|
"farmcontrol-server",
|
||||||
"config.json"
|
"config.json",
|
||||||
);
|
);
|
||||||
case "darwin":
|
case "darwin":
|
||||||
return path.join(
|
return path.join(
|
||||||
@ -30,7 +31,7 @@ function getProductionConfigPath() {
|
|||||||
"Library",
|
"Library",
|
||||||
"Application Support",
|
"Application Support",
|
||||||
"farmcontrol-server",
|
"farmcontrol-server",
|
||||||
"config.json"
|
"config.json",
|
||||||
);
|
);
|
||||||
case "linux":
|
case "linux":
|
||||||
default:
|
default:
|
||||||
@ -48,7 +49,7 @@ function getDevelopmentConfigPath() {
|
|||||||
return path.join(
|
return path.join(
|
||||||
process.env.APPDATA || path.join(homeDir, "AppData", "Roaming"),
|
process.env.APPDATA || path.join(homeDir, "AppData", "Roaming"),
|
||||||
"farmcontrol-server",
|
"farmcontrol-server",
|
||||||
"config.json"
|
"config.json",
|
||||||
);
|
);
|
||||||
case "darwin":
|
case "darwin":
|
||||||
return path.join(
|
return path.join(
|
||||||
@ -56,14 +57,14 @@ function getDevelopmentConfigPath() {
|
|||||||
"Library",
|
"Library",
|
||||||
"Application Support",
|
"Application Support",
|
||||||
"farmcontrol-server",
|
"farmcontrol-server",
|
||||||
"config.json"
|
"config.json",
|
||||||
);
|
);
|
||||||
default:
|
default:
|
||||||
return path.join(
|
return path.join(
|
||||||
homeDir,
|
homeDir,
|
||||||
".config",
|
".config",
|
||||||
"farmcontrol-server",
|
"farmcontrol-server",
|
||||||
"config.json"
|
"config.json",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -104,7 +105,7 @@ export function loadConfig() {
|
|||||||
try {
|
try {
|
||||||
if (!fs.existsSync(CONFIG_PATH)) {
|
if (!fs.existsSync(CONFIG_PATH)) {
|
||||||
logger.info(
|
logger.info(
|
||||||
`Configuration file not found at ${CONFIG_PATH}. Creating a new one.`
|
`Configuration file not found at ${CONFIG_PATH}. Creating a new one.`,
|
||||||
);
|
);
|
||||||
const configDir = path.dirname(CONFIG_PATH);
|
const configDir = path.dirname(CONFIG_PATH);
|
||||||
if (!fs.existsSync(configDir)) {
|
if (!fs.existsSync(configDir)) {
|
||||||
@ -113,7 +114,7 @@ export function loadConfig() {
|
|||||||
fs.writeFileSync(
|
fs.writeFileSync(
|
||||||
CONFIG_PATH,
|
CONFIG_PATH,
|
||||||
JSON.stringify(DEFAULT_CONFIG, null, 2),
|
JSON.stringify(DEFAULT_CONFIG, null, 2),
|
||||||
"utf8"
|
"utf8",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -122,7 +123,7 @@ export function loadConfig() {
|
|||||||
|
|
||||||
if (!config[NODE_ENV]) {
|
if (!config[NODE_ENV]) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Configuration for environment '${NODE_ENV}' not found in config.json`
|
`Configuration for environment '${NODE_ENV}' not found in config.json`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -134,9 +135,9 @@ export function loadConfig() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Save config file
|
// Save config file
|
||||||
export function saveConfig(newConfig) {
|
export async function saveConfig(newConfig) {
|
||||||
|
startWaiting("Saving...", logger);
|
||||||
try {
|
try {
|
||||||
logger.info("Saving...");
|
|
||||||
let config = {};
|
let config = {};
|
||||||
if (fs.existsSync(CONFIG_PATH)) {
|
if (fs.existsSync(CONFIG_PATH)) {
|
||||||
const configData = fs.readFileSync(CONFIG_PATH, "utf8");
|
const configData = fs.readFileSync(CONFIG_PATH, "utf8");
|
||||||
@ -153,8 +154,10 @@ export function saveConfig(newConfig) {
|
|||||||
|
|
||||||
// Write back to file with 2-space indentation
|
// Write back to file with 2-space indentation
|
||||||
fs.writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2), "utf8");
|
fs.writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2), "utf8");
|
||||||
|
await stopWaiting();
|
||||||
logger.info(`Configuration for '${NODE_ENV}' saved successfully.`);
|
logger.info(`Configuration for '${NODE_ENV}' saved successfully.`);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
await stopWaiting();
|
||||||
logger.error("Error saving config:", err);
|
logger.error("Error saving config:", err);
|
||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
// documentprinterclient.js - Handles connection to a single document printer
|
// documentprinterclient.js - Handles connection to a single document printer
|
||||||
import { loadConfig } from "../config.js";
|
import { loadConfig } from "../config.js";
|
||||||
import log4js from "log4js";
|
import log4js from "log4js";
|
||||||
|
import { startWaiting, stopWaiting } from "../spinner.js";
|
||||||
import CupsPrinterInterface from "./interfaces/cupsinterface.js";
|
import CupsPrinterInterface from "./interfaces/cupsinterface.js";
|
||||||
import ReceiptInterface from "./interfaces/receiptinterface.js";
|
import ReceiptInterface from "./interfaces/receiptinterface.js";
|
||||||
import { sendIPC } from "../electron/ipc.js";
|
import { sendIPC } from "../electron/ipc.js";
|
||||||
@ -138,9 +139,6 @@ export class DocumentPrinterClient {
|
|||||||
|
|
||||||
async connect() {
|
async connect() {
|
||||||
if (this.active == false) {
|
if (this.active == false) {
|
||||||
logger.info(
|
|
||||||
`Document printer ${this.id} is not active, skipping connection`,
|
|
||||||
);
|
|
||||||
this.shouldReconnect = false;
|
this.shouldReconnect = false;
|
||||||
this.isOnline = false;
|
this.isOnline = false;
|
||||||
this.state = { type: "inactive" };
|
this.state = { type: "inactive" };
|
||||||
@ -234,29 +232,37 @@ export class DocumentPrinterClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async initialize() {
|
async initialize() {
|
||||||
logger.info("Running document printer initialization...");
|
startWaiting("Running document printer initialization...", logger);
|
||||||
this.state = { type: "initializing", message: null };
|
try {
|
||||||
this.isOnline = true;
|
this.state = { type: "initializing", message: null };
|
||||||
this.connectedAt = new Date();
|
this.isOnline = true;
|
||||||
await this.updateDocumentPrinterState();
|
this.connectedAt = new Date();
|
||||||
if (this.printerInterface && this.printerInterface.initialize) {
|
|
||||||
const result = await this.printerInterface.initialize();
|
|
||||||
if (result.error) {
|
|
||||||
logger.error(
|
|
||||||
`Error initializing document printer ${this.documentPrinter.name}:`,
|
|
||||||
result.error,
|
|
||||||
);
|
|
||||||
this.state = { type: "offline", message: result.error };
|
|
||||||
await this.updateDocumentPrinterState();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
this.state = { type: "standby", message: null };
|
|
||||||
await this.updateDocumentPrinterState();
|
await this.updateDocumentPrinterState();
|
||||||
this.eventUpdateInterval = setInterval(
|
if (this.printerInterface && this.printerInterface.initialize) {
|
||||||
this.handleEventUpdate.bind(this),
|
const result = await this.printerInterface.initialize();
|
||||||
3000,
|
if (result.error) {
|
||||||
);
|
logger.error(
|
||||||
return true;
|
`Error initializing document printer ${this.documentPrinter.name}:`,
|
||||||
|
result.error,
|
||||||
|
);
|
||||||
|
this.state = { type: "offline", message: result.error };
|
||||||
|
await this.updateDocumentPrinterState();
|
||||||
|
await stopWaiting();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
this.state = { type: "standby", message: null };
|
||||||
|
await this.updateDocumentPrinterState();
|
||||||
|
this.eventUpdateInterval = setInterval(
|
||||||
|
this.handleEventUpdate.bind(this),
|
||||||
|
3000,
|
||||||
|
);
|
||||||
|
await stopWaiting();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
await stopWaiting();
|
||||||
|
} catch (error) {
|
||||||
|
await stopWaiting();
|
||||||
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -2,6 +2,7 @@ import { loadConfig } from "../config.js";
|
|||||||
import log4js from "log4js";
|
import log4js from "log4js";
|
||||||
import { sendIPC } from "../electron/ipc.js";
|
import { sendIPC } from "../electron/ipc.js";
|
||||||
import { DocumentPrinterClient } from "./documentprinterclient.js";
|
import { DocumentPrinterClient } from "./documentprinterclient.js";
|
||||||
|
import { startWaiting, stopWaiting } from "../spinner.js";
|
||||||
|
|
||||||
const config = loadConfig();
|
const config = loadConfig();
|
||||||
|
|
||||||
@ -16,7 +17,7 @@ export class DocumentPrinterManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async reloadDocumentPrinters() {
|
async reloadDocumentPrinters() {
|
||||||
logger.info("Reloading document printers...");
|
startWaiting("Reloading document printers...", logger);
|
||||||
try {
|
try {
|
||||||
this.documentPrinters = await this.socketClient.listObjects({
|
this.documentPrinters = await this.socketClient.listObjects({
|
||||||
objectType: "documentPrinter",
|
objectType: "documentPrinter",
|
||||||
@ -62,7 +63,9 @@ export class DocumentPrinterManager {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
await stopWaiting();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
await stopWaiting();
|
||||||
logger.error("Failed to update document printers:", error);
|
logger.error("Failed to update document printers:", error);
|
||||||
this.documentPrinters = [];
|
this.documentPrinters = [];
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,6 +2,7 @@
|
|||||||
import log4js from "log4js";
|
import log4js from "log4js";
|
||||||
import { loadConfig } from "../../config.js";
|
import { loadConfig } from "../../config.js";
|
||||||
import ipp from "ipp";
|
import ipp from "ipp";
|
||||||
|
import { startWaiting, stopWaiting } from "../../spinner.js";
|
||||||
|
|
||||||
const config = loadConfig();
|
const config = loadConfig();
|
||||||
const logger = log4js.getLogger("CUPS Printer Interface");
|
const logger = log4js.getLogger("CUPS Printer Interface");
|
||||||
@ -119,16 +120,18 @@ export default class CupsInterface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async disconnect() {
|
async disconnect() {
|
||||||
logger.info(`Disconnecting from CUPS printer...`);
|
startWaiting("Disconnecting from CUPS printer...", logger);
|
||||||
this.isConnected = false;
|
this.isConnected = false;
|
||||||
this.cupsPrinter = null;
|
this.cupsPrinter = null;
|
||||||
|
await stopWaiting();
|
||||||
return { success: true };
|
return { success: true };
|
||||||
}
|
}
|
||||||
|
|
||||||
async initialize() {
|
async initialize() {
|
||||||
logger.info(`Initializing CUPS printer...`);
|
startWaiting("Initializing CUPS printer...", logger);
|
||||||
|
|
||||||
if (!this.isConnected || !this.cupsPrinter) {
|
if (!this.isConnected || !this.cupsPrinter) {
|
||||||
|
await stopWaiting();
|
||||||
return { error: "Printer is not connected" };
|
return { error: "Printer is not connected" };
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -157,14 +160,17 @@ export default class CupsInterface {
|
|||||||
// Printer states: 3 = idle, 4 = processing, 5 = stopped
|
// Printer states: 3 = idle, 4 = processing, 5 = stopped
|
||||||
if (printerState === 5) {
|
if (printerState === 5) {
|
||||||
logger.warn(`Printer ${this.name} is stopped: ${stateMessage}`);
|
logger.warn(`Printer ${this.name} is stopped: ${stateMessage}`);
|
||||||
|
await stopWaiting();
|
||||||
return { error: `Printer is stopped: ${stateMessage}` };
|
return { error: `Printer is stopped: ${stateMessage}` };
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.info(
|
logger.info(
|
||||||
`CUPS printer ${this.name} initialized successfully. State: ${printerState}`
|
`CUPS printer ${this.name} initialized successfully. State: ${printerState}`
|
||||||
);
|
);
|
||||||
|
await stopWaiting();
|
||||||
return true;
|
return true;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
await stopWaiting();
|
||||||
logger.error(`Failed to initialize CUPS printer ${this.name}:`, error);
|
logger.error(`Failed to initialize CUPS printer ${this.name}:`, error);
|
||||||
return { error: error.message || "Failed to initialize CUPS printer" };
|
return { error: error.message || "Failed to initialize CUPS printer" };
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import log4js from "log4js";
|
import log4js from "log4js";
|
||||||
import { notPrompting } from "../utils.js";
|
import { notPrompting } from "../utils.js";
|
||||||
|
import { startWaiting, stopWaiting } from "../spinner.js";
|
||||||
|
|
||||||
const logger = log4js.getLogger("IPC");
|
const logger = log4js.getLogger("IPC");
|
||||||
let mainWindow = null;
|
let mainWindow = null;
|
||||||
@ -22,8 +23,8 @@ export async function setupIPC() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ipcMain.on("getData", (event) => {
|
ipcMain.on("getData", async () => {
|
||||||
logger.info("Getting data...");
|
startWaiting("Getting data...", logger);
|
||||||
try {
|
try {
|
||||||
// Get the global socket client instance
|
// Get the global socket client instance
|
||||||
const socketClient = global.socketClient;
|
const socketClient = global.socketClient;
|
||||||
@ -35,6 +36,7 @@ export async function setupIPC() {
|
|||||||
sendIPC("setHost", {});
|
sendIPC("setHost", {});
|
||||||
sendIPC("setPrinters", []);
|
sendIPC("setPrinters", []);
|
||||||
sendIPC("setDocumentPrinters", []);
|
sendIPC("setDocumentPrinters", []);
|
||||||
|
await stopWaiting();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -46,8 +48,9 @@ export async function setupIPC() {
|
|||||||
sendIPC("setPrinters", socketClient.printerManager.printers || []);
|
sendIPC("setPrinters", socketClient.printerManager.printers || []);
|
||||||
sendIPC(
|
sendIPC(
|
||||||
"setDocumentPrinters",
|
"setDocumentPrinters",
|
||||||
socketClient.documentPrinterManager.documentPrinters || []
|
socketClient.documentPrinterManager.documentPrinters || [],
|
||||||
);
|
);
|
||||||
|
await stopWaiting();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error("Error getting printer data:", error);
|
logger.error("Error getting printer data:", error);
|
||||||
sendIPC("setAuthenticated", false);
|
sendIPC("setAuthenticated", false);
|
||||||
@ -55,6 +58,7 @@ export async function setupIPC() {
|
|||||||
sendIPC("setLoading", false);
|
sendIPC("setLoading", false);
|
||||||
sendIPC("setHost", {});
|
sendIPC("setHost", {});
|
||||||
sendIPC("setPrinters", []);
|
sendIPC("setPrinters", []);
|
||||||
|
await stopWaiting();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -83,7 +87,7 @@ export async function setupIPC() {
|
|||||||
|
|
||||||
// OTP authentication handler
|
// OTP authentication handler
|
||||||
ipcMain.on("authenticateOTP", async (event, otp) => {
|
ipcMain.on("authenticateOTP", async (event, otp) => {
|
||||||
logger.info("Authenticating with OTP...");
|
startWaiting("Authenticating with OTP...", logger);
|
||||||
try {
|
try {
|
||||||
const socketClient = global.socketClient;
|
const socketClient = global.socketClient;
|
||||||
if (socketClient) {
|
if (socketClient) {
|
||||||
@ -92,7 +96,9 @@ export async function setupIPC() {
|
|||||||
} else {
|
} else {
|
||||||
logger.error("Socket client not available for OTP authentication");
|
logger.error("Socket client not available for OTP authentication");
|
||||||
}
|
}
|
||||||
|
await stopWaiting();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
await stopWaiting();
|
||||||
logger.error("Error during OTP authentication:", error);
|
logger.error("Error during OTP authentication:", error);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import path from "path";
|
|||||||
import { loadConfig } from "../config.js";
|
import { loadConfig } from "../config.js";
|
||||||
import log4js from "log4js";
|
import log4js from "log4js";
|
||||||
import { fileURLToPath } from "url";
|
import { fileURLToPath } from "url";
|
||||||
|
import { startWaiting, stopWaiting } from "../spinner.js";
|
||||||
// Load configuration
|
// Load configuration
|
||||||
const config = loadConfig();
|
const config = loadConfig();
|
||||||
|
|
||||||
@ -21,7 +22,7 @@ export async function createElectronWindow() {
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.warn(
|
logger.warn(
|
||||||
"Electron not available, skipping window creation. Error:",
|
"Electron not available, skipping window creation. Error:",
|
||||||
error
|
error,
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -49,8 +50,8 @@ export async function createElectronWindow() {
|
|||||||
process.env.NODE_ENV === "development"
|
process.env.NODE_ENV === "development"
|
||||||
? path.join(__dirname, "preload.js")
|
? path.join(__dirname, "preload.js")
|
||||||
: isRunningFromBuild
|
: isRunningFromBuild
|
||||||
? path.join(__dirname, "preload.js")
|
? path.join(__dirname, "preload.js")
|
||||||
: path.join(__dirname, "..", "build", "electron", "preload.js");
|
: path.join(__dirname, "..", "build", "electron", "preload.js");
|
||||||
|
|
||||||
const win = new BrowserWindow({
|
const win = new BrowserWindow({
|
||||||
width: 900,
|
width: 900,
|
||||||
@ -71,7 +72,7 @@ export async function createElectronWindow() {
|
|||||||
|
|
||||||
logger.info("Preload Script", preloadPath);
|
logger.info("Preload Script", preloadPath);
|
||||||
if (process.env.NODE_ENV === "development") {
|
if (process.env.NODE_ENV === "development") {
|
||||||
logger.info("Loading development url...");
|
startWaiting("Loading development url...", logger);
|
||||||
win.loadURL("http://localhost:5287"); // Vite dev server
|
win.loadURL("http://localhost:5287"); // Vite dev server
|
||||||
} else {
|
} else {
|
||||||
// In production, the built files will be in the build/electron directory
|
// In production, the built files will be in the build/electron directory
|
||||||
@ -79,12 +80,14 @@ export async function createElectronWindow() {
|
|||||||
? path.join(__dirname, "index.html")
|
? path.join(__dirname, "index.html")
|
||||||
: path.join(__dirname, "..", "build", "electron", "index.html");
|
: path.join(__dirname, "..", "build", "electron", "index.html");
|
||||||
|
|
||||||
|
startWaiting("Loading production file...", logger);
|
||||||
logger.info("Loading production file:", htmlPath);
|
logger.info("Loading production file:", htmlPath);
|
||||||
win.loadFile(htmlPath);
|
win.loadFile(htmlPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Resolve the promise when the window is ready
|
// Resolve the promise when the window is ready
|
||||||
win.webContents.on("did-finish-load", () => {
|
win.webContents.on("did-finish-load", async () => {
|
||||||
|
await stopWaiting();
|
||||||
resolve(win);
|
resolve(win);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,9 +1,8 @@
|
|||||||
process.env.FC_HEADLESS_ENTRY = "1";
|
process.env.FC_HEADLESS_ENTRY = "1";
|
||||||
|
|
||||||
import("./index.js")
|
import("./index.js").then(({ init }) =>
|
||||||
.then(({ init }) =>
|
init({ headless: true }).catch((err) => {
|
||||||
init({ headless: true }).catch((err) => {
|
console.error(err.message);
|
||||||
console.error(err.message);
|
process.exit(1);
|
||||||
process.exit(1);
|
}),
|
||||||
}),
|
);
|
||||||
);
|
|
||||||
|
|||||||
188
src/index.js
188
src/index.js
@ -1,163 +1,130 @@
|
|||||||
import { fileURLToPath } from "node:url";
|
import { fileURLToPath } from "node:url";
|
||||||
import path from "node:path";
|
import path from "node:path";
|
||||||
import { loadConfig } from "./config.js";
|
import { loadConfig } from "./config.js";
|
||||||
import axios from "axios";
|
|
||||||
import log4js from "log4js";
|
import log4js from "log4js";
|
||||||
import { createElectronWindow } from "./electron/window.js";
|
import { createElectronWindow } from "./electron/window.js";
|
||||||
import { setupIPC } from "./electron/ipc.js";
|
import { setupIPC } from "./electron/ipc.js";
|
||||||
import { LOCAL_SERVER_PORT, LocalServer } from "./localserver/localserver.js";
|
import { LocalServer } from "./localserver/localserver.js";
|
||||||
import { SocketClient } from "./socket/socketclient.js";
|
import { SocketClient } from "./socket/socketclient.js";
|
||||||
|
import {
|
||||||
|
authenticateWithOtp,
|
||||||
|
checkRunning,
|
||||||
|
createCliLogger,
|
||||||
|
isHeadless,
|
||||||
|
isInfo,
|
||||||
|
otpCode,
|
||||||
|
printAuthCommand,
|
||||||
|
printerHostInfo,
|
||||||
|
startWaiting,
|
||||||
|
stopWaiting,
|
||||||
|
} from "./commandlineutils.js";
|
||||||
|
|
||||||
if (!process.env.NODE_ENV) {
|
if (!process.env.NODE_ENV) {
|
||||||
process.env.NODE_ENV = "production";
|
process.env.NODE_ENV = "production";
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load configuration
|
|
||||||
const config = loadConfig();
|
const config = loadConfig();
|
||||||
|
|
||||||
const logger = log4js.getLogger("App");
|
const logger = log4js.getLogger("App");
|
||||||
logger.level = config.logLevel;
|
logger.level = config.logLevel;
|
||||||
|
|
||||||
const isHeadless = process.argv.includes("--headless");
|
const cli = createCliLogger(logger);
|
||||||
const isInfo = process.argv.includes("--info");
|
|
||||||
|
|
||||||
const printerHostInfo = (host) => {
|
|
||||||
var ref = host?._reference || "Unknown";
|
|
||||||
|
|
||||||
if (ref != "Unknown") {
|
|
||||||
ref = "HST:" + ref;
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.info("Reference:", ref);
|
|
||||||
logger.info("Name:", host?.name || "Unknown");
|
|
||||||
if (host?.tags?.length > 0) {
|
|
||||||
logger.info("Tags: -", host?.tags?.[0]);
|
|
||||||
for (var i = 0; i < host?.tags?.length - 1; i++) {
|
|
||||||
const tag = host?.tags[i + 1];
|
|
||||||
logger.info(" -", tag);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
logger.info("Tags: n/a");
|
|
||||||
}
|
|
||||||
logger.info("State:", host?.state?.type || "Unknown");
|
|
||||||
};
|
|
||||||
|
|
||||||
function getArgValue(flag) {
|
|
||||||
const withEquals = process.argv.find((arg) => arg.startsWith(`${flag}=`));
|
|
||||||
if (withEquals) {
|
|
||||||
return withEquals.slice(flag.length + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
const index = process.argv.indexOf(flag);
|
|
||||||
if (index !== -1 && index + 1 < process.argv.length) {
|
|
||||||
return process.argv[index + 1];
|
|
||||||
}
|
|
||||||
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
const otpCode = getArgValue("--otp") || undefined;
|
|
||||||
|
|
||||||
async function checkRunning() {
|
|
||||||
try {
|
|
||||||
const response = await axios.get(
|
|
||||||
`http://127.0.0.1:${LOCAL_SERVER_PORT}/info`,
|
|
||||||
{ timeout: 1000 },
|
|
||||||
);
|
|
||||||
if (response.data?.running === true) {
|
|
||||||
return response.data;
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function authenticateWithOtp(otpCode) {
|
|
||||||
try {
|
|
||||||
const response = await axios.post(
|
|
||||||
`http://127.0.0.1:${LOCAL_SERVER_PORT}/otpAuth`,
|
|
||||||
{ otp: otpCode },
|
|
||||||
{ timeout: 30000 },
|
|
||||||
);
|
|
||||||
return response.data;
|
|
||||||
} catch (err) {
|
|
||||||
if (err.response?.data) {
|
|
||||||
return err.response.data;
|
|
||||||
}
|
|
||||||
throw new Error("Failed to authenticate with OTP");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
export async function init(options = {}) {
|
export async function init(options = {}) {
|
||||||
const headless = options.headless ?? isHeadless;
|
const headless = options.headless ?? isHeadless;
|
||||||
|
cli.blank();
|
||||||
logger.info("⌛ Checking if Farm Control Server is running...");
|
startWaiting("Checking if Farm Control Server is running...", logger);
|
||||||
|
let runningServer;
|
||||||
const runningServer = await checkRunning();
|
try {
|
||||||
|
runningServer = await checkRunning();
|
||||||
|
} catch (err) {
|
||||||
|
await stopWaiting();
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
|
||||||
if (otpCode != undefined || isInfo == true) {
|
if (otpCode != undefined || isInfo == true) {
|
||||||
logger.info("✔ Farm Control Server is running.");
|
cli.success("Farm Control Server is running.");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isInfo) {
|
if (isInfo) {
|
||||||
const buildNumber =
|
const buildNumber =
|
||||||
runningServer.buildNumber ?? runningServer.build ?? "n/a";
|
runningServer.buildNumber ?? runningServer.build ?? "n/a";
|
||||||
|
cli.blank();
|
||||||
logger.info("-----FarmControl Server-----");
|
cli.sectionHeader("FarmControl Server");
|
||||||
logger.info(
|
cli.label(
|
||||||
"Version:",
|
"Version:",
|
||||||
runningServer.version ? `v${runningServer.version}` : "n/a",
|
runningServer.version ? `v${runningServer.version}` : "n/a",
|
||||||
);
|
);
|
||||||
logger.info("Build:", buildNumber === "dev" ? "dev" : `b${buildNumber}`);
|
cli.label("Build:", buildNumber === "dev" ? "dev" : `b${buildNumber}`);
|
||||||
logger.info(
|
cli.label("Authenticated:", cli.yesNo(runningServer.authenticated == true));
|
||||||
"Authenticated:",
|
cli.sectionFooter();
|
||||||
runningServer.authenticated == true ? "Yes" : "No",
|
cli.blank();
|
||||||
);
|
if (runningServer.authenticated == false) {
|
||||||
logger.info("------------Host------------");
|
cli.sectionHeader("Authenticate");
|
||||||
printerHostInfo(runningServer.host);
|
printAuthCommand(cli);
|
||||||
logger.info("----------------------------");
|
cli.sectionFooter();
|
||||||
|
} else {
|
||||||
|
cli.sectionHeader("Host");
|
||||||
|
printerHostInfo(runningServer.host, cli);
|
||||||
|
cli.sectionFooter();
|
||||||
|
}
|
||||||
|
cli.blank();
|
||||||
return runningServer;
|
return runningServer;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (otpCode != undefined) {
|
if (otpCode != undefined) {
|
||||||
logger.info("🔒 Authenticating with OTP...");
|
cli.blank();
|
||||||
const authenticatedServer = await authenticateWithOtp(otpCode);
|
startWaiting("Authenticating with OTP...", logger);
|
||||||
|
let authenticatedServer;
|
||||||
|
try {
|
||||||
|
authenticatedServer = await authenticateWithOtp(otpCode);
|
||||||
|
await stopWaiting();
|
||||||
|
} catch (err) {
|
||||||
|
await stopWaiting();
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
if (authenticatedServer.valid == false) {
|
if (authenticatedServer.valid == false) {
|
||||||
logger.error(
|
cli.failure("Failed to authenticate!");
|
||||||
"✘ Failed to authenticate with OTP -",
|
cli.failure(authenticatedServer.error);
|
||||||
authenticatedServer.error,
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
logger.info("✔ Authenticated with OTP.");
|
cli.success("Authenticated with OTP.");
|
||||||
logger.info("------------Host------------");
|
cli.blank();
|
||||||
printerHostInfo(authenticatedServer.host);
|
cli.sectionHeader("Host");
|
||||||
logger.info("----------------------------");
|
printerHostInfo(authenticatedServer.host, cli);
|
||||||
|
cli.sectionFooter();
|
||||||
|
cli.blank();
|
||||||
return authenticatedServer;
|
return authenticatedServer;
|
||||||
}
|
}
|
||||||
|
cli.blank();
|
||||||
return authenticatedServer;
|
return authenticatedServer;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (runningServer != false) {
|
if (runningServer != false) {
|
||||||
logger.warn("✘ Farm Control Server is already running.");
|
cli.warn("✘ Farm Control Server is already running.");
|
||||||
|
cli.blank();
|
||||||
return runningServer;
|
return runningServer;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!headless) {
|
if (!headless) {
|
||||||
// Create Electron window first
|
startWaiting("Creating electron window...", logger);
|
||||||
logger.info("Creating electron window...");
|
try {
|
||||||
await createElectronWindow().catch((err) => {
|
await createElectronWindow().catch((err) => {
|
||||||
logger.warn("Failed to create Electron window:", err);
|
cli.warn("Failed to create Electron window:", err);
|
||||||
});
|
});
|
||||||
|
await stopWaiting();
|
||||||
|
} catch (err) {
|
||||||
|
await stopWaiting();
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
|
||||||
// Setup IPC communication after window is created
|
|
||||||
setupIPC().catch((err) => {
|
setupIPC().catch((err) => {
|
||||||
logger.warn("Failed to setup IPC:", err);
|
cli.warn("Failed to setup IPC:", err);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
logger.info("Running in headless mode. Skipping window creation.");
|
cli.info("Running in headless mode. Skipping window creation.");
|
||||||
}
|
}
|
||||||
|
|
||||||
const socketClient = new SocketClient();
|
const socketClient = new SocketClient();
|
||||||
// Make socket client globally accessible for IPC handlers
|
|
||||||
global.socketClient = socketClient;
|
global.socketClient = socketClient;
|
||||||
|
|
||||||
const localServer = new LocalServer(socketClient);
|
const localServer = new LocalServer(socketClient);
|
||||||
@ -165,10 +132,12 @@ export async function init(options = {}) {
|
|||||||
|
|
||||||
socketClient.connect();
|
socketClient.connect();
|
||||||
|
|
||||||
process.on("SIGINT", () => {
|
process.on("SIGINT", async () => {
|
||||||
logger.info("Shutting down...");
|
startWaiting("Shutting down...", logger);
|
||||||
|
cli.blank();
|
||||||
socketClient.disconnect();
|
socketClient.disconnect();
|
||||||
localServer.stop();
|
localServer.stop();
|
||||||
|
await stopWaiting();
|
||||||
process.exit(0);
|
process.exit(0);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -180,6 +149,7 @@ const isMainModule =
|
|||||||
if (isMainModule && !process.env.FC_HEADLESS_ENTRY) {
|
if (isMainModule && !process.env.FC_HEADLESS_ENTRY) {
|
||||||
init().catch((err) => {
|
init().catch((err) => {
|
||||||
logger.error(err.message);
|
logger.error(err.message);
|
||||||
|
cli.blank();
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,6 +3,7 @@ import log4js from "log4js";
|
|||||||
import { notPrompting } from "../utils.js";
|
import { notPrompting } from "../utils.js";
|
||||||
import { loadConfig } from "../config.js";
|
import { loadConfig } from "../config.js";
|
||||||
import { getServerVersionInfo } from "../serverVersion.js";
|
import { getServerVersionInfo } from "../serverVersion.js";
|
||||||
|
import { startWaiting, stopWaiting } from "../spinner.js";
|
||||||
|
|
||||||
export const LOCAL_SERVER_PORT = 47291;
|
export const LOCAL_SERVER_PORT = 47291;
|
||||||
|
|
||||||
@ -41,13 +42,22 @@ export class LocalServer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
logger.info("Authenticating with OTP via local server...");
|
startWaiting("Authenticating with OTP via local server...", logger);
|
||||||
notPrompting();
|
let result;
|
||||||
const result = await this.socketClient.authenticateWithOtp(otp, {
|
try {
|
||||||
retryOnFailure: false,
|
notPrompting();
|
||||||
});
|
result = await this.socketClient.authenticateWithOtp(otp, {
|
||||||
|
retryOnFailure: false,
|
||||||
|
logs: false,
|
||||||
|
});
|
||||||
|
await stopWaiting();
|
||||||
|
} catch (err) {
|
||||||
|
await stopWaiting();
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
|
||||||
if (!result.valid) {
|
if (!result.valid) {
|
||||||
|
logger.error("OTP authentication failed:", result.error);
|
||||||
res.status(401).json(result);
|
res.status(401).json(result);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,7 +3,8 @@ import log4js from "log4js";
|
|||||||
import NodeCache from "node-cache";
|
import NodeCache from "node-cache";
|
||||||
import { loadConfig } from "../config.js";
|
import { loadConfig } from "../config.js";
|
||||||
import { sendIPC } from "../electron/ipc.js";
|
import { sendIPC } from "../electron/ipc.js";
|
||||||
|
import chalk from "chalk";
|
||||||
|
import { formatState } from "../commandlineutils.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;
|
||||||
@ -15,7 +16,6 @@ export class PrinterDatabase {
|
|||||||
this.id = this.printer._id;
|
this.id = 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 });
|
||||||
logger.info("Initialized PrinterDatabase with socket manager");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async getPrinter() {
|
async getPrinter() {
|
||||||
@ -146,12 +146,9 @@ export class PrinterDatabase {
|
|||||||
connectedAt,
|
connectedAt,
|
||||||
});
|
});
|
||||||
|
|
||||||
logger.info(`Updated printer ${this.printer.name} state:`, {
|
logger.info(
|
||||||
type: state.type,
|
`Updated printer: ${chalk.bold.white(this.printer.name)}, State: ${chalk.bold.white(formatState(this.printer.state.type))}${state?.progress ? ` (${state.progress})` : ""} (${online ? chalk.green("Online") : chalk.dim.white("Offline")})`,
|
||||||
progress: state.progress,
|
);
|
||||||
online,
|
|
||||||
previousState: updatedPrinter.state,
|
|
||||||
});
|
|
||||||
|
|
||||||
return updatedPrinter;
|
return updatedPrinter;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
@ -5,6 +5,7 @@ import { loadConfig } from "../config.js";
|
|||||||
import { PrinterDatabase } from "./database.js";
|
import { PrinterDatabase } from "./database.js";
|
||||||
import { PrinterFileManager } from "./printerfilemanager.js";
|
import { PrinterFileManager } from "./printerfilemanager.js";
|
||||||
import log4js from "log4js";
|
import log4js from "log4js";
|
||||||
|
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";
|
||||||
@ -151,7 +152,6 @@ export class PrinterClient {
|
|||||||
|
|
||||||
async connect() {
|
async connect() {
|
||||||
if (this.active == false) {
|
if (this.active == false) {
|
||||||
logger.info(`Printer ${this.id} is not active, skipping connection`);
|
|
||||||
this.shouldReconnect = false;
|
this.shouldReconnect = false;
|
||||||
this.isOnline = false;
|
this.isOnline = false;
|
||||||
this.state = { type: "inactive" };
|
this.state = { type: "inactive" };
|
||||||
@ -238,96 +238,108 @@ export class PrinterClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async initialize() {
|
async initialize() {
|
||||||
logger.info("Running printer initialization...");
|
startWaiting("Running printer initialization...", logger);
|
||||||
this.state = { type: "initializing", message: null };
|
try {
|
||||||
this.isOnline = true;
|
this.state = { type: "initializing", message: null };
|
||||||
this.connectedAt = new Date();
|
this.isOnline = true;
|
||||||
await this.getInfo();
|
this.connectedAt = new Date();
|
||||||
await this.getFiles();
|
await this.getInfo();
|
||||||
await this.getCurrentFilament();
|
await this.getFiles();
|
||||||
await this.loadCurrentFilament();
|
await this.getCurrentFilament();
|
||||||
await this.updateSubscriptions();
|
await this.loadCurrentFilament();
|
||||||
await this.getPrinterState();
|
await this.updateSubscriptions();
|
||||||
await this.syncSubJobs();
|
await this.getPrinterState();
|
||||||
this.eventUpdateInterval = setInterval(
|
await this.syncSubJobs();
|
||||||
this.handleEventUpdate.bind(this),
|
this.eventUpdateInterval = setInterval(
|
||||||
500,
|
this.handleEventUpdate.bind(this),
|
||||||
);
|
500,
|
||||||
|
);
|
||||||
|
await stopWaiting();
|
||||||
|
} catch (error) {
|
||||||
|
await stopWaiting();
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async getInfo() {
|
async getInfo() {
|
||||||
logger.info("Getting printer info...");
|
startWaiting("Getting printer info...", logger);
|
||||||
try {
|
try {
|
||||||
// Get server info
|
|
||||||
const serverResult = await this.jsonRpc.callMethod("server.info");
|
|
||||||
this.isOnline = true;
|
|
||||||
this.klippyState = { type: serverResult.klippy_state };
|
|
||||||
logger.info(
|
|
||||||
"Server:",
|
|
||||||
`Moonraker ${serverResult.moonraker_version} (${this.printer.name})`,
|
|
||||||
`State: ${this.klippyState.type}`,
|
|
||||||
);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const klippyResult = await this.jsonRpc.callMethod("printer.info");
|
// Get server info
|
||||||
|
const serverResult = await this.jsonRpc.callMethod("server.info");
|
||||||
|
this.isOnline = true;
|
||||||
|
this.klippyState = { type: serverResult.klippy_state };
|
||||||
logger.info(
|
logger.info(
|
||||||
`Klippy info for ${this.printer.name}: ${klippyResult.hostname}, ${klippyResult.software_version}`,
|
"Server:",
|
||||||
);
|
`Moonraker ${serverResult.moonraker_version} (${this.printer.name})`,
|
||||||
// Update firmware version in database
|
`State: ${this.klippyState.type}`,
|
||||||
await this.database.updatePrinterFirmware(
|
|
||||||
klippyResult.software_version,
|
|
||||||
);
|
|
||||||
logger.info(
|
|
||||||
`Updated firmware version for ${this.printer.name} to ${klippyResult.software_version}`,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
if (klippyResult.state === "error" && klippyResult.state_message) {
|
try {
|
||||||
logger.error(
|
const klippyResult = await this.jsonRpc.callMethod("printer.info");
|
||||||
`Klippy error for ${this.printer.name}: ${klippyResult.state_message}`,
|
logger.info(
|
||||||
this.database.addAlert({
|
`Klippy info for ${this.printer.name}: ${klippyResult.hostname}, ${klippyResult.software_version}`,
|
||||||
code: "klippyError",
|
|
||||||
type: "error",
|
|
||||||
message: klippyResult.state_message,
|
|
||||||
actions: ["restart", "restartFirmware"],
|
|
||||||
}),
|
|
||||||
);
|
);
|
||||||
}
|
// Update firmware version in database
|
||||||
|
await this.database.updatePrinterFirmware(
|
||||||
if (klippyResult.state === "shutdown" && klippyResult.state_message) {
|
klippyResult.software_version,
|
||||||
logger.error(
|
);
|
||||||
`Klippy error for ${this.printer.name}: ${klippyResult.state_message}`,
|
logger.info(
|
||||||
this.database.addAlert({
|
`Updated firmware version for ${this.printer.name} to ${klippyResult.software_version}`,
|
||||||
code: "klippyError",
|
|
||||||
type: "error",
|
|
||||||
message: klippyResult.state_message,
|
|
||||||
actions: ["restart", "restartFirmware"],
|
|
||||||
}),
|
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
if (klippyResult.state === "startup" && klippyResult.state_message) {
|
if (klippyResult.state === "error" && klippyResult.state_message) {
|
||||||
logger.warn(
|
logger.error(
|
||||||
`Klippy startup message for ${this.printer.name}: ${klippyResult.state_message}`,
|
`Klippy error for ${this.printer.name}: ${klippyResult.state_message}`,
|
||||||
this.database.addAlert({
|
this.database.addAlert({
|
||||||
type: "info",
|
code: "klippyError",
|
||||||
code: "klippyStartup",
|
type: "error",
|
||||||
message: klippyResult.state_message,
|
message: klippyResult.state_message,
|
||||||
priority: 8,
|
actions: ["restart", "restartFirmware"],
|
||||||
timestamp: new Date(),
|
}),
|
||||||
}),
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (klippyResult.state === "shutdown" && klippyResult.state_message) {
|
||||||
|
logger.error(
|
||||||
|
`Klippy error for ${this.printer.name}: ${klippyResult.state_message}`,
|
||||||
|
this.database.addAlert({
|
||||||
|
code: "klippyError",
|
||||||
|
type: "error",
|
||||||
|
message: klippyResult.state_message,
|
||||||
|
actions: ["restart", "restartFirmware"],
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (klippyResult.state === "startup" && klippyResult.state_message) {
|
||||||
|
logger.warn(
|
||||||
|
`Klippy startup message for ${this.printer.name}: ${klippyResult.state_message}`,
|
||||||
|
this.database.addAlert({
|
||||||
|
type: "info",
|
||||||
|
code: "klippyStartup",
|
||||||
|
message: klippyResult.state_message,
|
||||||
|
priority: 8,
|
||||||
|
timestamp: new Date(),
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
logger.error(
|
||||||
|
`Error getting Klippy info for ${this.printer.name}:`,
|
||||||
|
error,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error(
|
logger.error(
|
||||||
`Error getting Klippy info for ${this.printer.name}:`,
|
`Error getting server info for ${this.printer.name}:`,
|
||||||
error,
|
error,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
await stopWaiting();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error(
|
await stopWaiting();
|
||||||
`Error getting server info for ${this.printer.name}:`,
|
throw error;
|
||||||
error,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -3,6 +3,7 @@ import { PrinterClient } from "./printerclient.js";
|
|||||||
import { loadConfig } from "../config.js";
|
import { loadConfig } from "../config.js";
|
||||||
import log4js from "log4js";
|
import log4js from "log4js";
|
||||||
import { sendIPC } from "../electron/ipc.js";
|
import { sendIPC } from "../electron/ipc.js";
|
||||||
|
import { startWaiting, stopWaiting } from "../spinner.js";
|
||||||
// Load configuration
|
// Load configuration
|
||||||
const config = loadConfig();
|
const config = loadConfig();
|
||||||
|
|
||||||
@ -17,7 +18,7 @@ export class PrinterManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async reloadPrinters() {
|
async reloadPrinters() {
|
||||||
logger.info("Reloading printers...");
|
startWaiting("Reloading printers...", logger);
|
||||||
try {
|
try {
|
||||||
this.printers = await this.socketClient.listObjects({
|
this.printers = await this.socketClient.listObjects({
|
||||||
objectType: "printer",
|
objectType: "printer",
|
||||||
@ -64,7 +65,9 @@ export class PrinterManager {
|
|||||||
|
|
||||||
logger.debug("Printers added:", addedPrintersCount);
|
logger.debug("Printers added:", addedPrintersCount);
|
||||||
logger.debug("Printers removed:", removedPrintersCount);
|
logger.debug("Printers removed:", removedPrintersCount);
|
||||||
|
await stopWaiting();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
await stopWaiting();
|
||||||
logger.error("Failed to update printers:", error);
|
logger.error("Failed to update printers:", error);
|
||||||
this.printers = [];
|
this.printers = [];
|
||||||
}
|
}
|
||||||
@ -155,39 +158,44 @@ export class PrinterManager {
|
|||||||
|
|
||||||
// Close all printer connections
|
// Close all printer connections
|
||||||
async closeAllConnections() {
|
async closeAllConnections() {
|
||||||
logger.info(
|
startWaiting(
|
||||||
`Closing all printer connections... current count: ${this.printerClients.size}`,
|
`Closing all printer connections... current count: ${this.printerClients.size}`,
|
||||||
|
logger,
|
||||||
);
|
);
|
||||||
|
try {
|
||||||
|
const clients = Array.from(this.printerClients.values());
|
||||||
|
|
||||||
// Take a snapshot so any mutations during disconnects don't affect iteration
|
for (const printerClient of clients) {
|
||||||
const clients = Array.from(this.printerClients.values());
|
try {
|
||||||
|
// Ensure we never auto-reconnect after a manual close-all
|
||||||
for (const printerClient of clients) {
|
printerClient.shouldReconnect = false;
|
||||||
try {
|
await printerClient.disconnect();
|
||||||
// Ensure we never auto-reconnect after a manual close-all
|
logger.info(
|
||||||
printerClient.shouldReconnect = false;
|
`Disconnected printer client ${printerClient?.id || "unknown"}`,
|
||||||
await printerClient.disconnect();
|
);
|
||||||
logger.info(
|
} catch (error) {
|
||||||
`Disconnected printer client ${printerClient?.id || "unknown"}`,
|
logger.error(
|
||||||
);
|
`Failed to disconnect printer client ${
|
||||||
} catch (error) {
|
printerClient?.id || "unknown"
|
||||||
logger.error(
|
}:`,
|
||||||
`Failed to disconnect printer client ${
|
error,
|
||||||
printerClient?.id || "unknown"
|
);
|
||||||
}:`,
|
}
|
||||||
error,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.log("Printer clients:", this.printerClients);
|
||||||
|
|
||||||
|
// Clear local references so no stale clients remain
|
||||||
|
this.printerClients.clear();
|
||||||
|
this.printers = [];
|
||||||
|
|
||||||
|
logger.info(
|
||||||
|
`All printer connections closed. Remaining clients: ${this.printerClients.size}`,
|
||||||
|
);
|
||||||
|
await stopWaiting();
|
||||||
|
} catch (error) {
|
||||||
|
await stopWaiting();
|
||||||
|
throw error;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log("Printer clients:", this.printerClients);
|
|
||||||
|
|
||||||
// Clear local references so no stale clients remain
|
|
||||||
this.printerClients.clear();
|
|
||||||
this.printers = [];
|
|
||||||
|
|
||||||
logger.info(
|
|
||||||
`All printer connections closed. Remaining clients: ${this.printerClients.size}`,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -9,6 +9,7 @@ import { PrinterManager } from "../printer/printermanager.js";
|
|||||||
import { HostManager } from "../host/hostmanager.js";
|
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";
|
||||||
|
|
||||||
const config = loadConfig();
|
const config = loadConfig();
|
||||||
|
|
||||||
@ -51,9 +52,9 @@ export class SocketClient {
|
|||||||
this.reconnectTimeout = null;
|
this.reconnectTimeout = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.info("Will attempt to reconnect in 3 seconds...");
|
startWaiting("Will attempt to reconnect in 3 seconds...", logger);
|
||||||
this.reconnectTimeout = setTimeout(() => {
|
this.reconnectTimeout = setTimeout(async () => {
|
||||||
logger.info("Attempting to reconnect...");
|
await stopWaiting();
|
||||||
this.connect();
|
this.connect();
|
||||||
}, 3000);
|
}, 3000);
|
||||||
}
|
}
|
||||||
@ -94,9 +95,10 @@ export class SocketClient {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
connect() {
|
async connect() {
|
||||||
try {
|
try {
|
||||||
logger.info(`Connecting to Socket.IO server: ${config.url}`);
|
await stopWaiting();
|
||||||
|
startWaiting(`Connecting to Socket.IO server: ${config.url}`, logger);
|
||||||
this.socket = io(config.url, {
|
this.socket = io(config.url, {
|
||||||
auth: { type: "host" },
|
auth: { type: "host" },
|
||||||
reconnection: false,
|
reconnection: false,
|
||||||
@ -110,6 +112,7 @@ export class SocketClient {
|
|||||||
sendIPC("setAuthenticated", false);
|
sendIPC("setAuthenticated", false);
|
||||||
this.setupSocketEventHandlers();
|
this.setupSocketEventHandlers();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
await stopWaiting();
|
||||||
logger.error("Failed to create Socket.IO connection:", error);
|
logger.error("Failed to create Socket.IO connection:", error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -150,18 +153,18 @@ export class SocketClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async authenticateWithOtp(otp = undefined, options = {}) {
|
async authenticateWithOtp(otp = undefined, options = {}) {
|
||||||
const { retryOnFailure = otp == undefined } = options;
|
const { retryOnFailure = otp == undefined, logs = true } = options;
|
||||||
|
|
||||||
if (otp == undefined) {
|
if (otp == undefined) {
|
||||||
otp = await askOtp();
|
otp = await askOtp();
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.waitForConnection();
|
await this.waitForConnection();
|
||||||
return this.authenticate({ otp }, { retryOnFailure });
|
return this.authenticate({ otp }, { retryOnFailure, logs });
|
||||||
}
|
}
|
||||||
|
|
||||||
async authenticate(authenticationData, options = {}) {
|
async authenticate(authenticationData, options = {}) {
|
||||||
const { retryOnFailure = true } = options;
|
const { retryOnFailure = true, logs = true } = options;
|
||||||
|
|
||||||
await this.waitForConnection();
|
await this.waitForConnection();
|
||||||
|
|
||||||
@ -171,7 +174,9 @@ export class SocketClient {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.debug("Host authenticating...");
|
if (logs) {
|
||||||
|
logger.debug("Host authenticating...");
|
||||||
|
}
|
||||||
this.socket.emit(
|
this.socket.emit(
|
||||||
"authenticate",
|
"authenticate",
|
||||||
authenticationData,
|
authenticationData,
|
||||||
@ -179,7 +184,9 @@ export class SocketClient {
|
|||||||
if (verifyResult.valid == false) {
|
if (verifyResult.valid == false) {
|
||||||
this.authenticated = false;
|
this.authenticated = false;
|
||||||
sendIPC("setAuthenticated", false);
|
sendIPC("setAuthenticated", false);
|
||||||
logger.error("Host not authenticated:", verifyResult.error);
|
if (logs) {
|
||||||
|
logger.error("Host not authenticated:", verifyResult.error);
|
||||||
|
}
|
||||||
|
|
||||||
if (retryOnFailure) {
|
if (retryOnFailure) {
|
||||||
const retryResult = await this.authenticateWithOtp();
|
const retryResult = await this.authenticateWithOtp();
|
||||||
@ -191,7 +198,9 @@ export class SocketClient {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.info("Host authenticated.");
|
if (logs) {
|
||||||
|
logger.info("Host authenticated.");
|
||||||
|
}
|
||||||
this.authenticated = true;
|
this.authenticated = true;
|
||||||
sendIPC("setAuthenticated", true);
|
sendIPC("setAuthenticated", true);
|
||||||
sendIPC("setLoading", false);
|
sendIPC("setLoading", false);
|
||||||
@ -212,7 +221,9 @@ export class SocketClient {
|
|||||||
this.id = this.host._id;
|
this.id = this.host._id;
|
||||||
|
|
||||||
config.host = { id: this.id, authCode: this.host.authCode };
|
config.host = { id: this.id, authCode: this.host.authCode };
|
||||||
saveConfig(config);
|
if (logs) {
|
||||||
|
await saveConfig(config);
|
||||||
|
}
|
||||||
this.sendDeviceInfo();
|
this.sendDeviceInfo();
|
||||||
this.subscribeToObjectEvents();
|
this.subscribeToObjectEvents();
|
||||||
this.subscribeToObjectUpdates({ objectType: "host", _id: this.id });
|
this.subscribeToObjectUpdates({ objectType: "host", _id: this.id });
|
||||||
@ -226,7 +237,6 @@ export class SocketClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
handleHostAction(action, callback) {
|
handleHostAction(action, callback) {
|
||||||
console.log("RUNNING HOST ACTION");
|
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
case "reloadPrinters":
|
case "reloadPrinters":
|
||||||
this.printerManager.updatePrinters().catch((error) => {
|
this.printerManager.updatePrinters().catch((error) => {
|
||||||
@ -237,7 +247,7 @@ export class SocketClient {
|
|||||||
callback({ success: true });
|
callback({ success: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
handleHostUpdate(data) {
|
async handleHostUpdate(data) {
|
||||||
if (data._id != this.id) {
|
if (data._id != this.id) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -249,7 +259,7 @@ export class SocketClient {
|
|||||||
|
|
||||||
if (data.object?.authCode) {
|
if (data.object?.authCode) {
|
||||||
config.host = { id: this.id, authCode: data.object.authCode };
|
config.host = { id: this.id, authCode: data.object.authCode };
|
||||||
saveConfig(config);
|
await saveConfig(config);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -298,7 +308,8 @@ export class SocketClient {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handleConnect() {
|
async handleConnect() {
|
||||||
|
await stopWaiting();
|
||||||
logger.info("Connected to FarmControl Api.");
|
logger.info("Connected to FarmControl Api.");
|
||||||
this.connected = true;
|
this.connected = true;
|
||||||
sendIPC("setConnected", true);
|
sendIPC("setConnected", true);
|
||||||
@ -326,7 +337,8 @@ export class SocketClient {
|
|||||||
this.authenticate({ id: config.host.id, authCode: config.host.authCode });
|
this.authenticate({ id: config.host.id, authCode: config.host.authCode });
|
||||||
}
|
}
|
||||||
|
|
||||||
handleError(error) {
|
async handleError(error) {
|
||||||
|
await stopWaiting();
|
||||||
logger.error("Connection error:", error.message);
|
logger.error("Connection error:", error.message);
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
sendIPC("setLoading", false);
|
sendIPC("setLoading", false);
|
||||||
|
|||||||
122
src/spinner.js
Normal file
122
src/spinner.js
Normal file
@ -0,0 +1,122 @@
|
|||||||
|
import chalk from "chalk";
|
||||||
|
import cliSpinners from "cli-spinners";
|
||||||
|
import log4js from "log4js";
|
||||||
|
import { writeSync } from "node:fs";
|
||||||
|
import { colouredLayout } from "log4js/lib/layouts.js";
|
||||||
|
|
||||||
|
let spinner = null;
|
||||||
|
let consoleLogPatched = false;
|
||||||
|
|
||||||
|
function patchConsoleLog() {
|
||||||
|
if (consoleLogPatched) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const originalLog = console.log.bind(console);
|
||||||
|
console.log = (...args) => {
|
||||||
|
spinner?.stopSync();
|
||||||
|
originalLog(...args);
|
||||||
|
};
|
||||||
|
consoleLogPatched = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatLogPrefix(logger) {
|
||||||
|
const categoryName = logger?.category ?? "App";
|
||||||
|
return colouredLayout({
|
||||||
|
startTime: new Date(),
|
||||||
|
level: log4js.levels.INFO,
|
||||||
|
categoryName,
|
||||||
|
data: [],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function clearSpinnerLineSync() {
|
||||||
|
if (!process.stderr.isTTY) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
writeSync(process.stderr.fd, "\r\x1b[2K");
|
||||||
|
} catch {
|
||||||
|
process.stderr.write("\r\x1b[2K");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createSpinner() {
|
||||||
|
const { frames, interval } = cliSpinners.dots;
|
||||||
|
let timer = null;
|
||||||
|
let frameIndex = 0;
|
||||||
|
let message = "";
|
||||||
|
let active = false;
|
||||||
|
let activeLogger = null;
|
||||||
|
|
||||||
|
function render() {
|
||||||
|
if (!active) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const frame = frames[frameIndex];
|
||||||
|
frameIndex = (frameIndex + 1) % frames.length;
|
||||||
|
const prefix = formatLogPrefix(activeLogger);
|
||||||
|
const line = `${prefix}${frame} ${chalk.bold.cyan(message)}`;
|
||||||
|
process.stderr.write(`\r\x1b[2K${line}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
function stopSync() {
|
||||||
|
if (timer) {
|
||||||
|
clearInterval(timer);
|
||||||
|
timer = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const wasActive = active;
|
||||||
|
active = false;
|
||||||
|
activeLogger = null;
|
||||||
|
|
||||||
|
if (wasActive) {
|
||||||
|
clearSpinnerLineSync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function stop() {
|
||||||
|
await new Promise((resolve) => setTimeout(resolve, 15));
|
||||||
|
stopSync();
|
||||||
|
}
|
||||||
|
|
||||||
|
function start(text, logger, timeout = true) {
|
||||||
|
if (timeout == true) {
|
||||||
|
setTimeout(() => {
|
||||||
|
start(text, logger, false);
|
||||||
|
}, 10);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
stopSync();
|
||||||
|
message = text;
|
||||||
|
frameIndex = 0;
|
||||||
|
activeLogger = logger;
|
||||||
|
|
||||||
|
if (!process.stderr.isTTY) {
|
||||||
|
const prefix = formatLogPrefix(activeLogger);
|
||||||
|
process.stderr.write(
|
||||||
|
`${prefix}${frames[0]} ${chalk.bold.cyan(message)}\n`,
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
active = true;
|
||||||
|
render();
|
||||||
|
timer = setInterval(render, interval);
|
||||||
|
}
|
||||||
|
|
||||||
|
return { start, stop, stopSync };
|
||||||
|
}
|
||||||
|
|
||||||
|
export function registerSpinner(instance) {
|
||||||
|
spinner = instance;
|
||||||
|
patchConsoleLog();
|
||||||
|
}
|
||||||
|
|
||||||
|
export function startWaiting(message, logger) {
|
||||||
|
spinner?.start(message, logger);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function stopWaiting() {
|
||||||
|
await spinner?.stop();
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user