Compare commits

..

No commits in common. "853784e36100977c0f4b1b02f815990d39ed0062" and "3ca730ecee0be3125d71810f4f2c37f09e49cb3a" have entirely different histories.

7 changed files with 89 additions and 332 deletions

View File

@ -1,47 +1,40 @@
import { import {
authenticateWithOtp, authenticateWithOtp,
checkRunning, checkRunning,
getFlagValue, isInfo,
hasFlag, otpCode,
printAuthCommand, printAuthCommand,
printerHostInfo, printerHostInfo,
setLogLevel,
startWaiting, startWaiting,
stopWaiting, stopWaiting,
} from "./commandlineutils.js"; } from "./commandlineutils.js";
import { VALID_LOG_LEVELS } from "./logging.js";
import chalk from "chalk";
async function getRunningServer(cli, logger) { export async function handleCommandLine(cli, logger) {
const isCommandLines = otpCode != undefined || isInfo == true;
let runningServer;
try { try {
startWaiting("Checking if Farm Control Server is running...", logger); startWaiting("Checking if Farm Control Server is running...", logger);
const runningServer = await checkRunning(); runningServer = await checkRunning();
await stopWaiting(); await stopWaiting();
return runningServer; if (runningServer == false && isCommandLines == true) {
cli.failure("Farm Control Server is not running.");
cli.blank();
return { handled: true, result: false };
}
} catch (err) { } catch (err) {
await stopWaiting(); await stopWaiting();
cli.failure("Failed to check if Farm Control Server is running:", err); cli.failure("Failed to check if Farm Control Server is running:", err);
cli.blank(); cli.blank();
return null; return { handled: true, result: false };
} }
}
async function checkServerRunning(cli, logger) { if (isCommandLines == true) {
const runningServer = await getRunningServer(cli, logger);
if (runningServer == false) {
cli.failure("Farm Control Server is not running.");
cli.blank();
return false;
}
if (runningServer == null) {
return false;
}
cli.success("Farm Control Server is running."); cli.success("Farm Control Server is running.");
return runningServer; }
}
function printServerInfo(cli, runningServer) { if (isInfo) {
const buildNumber = runningServer.buildNumber ?? runningServer.build ?? "n/a"; const buildNumber =
runningServer.buildNumber ?? runningServer.build ?? "n/a";
cli.blank(); cli.blank();
cli.sectionHeader("FarmControl Server"); cli.sectionHeader("FarmControl Server");
cli.label( cli.label(
@ -55,8 +48,10 @@ function printServerInfo(cli, runningServer) {
cli.label("Authenticated:", cli.yesNo(runningServer.authenticated == true)); cli.label("Authenticated:", cli.yesNo(runningServer.authenticated == true));
cli.sectionFooter(); cli.sectionFooter();
cli.blank(); cli.blank();
if (
if (runningServer.authenticated == false && runningServer.connected == true) { runningServer.authenticated == false &&
runningServer.connected == true
) {
cli.sectionHeader("Authenticate"); cli.sectionHeader("Authenticate");
printAuthCommand(cli); printAuthCommand(cli);
cli.sectionFooter(); cli.sectionFooter();
@ -68,67 +63,12 @@ function printServerInfo(cli, runningServer) {
printerHostInfo(runningServer.host, cli); printerHostInfo(runningServer.host, cli);
cli.sectionFooter(); cli.sectionFooter();
} }
cli.blank(); cli.blank();
} return { handled: true, result: runningServer };
function printHelp(cli) {
cli.sectionHeader("Help");
cli.label(
chalk.bold.white("Usage: ") +
chalk.bold.cyanBright("farmcontrol-server ") +
chalk.white.dim("[options]"),
);
cli.sectionFooter();
const optionsPadding = " ".repeat(9);
cli.label(
chalk.bold.white("Options: ") +
chalk.bold.magentaBright("--info") +
" Outputs server information",
);
cli.label(
optionsPadding +
chalk.bold.magentaBright("--logLevel") +
" " +
chalk.dim("<level>") +
" Sets log level (" +
chalk.bold.cyan("trace") +
", " +
chalk.bold.blue("debug") +
", " +
chalk.bold.green("info") +
", " +
chalk.bold.yellow("warn") +
", " +
chalk.bold.red("error") +
")",
);
cli.label(
optionsPadding +
chalk.bold.magentaBright("--otp") +
" " +
chalk.dim("<otp>") +
" Authenticates with OTP",
);
cli.sectionFooter();
cli.blank();
}
async function runOtpCommand(cli, logger, otpCode) {
cli.blank();
if (otpCode == undefined) {
cli.failure("OTP code is required.");
cli.blank();
return { handled: true, result: false };
} }
if (!/^\d{6}$/.test(String(otpCode))) { if (otpCode != undefined) {
cli.failure("OTP code must be a 6-digit number.");
cli.blank(); cli.blank();
return { handled: true, result: false };
}
startWaiting("Authenticating with OTP...", logger); startWaiting("Authenticating with OTP...", logger);
let authenticatedServer; let authenticatedServer;
try { try {
@ -138,122 +78,21 @@ async function runOtpCommand(cli, logger, otpCode) {
await stopWaiting(); await stopWaiting();
throw err; throw err;
} }
if (authenticatedServer.valid == false) { if (authenticatedServer.valid == false) {
cli.failure("Failed to authenticate!"); cli.failure("Failed to authenticate!");
cli.failure(authenticatedServer.error); cli.failure(authenticatedServer.error);
cli.blank(); } else {
return authenticatedServer;
}
cli.success("Authenticated with OTP."); cli.success("Authenticated with OTP.");
cli.blank(); cli.blank();
cli.sectionHeader("Host"); cli.sectionHeader("Host");
printerHostInfo(authenticatedServer.host, cli); printerHostInfo(authenticatedServer.host, cli);
cli.sectionFooter(); cli.sectionFooter();
cli.blank(); cli.blank();
return authenticatedServer; return { handled: true, result: authenticatedServer };
} }
async function runLogLevelCommand(cli, logger, level) {
cli.blank(); cli.blank();
return { handled: true, result: authenticatedServer };
if (level == undefined) {
cli.failure("Log level is required.");
cli.blank();
return { handled: true, result: false };
} }
const normalized = String(level).toLowerCase();
if (!VALID_LOG_LEVELS.includes(normalized)) {
cli.failure(
`Invalid log level. Must be one of: ${VALID_LOG_LEVELS.join(", ")}`,
);
cli.blank();
return { handled: true, result: false };
}
startWaiting("Setting log level...", logger);
let result;
try {
result = await setLogLevel(normalized);
await stopWaiting();
} catch (err) {
await stopWaiting();
throw err;
}
if (result.valid == false) {
cli.failure("Failed to set log level!");
cli.failure(result.error);
cli.blank();
return result;
}
cli.success(`Log level set to ${result.logLevel}.`);
cli.blank();
return result;
}
const commands = [
{
matches: () => hasFlag("info"),
run: async ({ cli, logger }) => {
const runningServer = await checkServerRunning(cli, logger);
if (runningServer == false) {
return { handled: true, result: false };
}
printServerInfo(cli, runningServer);
return { handled: true, result: true };
},
},
{
matches: () => hasFlag("help") || hasFlag("h"),
run: async ({ cli }) => {
printHelp(cli);
return true;
},
},
{
matches: () =>
getFlagValue("logLevel") != undefined || hasFlag("logLevel"),
run: async ({ cli, logger }) => {
const runningServer = await checkServerRunning(cli, logger);
if (runningServer == false) {
return { handled: true, result: false };
}
const result = await runLogLevelCommand(
cli,
logger,
getFlagValue("logLevel"),
);
return { handled: true, result };
},
},
{
matches: () => getFlagValue("otp") != undefined || hasFlag("otp"),
run: async ({ cli, logger }) => {
const runningServer = await checkServerRunning(cli, logger);
if (runningServer == false) {
return { handled: true, result: false };
}
const result = await runOtpCommand(cli, logger, getFlagValue("otp"));
return { handled: true, result };
},
},
];
export async function handleCommandLine(cli, logger) {
const command = commands.find((entry) => entry.matches());
if (!command) {
const runningServer = await getRunningServer(cli, logger);
if (runningServer == null) {
return { handled: true, result: false };
}
return { handled: false, runningServer }; return { handled: false, runningServer };
}
const result = await command.run({ cli, logger });
return { handled: true, result };
} }

View File

@ -102,11 +102,7 @@ export function createCliLogger(logger) {
}), }),
label: withSpinnerStopped((key, value) => { label: withSpinnerStopped((key, value) => {
if (value == undefined) {
logger.info(key);
} else {
logger.info(chalk.bold(key), value); logger.info(chalk.bold(key), value);
}
}), }),
yesNo(value) { yesNo(value) {
@ -123,32 +119,23 @@ export function createCliLogger(logger) {
return cli; return cli;
} }
export function hasFlag(name) { export function getArgValue(flag) {
return ( const withEquals = process.argv.find((arg) => arg.startsWith(`${flag}=`));
process.argv.includes(`--${name}`) || process.argv.includes(`-${name}`)
);
}
export function getFlagValue(name) {
for (const prefix of [`--${name}`, `-${name}`]) {
const withEquals = process.argv.find((arg) => arg.startsWith(`${prefix}=`));
if (withEquals) { if (withEquals) {
return withEquals.slice(prefix.length + 1); return withEquals.slice(flag.length + 1);
} }
const index = process.argv.indexOf(prefix); const index = process.argv.indexOf(flag);
if (index !== -1 && index + 1 < process.argv.length) { if (index !== -1 && index + 1 < process.argv.length) {
const next = process.argv[index + 1]; return process.argv[index + 1];
if (!next.startsWith("-")) {
return next;
}
}
} }
return undefined; return undefined;
} }
export const isHeadless = process.argv.includes("--headless"); 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) { export function printerHostInfo(host, cli) {
let ref = host?._reference || "Unknown"; let ref = host?._reference || "Unknown";
@ -176,7 +163,7 @@ export function printerHostInfo(host, cli) {
export function printAuthCommand(cli) { export function printAuthCommand(cli) {
cli.info( cli.info(
"Run '" + "Run '" +
chalk.bold.magentaBright("farmcontrol-server") + chalk.bold.white("farmcontrol-server") +
" " + " " +
chalk.bold.magentaBright("--otp") + chalk.bold.magentaBright("--otp") +
" " + " " +
@ -214,19 +201,3 @@ export async function authenticateWithOtp(code) {
throw new Error("Failed to authenticate with OTP"); throw new Error("Failed to authenticate with OTP");
} }
} }
export async function setLogLevel(level) {
try {
const response = await axios.post(
`http://127.0.0.1:${LOCAL_SERVER_PORT}/logLevel`,
{ logLevel: level },
{ timeout: 5000 },
);
return response.data;
} catch (err) {
if (err.response?.data) {
return err.response.data;
}
throw new Error("Failed to set log level");
}
}

View File

@ -2,7 +2,6 @@ import express from "express";
import log4js from "log4js"; import log4js from "log4js";
import { notPrompting } from "../utils.js"; import { notPrompting } from "../utils.js";
import { getConfigPath, loadConfig } from "../config.js"; import { getConfigPath, loadConfig } from "../config.js";
import { applyLogLevel } from "../logging.js";
import { getServerVersionInfo } from "../serverVersion.js"; import { getServerVersionInfo } from "../serverVersion.js";
import { startWaiting, stopWaiting } from "../spinner.js"; import { startWaiting, stopWaiting } from "../spinner.js";
@ -72,31 +71,6 @@ export class LocalServer {
res.status(500).json({ error: err.message }); res.status(500).json({ error: err.message });
} }
}); });
this.app.post("/logLevel", async (req, res) => {
logger.debug("Received request to set log level");
const { logLevel } = req.body;
if (!logLevel) {
res.status(400).json({ valid: false, error: "logLevel is required" });
return;
}
try {
const result = await applyLogLevel(logLevel);
if (!result.valid) {
res.status(400).json(result);
return;
}
logger.level = result.logLevel;
res.json(result);
} catch (err) {
logger.error("Failed to set log level:", err);
res.status(500).json({ valid: false, error: err.message });
}
});
} }
start() { start() {

View File

@ -1,26 +0,0 @@
import log4js from "log4js";
import { loadConfig, saveConfig } from "./config.js";
export const VALID_LOG_LEVELS = ["trace", "debug", "info", "warn", "error"];
export async function applyLogLevel(level) {
const normalized = String(level || "").toLowerCase();
if (!VALID_LOG_LEVELS.includes(normalized)) {
return {
valid: false,
error: `Invalid log level. Must be one of: ${VALID_LOG_LEVELS.join(", ")}`,
};
}
const config = loadConfig();
config.logLevel = normalized;
await saveConfig(config);
log4js.configure({
appenders: { out: { type: "stdout" } },
categories: { default: { appenders: ["out"], level: normalized } },
});
return { valid: true, logLevel: normalized };
}

View File

@ -147,7 +147,7 @@ export class PrinterDatabase {
}); });
logger.info( logger.info(
`Updated printer: ${chalk.bold.white(this.printer.name)}, State: ${chalk.bold.white(formatState(state.type))}${state?.progress ? ` (${state.progress})` : ""} (${online ? chalk.green("Online") : chalk.dim.white("Offline")})`, `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")})`,
); );
return updatedPrinter; return updatedPrinter;

View File

@ -63,10 +63,9 @@ export class PrinterManager {
} }
} }
await stopWaiting();
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(); await stopWaiting();
logger.error("Failed to update printers:", error); logger.error("Failed to update printers:", error);

View File

@ -221,9 +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 };
if (logs) {
await saveConfig(config); 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 });