From 173a0a9f9556df27a8d0f59ae8e2f7c90fbab58d Mon Sep 17 00:00:00 2001 From: MayaTheShy Date: Sun, 22 Mar 2026 20:51:31 -0400 Subject: [PATCH] Persist config files across Opus package updates The Opus package manager deletes the entire package directory on update (fs.delete(packageDir)) before re-downloading files. This wiped all config files (.manager_config, .client_config, etc.) that were stored inside packages/inventory-manager/. Fix: add _configPath() helper to every program that resolves config file paths to usr/config/inventory-manager/ when running under Opus, which lives outside the package directory and survives updates. Falls back to the local _path() for standalone (non-Opus) use. Updated files: - inventoryManager.lua, manager/config.lua, inventoryClient.lua, inventoryWebBridge.lua, dropperController.lua, craftingTurtle.lua - .package install script: saves configs to usr/config/inventory-manager/ - autorun/startup.lua: checks both persistent and package dirs - startup/client.lua: uses persistent dir for .client_setup/.dropper_config - web/server/Dockerfile: switch health check to wget (from prior fix) --- .package | 11 ++++++----- autorun/startup.lua | 12 +++++++++--- craftingTurtle.lua | 12 +++++++++++- dropperController.lua | 13 ++++++++++++- inventoryClient.lua | 12 +++++++++++- inventoryManager.lua | 13 ++++++++++++- inventoryWebBridge.lua | 12 +++++++++++- manager/config.lua | 18 ++++++++++++++---- startup/client.lua | 9 +++++++-- web/server/Dockerfile | 2 +- 10 files changed, 94 insertions(+), 20 deletions(-) diff --git a/.package b/.package index a1d21f4..07b48b9 100644 --- a/.package +++ b/.package @@ -8,7 +8,8 @@ "%.bak$", "^listDevicesByType%.lua$", }, install = [[ - local pkgDir = fs.combine("packages", "inventory-manager") + local cfgDir = "usr/config/inventory-manager" + if not fs.isDir(cfgDir) then fs.makeDir(cfgDir) end local function ask(prompt, default) if default and #default > 0 then write(prompt .. " [" .. default .. "]: ") @@ -47,14 +48,14 @@ dropperName = dropperName, barrelName = barrelName, } - local f = fs.open(fs.combine(pkgDir, ".manager_config"), "w") + local f = fs.open(fs.combine(cfgDir, ".manager_config"), "w") f.write(textutils.serialiseJSON(cfg)) f.close() print("Saved manager config.") if serverUrl and #serverUrl > 0 then local bcfg = { serverUrl = serverUrl } - local bf = fs.open(fs.combine(pkgDir, ".webbridge_config"), "w") + local bf = fs.open(fs.combine(cfgDir, ".webbridge_config"), "w") bf.write(textutils.serialiseJSON(bcfg)) bf.close() print("Saved web bridge config.") @@ -75,7 +76,7 @@ if dropperName and #dropperName > 0 then cfg.dropperName = dropperName end if barrelName and #barrelName > 0 then cfg.barrelName = barrelName end - local f = fs.open(fs.combine(pkgDir, ".client_config"), "w") + local f = fs.open(fs.combine(cfgDir, ".client_config"), "w") f.write(textutils.serialiseJSON(cfg)) f.close() print("Saved client config.") @@ -86,7 +87,7 @@ local serverUrl = ask("Web server URL", "http://localhost") local cfg = { serverUrl = serverUrl } - local f = fs.open(fs.combine(pkgDir, ".webbridge_config"), "w") + local f = fs.open(fs.combine(cfgDir, ".webbridge_config"), "w") f.write(textutils.serialiseJSON(cfg)) f.close() print("Saved web bridge config.") diff --git a/autorun/startup.lua b/autorun/startup.lua index 8e9da79..fcb3a8f 100644 --- a/autorun/startup.lua +++ b/autorun/startup.lua @@ -8,17 +8,23 @@ local peripheral = _G.peripheral local shell = _ENV.shell local BASE = 'packages/inventory-manager' +local CFG = 'usr/config/inventory-manager' ------------------------------------------------- -- Determine role from config files written during install ------------------------------------------------- +local function cfgExists(name) + return fs.exists(fs.combine(CFG, name)) + or fs.exists(fs.combine(BASE, name)) +end + local role -if fs.exists(fs.combine(BASE, '.manager_config')) then +if cfgExists('.manager_config') then role = 'manager' -elseif fs.exists(fs.combine(BASE, '.client_config')) then +elseif cfgExists('.client_config') then role = 'client' -elseif fs.exists(fs.combine(BASE, '.webbridge_config')) then +elseif cfgExists('.webbridge_config') then role = 'bridge' elseif _G.turtle then role = 'turtle' diff --git a/craftingTurtle.lua b/craftingTurtle.lua index 376c685..6668eb2 100644 --- a/craftingTurtle.lua +++ b/craftingTurtle.lua @@ -31,7 +31,17 @@ local CRAFT_SLOTS = {1, 2, 3, 5, 6, 7, 9, 10, 11} local _baseDir = fs.getDir(shell.getRunningProgram()) local function _path(rel) return fs.combine(_baseDir, rel) end -local TURTLE_CONFIG_FILE = _path(".turtle_config") +-- Persistent config path (survives Opus package updates) +local _PERSIST_DIR = "usr/config/inventory-manager" +local function _configPath(rel) + if fs.isDir(_PERSIST_DIR) or fs.isDir("packages/inventory-manager") then + if not fs.isDir(_PERSIST_DIR) then fs.makeDir(_PERSIST_DIR) end + return fs.combine(_PERSIST_DIR, rel) + end + return _path(rel) +end + +local TURTLE_CONFIG_FILE = _configPath(".turtle_config") local function loadConfig() if not fs.exists(TURTLE_CONFIG_FILE) then return end diff --git a/dropperController.lua b/dropperController.lua index de933be..14f995d 100644 --- a/dropperController.lua +++ b/dropperController.lua @@ -11,7 +11,18 @@ local POLL_INTERVAL = 0.5 -- how often to check the dropper ------------------------------------------------- local _baseDir = fs.getDir(shell.getRunningProgram()) -local CONFIG_FILE = fs.combine(_baseDir, ".dropper_config") + +-- Persistent config path: survives Opus package updates +local _PERSIST_DIR = "usr/config/inventory-manager" +local function _configPath(rel) + if fs.isDir(_PERSIST_DIR) or fs.isDir("packages/inventory-manager") then + if not fs.isDir(_PERSIST_DIR) then fs.makeDir(_PERSIST_DIR) end + return fs.combine(_PERSIST_DIR, rel) + end + return fs.combine(_baseDir, rel) +end + +local CONFIG_FILE = _configPath(".dropper_config") if fs.exists(CONFIG_FILE) then local f = fs.open(CONFIG_FILE, "r") diff --git a/inventoryClient.lua b/inventoryClient.lua index 5017e15..fc8dc8d 100644 --- a/inventoryClient.lua +++ b/inventoryClient.lua @@ -30,6 +30,16 @@ local DROPPER_ANNOUNCE_INTERVAL = 30 -- seconds between dropper announcements local _baseDir = fs.getDir(shell.getRunningProgram()) local function _path(rel) return fs.combine(_baseDir, rel) end +-- Persistent config path: survives Opus package updates +local _PERSIST_DIR = "usr/config/inventory-manager" +local function _configPath(rel) + if fs.isDir(_PERSIST_DIR) or fs.isDir("packages/inventory-manager") then + if not fs.isDir(_PERSIST_DIR) then fs.makeDir(_PERSIST_DIR) end + return fs.combine(_PERSIST_DIR, rel) + end + return _path(rel) +end + -- Override dofile to load modules into our _ENV so they inherit -- Opus's require/package (CC:Tweaked dofile uses _G instead). local _ccDofile = dofile @@ -39,7 +49,7 @@ local function dofile(path) -- luacheck: ignore else error(err, 2) end end -local CLIENT_CONFIG_FILE = _path(".client_config") +local CLIENT_CONFIG_FILE = _configPath(".client_config") ------------------------------------------------- -- Modules diff --git a/inventoryManager.lua b/inventoryManager.lua index 90688c0..46aff86 100644 --- a/inventoryManager.lua +++ b/inventoryManager.lua @@ -15,6 +15,17 @@ local _baseDir = fs.getDir(shell.getRunningProgram()) local function _path(rel) return fs.combine(_baseDir, rel) end +-- Persistent config path: survives Opus package updates by storing +-- user data in usr/config/inventory-manager/ instead of the package dir. +local _PERSIST_DIR = "usr/config/inventory-manager" +local function _configPath(rel) + if fs.isDir(_PERSIST_DIR) or fs.isDir("packages/inventory-manager") then + if not fs.isDir(_PERSIST_DIR) then fs.makeDir(_PERSIST_DIR) end + return fs.combine(_PERSIST_DIR, rel) + end + return _path(rel) +end + -- Write crash info to a file so we can always read it local function _crashLog(err) local f = fs.open(_path(".crash.log"), "w") @@ -42,7 +53,7 @@ end local log = dofile(_path("lib/log.lua")) local ui = dofile(_path("lib/ui.lua")) local itemDB = dofile(_path("lib/itemDB.lua")) -itemDB.init(_path(".item_names.db")) +itemDB.init(_configPath(".item_names.db")) ------------------------------------------------- -- Load modules (factory pattern → shared context) diff --git a/inventoryWebBridge.lua b/inventoryWebBridge.lua index d1cd071..7d77dd9 100644 --- a/inventoryWebBridge.lua +++ b/inventoryWebBridge.lua @@ -24,7 +24,17 @@ local ORDER_CHANNEL = 4201 local _baseDir = fs.getDir(shell.getRunningProgram()) local function _path(rel) return fs.combine(_baseDir, rel) end -local CONFIG_FILE = _path(".webbridge_config") +-- Persistent config path: survives Opus package updates +local _PERSIST_DIR = "usr/config/inventory-manager" +local function _configPath(rel) + if fs.isDir(_PERSIST_DIR) or fs.isDir("packages/inventory-manager") then + if not fs.isDir(_PERSIST_DIR) then fs.makeDir(_PERSIST_DIR) end + return fs.combine(_PERSIST_DIR, rel) + end + return _path(rel) +end + +local CONFIG_FILE = _configPath(".webbridge_config") local API_KEY = nil -- optional API key for server auth local function loadConfig() diff --git a/manager/config.lua b/manager/config.lua index 2df0147..709cf08 100644 --- a/manager/config.lua +++ b/manager/config.lua @@ -22,9 +22,9 @@ C.SMELT_RESERVE = 128 C.DEFRAG_INTERVAL = 600 C.COMPOST_INTERVAL = 3 C.ALERT_INTERVAL = 15 -C.CACHE_FILE = _path(".inventory_cache") +C.CACHE_FILE = _configPath(".inventory_cache") C.SMELTER_MONITOR_SIDE = "top" -C.DISABLED_RECIPES_FILE = _path(".disabled_recipes") +C.DISABLED_RECIPES_FILE = _configPath(".disabled_recipes") -- Network C.BROADCAST_CHANNEL = 4200 @@ -71,7 +71,17 @@ C.SLOT_OUTPUT = 3 -- Config file loader ------------------------------------------------- -local CONFIG_FILE = _path(".manager_config") +-- Persistent config path: survives Opus package updates +local _PERSIST_DIR = "usr/config/inventory-manager" +local function _configPath(rel) + if fs.isDir(_PERSIST_DIR) or fs.isDir("packages/inventory-manager") then + if not fs.isDir(_PERSIST_DIR) then fs.makeDir(_PERSIST_DIR) end + return fs.combine(_PERSIST_DIR, rel) + end + return _path(rel) +end + +local CONFIG_FILE = _configPath(".manager_config") function C.loadConfig() if not fs.exists(CONFIG_FILE) then return end @@ -124,7 +134,7 @@ C.LOW_STOCK_ALERTS = dofile(_path("data/alerts.lua")) -- Recipe book: merges built-in recipes + user-learned recipes local recipeBook = dofile(_path("lib/recipeBook.lua")) -recipeBook.init(_path(".recipes.db")) +recipeBook.init(_configPath(".recipes.db")) recipeBook.loadLegacyCrafting(dofile(_path("data/craftable.lua"))) recipeBook.loadLegacySmelting(dofile(_path("data/smeltable.lua"))) C.recipeBook = recipeBook diff --git a/startup/client.lua b/startup/client.lua index 8b886bd..13bdfe1 100644 --- a/startup/client.lua +++ b/startup/client.lua @@ -2,8 +2,13 @@ -- Auto-updates from git then launches inventoryClient + dropperController in parallel local REPO_RAW = "https://git.spatulaa.com/MayaTheShy/Inventory-Manager-CC/raw/branch/main" -local SETUP_FILE = ".client_setup" -- marks first-run complete -local DROPPER_CONFIG = ".dropper_config" + +-- Persistent config directory (survives Opus package updates) +local PERSIST_DIR = "usr/config/inventory-manager" +if not fs.isDir(PERSIST_DIR) then fs.makeDir(PERSIST_DIR) end + +local SETUP_FILE = fs.combine(PERSIST_DIR, ".client_setup") +local DROPPER_CONFIG = fs.combine(PERSIST_DIR, ".dropper_config") -- Files to download (destination -> repo path) local FILES = { diff --git a/web/server/Dockerfile b/web/server/Dockerfile index 6606150..47839eb 100644 --- a/web/server/Dockerfile +++ b/web/server/Dockerfile @@ -27,7 +27,7 @@ RUN chmod +x /usr/local/bin/docker-entrypoint.sh EXPOSE 3001 HEALTHCHECK --interval=10s --timeout=5s --start-period=15s --retries=3 \ - CMD wget -qO- http://localhost:3001/api/health || exit 1 + CMD wget --spider -q http://localhost:3001/api/health || exit 1 ENTRYPOINT ["docker-entrypoint.sh"] CMD ["node", "server.js"]