diff --git a/.gitignore b/.gitignore index e07656c..f2afeb3 100644 --- a/.gitignore +++ b/.gitignore @@ -137,6 +137,7 @@ build .nova temp_files/* +data/* dist/* diff --git a/src/commandline.js b/src/commandline.js index f5ec082..c7b1047 100644 --- a/src/commandline.js +++ b/src/commandline.js @@ -42,6 +42,8 @@ export async function handleCommandLine(cli, logger) { runningServer.version ? `v${runningServer.version}` : "n/a", ); cli.label("Build:", buildNumber === "dev" ? "dev" : `b${buildNumber}`); + cli.label("Config:", runningServer.configPath || "n/a"); + cli.label("Data Dir:", runningServer.dataDir || "n/a"); cli.label("Connected:", cli.yesNo(runningServer.connected == true)); cli.label("Authenticated:", cli.yesNo(runningServer.authenticated == true)); cli.sectionFooter(); diff --git a/src/config.js b/src/config.js index 1376f49..368e7f3 100644 --- a/src/config.js +++ b/src/config.js @@ -79,6 +79,57 @@ const CONFIG_PATH = const logger = log4js.getLogger("Config"); logger.level = "info"; +function getDefaultProductionDataDir() { + const homeDir = process.env.HOME || process.env.USERPROFILE || ""; + switch (process.platform) { + case "win32": + return path.join( + process.env.PROGRAMDATA || "C:\\ProgramData", + "farmcontrol-server", + "data", + ); + case "darwin": + return path.join( + homeDir, + "Library", + "Application Support", + "farmcontrol-server", + "data", + ); + case "linux": + default: + return "/var/lib/farmcontrol-server"; + } +} + +function getDefaultDevelopmentDataDir() { + if (isPackaged) { + return path.join(path.dirname(getDevelopmentConfigPath()), "data"); + } + return path.resolve(__dirname, "../data"); +} + +function resolveDataDir(envConfig) { + let dataDir = envConfig.dataDir; + const wasMissing = !dataDir; + + if (!dataDir) { + dataDir = + NODE_ENV === "production" + ? getDefaultProductionDataDir() + : getDefaultDevelopmentDataDir(); + } + + dataDir = path.resolve(dataDir); + + if (!fs.existsSync(dataDir)) { + fs.mkdirSync(dataDir, { recursive: true }); + logger.info(`Created data directory at ${dataDir}`); + } + + return { dataDir, shouldPersist: wasMissing && NODE_ENV === "production" }; +} + const DEFAULT_CONFIG = { development: { logLevel: "debug", @@ -127,6 +178,14 @@ export function loadConfig() { ); } + const { dataDir, shouldPersist } = resolveDataDir(config[NODE_ENV]); + config[NODE_ENV] = { ...config[NODE_ENV], dataDir }; + + if (shouldPersist) { + fs.writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2), "utf8"); + logger.info(`Set data directory to ${dataDir} in config`); + } + return config[NODE_ENV]; } catch (err) { logger.error("Error loading config:", err); @@ -167,3 +226,25 @@ export async function saveConfig(newConfig) { export function getEnvironment() { return NODE_ENV; } + +// Get the path to the active config.json file +export function getConfigPath() { + return CONFIG_PATH; +} + +// Get the configured data directory, optionally with subpaths +export function getDataDir(...subpaths) { + const config = loadConfig(); + return subpaths.length > 0 + ? path.join(config.dataDir, ...subpaths) + : config.dataDir; +} + +// Ensure a subdirectory exists within the data directory +export function ensureDataDir(...subpaths) { + const dir = getDataDir(...subpaths); + if (!fs.existsSync(dir)) { + fs.mkdirSync(dir, { recursive: true }); + } + return dir; +} diff --git a/src/files/filemanager.js b/src/files/filemanager.js index 40a15ee..dcd4529 100644 --- a/src/files/filemanager.js +++ b/src/files/filemanager.js @@ -1,11 +1,10 @@ // filemanager.js - Manages file downloads and caching import fs from "fs"; import path from "path"; -import { fileURLToPath } from "url"; import axios from "axios"; import log4js from "log4js"; import _ from "lodash"; -import { loadConfig } from "../config.js"; +import { loadConfig, ensureDataDir } from "../config.js"; import { sendIPC } from "../electron/ipc.js"; const config = loadConfig(); @@ -13,31 +12,25 @@ const config = loadConfig(); const logger = log4js.getLogger("File Manager"); logger.level = config.logLevel; -// Configure paths relative to this file -const __filename = fileURLToPath(import.meta.url); -const __dirname = path.dirname(__filename); -const TEMP_FILES_DIR = path.resolve(__dirname, "../../temp_files"); +const FILES_DIR = ensureDataDir("files"); export class FileManager { constructor(socketClient) { this.socketClient = socketClient; this.files = []; this.downloadingFiles = new Map(); // Track ongoing downloads by fileId - this.ensureTempFilesDirectory(); + this.ensureFilesDirectory(); this.progressCallbacks = new Map(); } /** - * Ensure the temp_files directory exists + * Ensure the files directory exists within the data directory */ - ensureTempFilesDirectory() { + ensureFilesDirectory() { try { - if (!fs.existsSync(TEMP_FILES_DIR)) { - fs.mkdirSync(TEMP_FILES_DIR, { recursive: true }); - logger.info(`Created temp_files directory at ${TEMP_FILES_DIR}`); - } + ensureDataDir("files"); } catch (error) { - logger.error(`Failed to create temp_files directory: ${error.message}`); + logger.error(`Failed to create files directory: ${error.message}`); } } @@ -70,14 +63,14 @@ export class FileManager { } /** - * Get the file path in temp_files directory + * Get the file path in the files directory */ getFilePath(fileId) { - return path.join(TEMP_FILES_DIR, fileId); + return path.join(FILES_DIR, fileId); } /** - * Check if file exists in temp_files directory + * Check if file exists in the files directory */ fileExistsInCache(fileId) { const filePath = this.getFilePath(fileId); @@ -201,7 +194,7 @@ export class FileManager { /** * Get file by ID - * Checks temp_files directory first, then downloads from API if not found + * Checks the files directory first, then downloads from API if not found * If a download is already in progress for the same fileId, waits for that download */ async getFile(fileId, onProgress) { diff --git a/src/localserver/localserver.js b/src/localserver/localserver.js index e62c598..b6d4fca 100644 --- a/src/localserver/localserver.js +++ b/src/localserver/localserver.js @@ -1,7 +1,7 @@ import express from "express"; import log4js from "log4js"; import { notPrompting } from "../utils.js"; -import { loadConfig } from "../config.js"; +import { getConfigPath, loadConfig } from "../config.js"; import { getServerVersionInfo } from "../serverVersion.js"; import { startWaiting, stopWaiting } from "../spinner.js"; @@ -29,6 +29,8 @@ export class LocalServer { authenticated: this.socketClient.authenticated, connected: this.socketClient.connected, host: this.socketClient.host, + configPath: getConfigPath(), + dataDir: config.dataDir, ...getServerVersionInfo(), }); });