feat: simplify OTA updates with timestamp
This commit is contained in:
@@ -1,3 +1,11 @@
|
|||||||
|
<!--
|
||||||
|
* @Author: HenryDu8133 813367384@qq.com
|
||||||
|
* @Date: 2025-10-06 09:06:54
|
||||||
|
* @LastEditors: HenryDu8133 813367384@qq.com
|
||||||
|
* @LastEditTime: 2026-07-11 10:38:17
|
||||||
|
* @FilePath: \TicketMachine\README_ZH.md
|
||||||
|
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
|
||||||
|
-->
|
||||||
|
|
||||||
# FTC 票务系统
|
# FTC 票务系统
|
||||||
|
|
||||||
|
|||||||
@@ -127,17 +127,49 @@ local function setLastUpdateVersion(v)
|
|||||||
if f then f.write(tostring(v)); f.close() end
|
if f then f.write(tostring(v)); f.close() end
|
||||||
end
|
end
|
||||||
|
|
||||||
local function runSilentGateUpdate()
|
local function checkAndApplyUpdate()
|
||||||
if not fs.exists("update_gate.lua") then
|
if type(CFG) ~= 'table' then return end
|
||||||
return false, "update_gate.lua not found"
|
local remoteTs = tonumber(CFG.update_timestamp)
|
||||||
|
|
||||||
|
if not remoteTs or remoteTs <= 0 then return end
|
||||||
|
|
||||||
|
-- Prevent infinite reboot loops: only update once per timestamp
|
||||||
|
local attemptFile = ".last_update_ts"
|
||||||
|
if fs.exists(attemptFile) then
|
||||||
|
local f = fs.open(attemptFile, "r")
|
||||||
|
local tried = tonumber(f and f.readAll() or "0") or 0
|
||||||
|
if f then f.close() end
|
||||||
|
if tried >= remoteTs then
|
||||||
|
return
|
||||||
end
|
end
|
||||||
local baseEnv = (getfenv and getfenv()) or _ENV or _G
|
end
|
||||||
local env = setmetatable({ AUTO_UPDATE_SILENT = true }, { __index = baseEnv })
|
|
||||||
|
-- We have a NEW remote timestamp. Trigger update!
|
||||||
|
isUpdating = true
|
||||||
|
pcall(drawReadyScreen)
|
||||||
|
sleep(0.5)
|
||||||
|
|
||||||
|
-- Record that we are attempting this timestamp
|
||||||
|
local f = fs.open(attemptFile, "w")
|
||||||
|
if f then f.write(tostring(remoteTs)); f.close() end
|
||||||
|
|
||||||
|
-- Load and run update script
|
||||||
|
local env = setmetatable({ AUTO_UPDATE_SILENT = true }, { __index = _G or getfenv() or _ENV })
|
||||||
local fn, err = loadfile("update_gate.lua", env)
|
local fn, err = loadfile("update_gate.lua", env)
|
||||||
if not fn then return false, err end
|
if fn then
|
||||||
local ok, res = pcall(fn, "--silent")
|
local ok, res = pcall(fn, "--silent")
|
||||||
if not ok then return false, tostring(res) end
|
if not ok then
|
||||||
return true, true
|
lastUpdateError = string.sub(tostring(res), 1, 30)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
lastUpdateError = "loadfile failed: " .. tostring(err)
|
||||||
|
end
|
||||||
|
|
||||||
|
isUpdating = false
|
||||||
|
pcall(drawReadyScreen)
|
||||||
|
|
||||||
|
-- Reboot
|
||||||
|
os.reboot()
|
||||||
end
|
end
|
||||||
|
|
||||||
local function setServerConnected(ok)
|
local function setServerConnected(ok)
|
||||||
@@ -422,9 +454,10 @@ end
|
|||||||
local function refreshRemoteLuaVersion(serverBase)
|
local function refreshRemoteLuaVersion(serverBase)
|
||||||
serverBase = trim(serverBase or "")
|
serverBase = trim(serverBase or "")
|
||||||
if #serverBase == 0 then return false end
|
if #serverBase == 0 then return false end
|
||||||
local url = serverBase:gsub("/+$", "") .. "/api/public/config"
|
local url = serverBase:gsub("/+$", "") .. "/api/config?_t=" .. tostring(nowMs() + math.random(1, 999999))
|
||||||
local ok, parsed = getJSON(url)
|
local ok, parsed = getJSON(url)
|
||||||
if not ok or type(parsed) ~= "table" then return false end
|
if not ok or type(parsed) ~= "table" then return false end
|
||||||
|
_G.CFG = parsed
|
||||||
local remote = normalizeVersionTag(type(parsed.lua_versions) == "table" and parsed.lua_versions.gate or nil)
|
local remote = normalizeVersionTag(type(parsed.lua_versions) == "table" and parsed.lua_versions.gate or nil)
|
||||||
remoteForceUpdate = (parsed.force_update == true)
|
remoteForceUpdate = (parsed.force_update == true)
|
||||||
if #remote == 0 then
|
if #remote == 0 then
|
||||||
|
|||||||
@@ -189,6 +189,9 @@ router.put('/config', async (req, res) => {
|
|||||||
if (typeof incoming.force_update === 'boolean') {
|
if (typeof incoming.force_update === 'boolean') {
|
||||||
cfg.force_update = incoming.force_update;
|
cfg.force_update = incoming.force_update;
|
||||||
}
|
}
|
||||||
|
if (incoming.update_timestamp) {
|
||||||
|
cfg.update_timestamp = incoming.update_timestamp;
|
||||||
|
}
|
||||||
await DataService.saveConfig(cfg);
|
await DataService.saveConfig(cfg);
|
||||||
appendReqLog(req, { category: 'admin', type: 'update_config_generic', detail: incoming });
|
appendReqLog(req, { category: 'admin', type: 'update_config_generic', detail: incoming });
|
||||||
io.emit('config:updated', cfg);
|
io.emit('config:updated', cfg);
|
||||||
|
|||||||
@@ -229,7 +229,8 @@ router.get('/config', (req, res) => {
|
|||||||
res.json({
|
res.json({
|
||||||
promotion: { name: cfg.promotion?.name || '', discount: cfg.promotion?.discount ?? 1 },
|
promotion: { name: cfg.promotion?.name || '', discount: cfg.promotion?.discount ?? 1 },
|
||||||
lua_versions: cfg.lua_versions || {},
|
lua_versions: cfg.lua_versions || {},
|
||||||
force_update: cfg.force_update === true
|
force_update: cfg.force_update === true,
|
||||||
|
update_timestamp: cfg.update_timestamp || 0
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
+33
-17
@@ -14,8 +14,6 @@ end
|
|||||||
|
|
||||||
local serverConnected = nil
|
local serverConnected = nil
|
||||||
local versionMismatch = nil
|
local versionMismatch = nil
|
||||||
local failedUpdateVersion = nil
|
|
||||||
local pendingMachineUpdate = false
|
|
||||||
local isUpdating = false
|
local isUpdating = false
|
||||||
local state = {}
|
local state = {}
|
||||||
|
|
||||||
@@ -716,31 +714,49 @@ local function drawVersionIndicator()
|
|||||||
termDev.setTextColor(colors.white)
|
termDev.setTextColor(colors.white)
|
||||||
end
|
end
|
||||||
|
|
||||||
local function backgroundSyncTask()
|
local function checkAndApplyUpdate()
|
||||||
pcall(refreshConfigOnce)
|
if type(CFG) ~= 'table' then return end
|
||||||
while true do
|
local remoteTs = tonumber(CFG.update_timestamp)
|
||||||
if type(CFG) == 'table' and type(CFG.force_update) == 'boolean' and CFG.force_update == true and versionMismatch == true then
|
|
||||||
|
if not remoteTs or remoteTs <= 0 then return end
|
||||||
|
|
||||||
|
local attemptFile = ".last_update_ts"
|
||||||
|
if fs.exists(attemptFile) then
|
||||||
|
local f = fs.open(attemptFile, "r")
|
||||||
|
local tried = tonumber(f and f.readAll() or "0") or 0
|
||||||
|
if f then f.close() end
|
||||||
|
if tried >= remoteTs then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
end
|
||||||
isUpdating = true
|
isUpdating = true
|
||||||
pcall(drawVersionIndicator)
|
pcall(drawVersionIndicator)
|
||||||
sleep(0.5)
|
sleep(0.5)
|
||||||
|
|
||||||
-- Extreme fallback: just run the update script directly
|
local f = fs.open(attemptFile, "w")
|
||||||
local baseEnv = (getfenv and getfenv()) or _ENV or _G
|
if f then f.write(tostring(remoteTs)); f.close() end
|
||||||
local env = setmetatable({ AUTO_UPDATE_SILENT = true }, { __index = baseEnv })
|
|
||||||
local fn = loadfile('update_machine.lua', env)
|
local env = setmetatable({ AUTO_UPDATE_SILENT = true }, { __index = _G or getfenv() or _ENV })
|
||||||
|
local fn, err = loadfile("update_machine.lua", env)
|
||||||
if fn then
|
if fn then
|
||||||
local ok, err = pcall(fn, '--silent')
|
local ok, res = pcall(fn, "--silent")
|
||||||
if not ok then
|
if not ok then
|
||||||
lastUpdateError = string.sub(tostring(err), 1, 30)
|
lastUpdateError = string.sub(tostring(res), 1, 30)
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
lastUpdateError = "loadfile failed"
|
lastUpdateError = "loadfile failed: " .. tostring(err)
|
||||||
end
|
end
|
||||||
|
|
||||||
isUpdating = false
|
isUpdating = false
|
||||||
pcall(drawVersionIndicator)
|
pcall(drawVersionIndicator)
|
||||||
sleep(10) -- Sleep 10s to prevent spamming
|
|
||||||
end
|
os.reboot()
|
||||||
|
end
|
||||||
|
|
||||||
|
local function backgroundSyncTask()
|
||||||
|
pcall(refreshConfigOnce)
|
||||||
|
while true do
|
||||||
|
pcall(checkAndApplyUpdate)
|
||||||
sleep(UPDATE_CHECK_INTERVAL)
|
sleep(UPDATE_CHECK_INTERVAL)
|
||||||
pcall(refreshConfigOnce)
|
pcall(refreshConfigOnce)
|
||||||
end
|
end
|
||||||
@@ -910,7 +926,7 @@ local function sameLogicalStation(a, b)
|
|||||||
end
|
end
|
||||||
|
|
||||||
local function fetchConfig()
|
local function fetchConfig()
|
||||||
local txt = fetchHTTP(API_BASE .. '/config')
|
local txt = fetchHTTP(API_BASE .. '/config?_t=' .. tostring(os.epoch and os.epoch("utc") or math.random(1, 999999)))
|
||||||
return txt and jsonDecode(txt) or nil
|
return txt and jsonDecode(txt) or nil
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -1033,7 +1049,7 @@ local function waitButtons()
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
elseif ev == 'trigger_update' then
|
elseif ev == 'trigger_update' then
|
||||||
pendingMachineUpdate = true
|
-- obsolete
|
||||||
elseif ev == 'config_updated' then
|
elseif ev == 'config_updated' then
|
||||||
if state and state.page == 'home' then return end
|
if state and state.page == 'home' then return end
|
||||||
end
|
end
|
||||||
|
|||||||
+4
-7
@@ -1483,19 +1483,16 @@ createApp({
|
|||||||
|
|
||||||
isPushingUpdate.value = true;
|
isPushingUpdate.value = true;
|
||||||
|
|
||||||
// 自动提升版本号 & 开启 force_update
|
// 放弃版本号验证,直接使用时间戳强制下发更新指令
|
||||||
if (!config.lua_versions.ticketmachine) config.lua_versions.ticketmachine = 'v1.0.0';
|
config.update_timestamp = Date.now();
|
||||||
if (!config.lua_versions.gate) config.lua_versions.gate = 'v1.0.0';
|
|
||||||
config.lua_versions.ticketmachine = bumpPatchVersion(config.lua_versions.ticketmachine);
|
|
||||||
config.lua_versions.gate = bumpPatchVersion(config.lua_versions.gate);
|
|
||||||
config.force_update = true;
|
config.force_update = true;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await requestJson('/api/config', { method: 'PUT', headers: {'Content-Type': 'application/json'}, body: JSON.stringify(config) }, { expectOk: true });
|
await requestJson('/api/config', { method: 'PUT', headers: {'Content-Type': 'application/json'}, body: JSON.stringify(config) }, { expectOk: true });
|
||||||
toast('推送指令发送完毕,终端将陆续更新', 'success');
|
alert('推送指令发送完毕,终端将陆续更新');
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
toast('推送更新失败', 'error');
|
alert('推送更新失败: ' + err.message);
|
||||||
} finally {
|
} finally {
|
||||||
isPushingUpdate.value = false;
|
isPushingUpdate.value = false;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user