103 lines
2.6 KiB
Luau
103 lines
2.6 KiB
Luau
--!nonstrict
|
|
|
|
local entries = {}
|
|
local scanInFlight = false
|
|
|
|
local function publish()
|
|
noctalia.state.set("entries", entries)
|
|
end
|
|
|
|
local function shellEscape(value)
|
|
return "'" .. value:gsub("'", "'\\''") .. "'"
|
|
end
|
|
|
|
local function passwordStoreDir()
|
|
local dir = noctalia.getenv("PASSWORD_STORE_DIR")
|
|
|
|
if dir and dir ~= "" then
|
|
return noctalia.expandPath(dir)
|
|
end
|
|
|
|
return noctalia.expandPath("~/.password-store")
|
|
end
|
|
|
|
local function rebuild()
|
|
if scanInFlight then
|
|
return
|
|
end
|
|
|
|
local root = passwordStoreDir()
|
|
|
|
if not noctalia.fileExists(root) then
|
|
noctalia.notifyError("Pass", noctalia.tr("notification.store_not_found"))
|
|
return
|
|
end
|
|
|
|
-- Directory traversal can exceed Noctalia's callback CPU budget on large stores.
|
|
-- Run it in a subprocess and only build the small launcher cache in Luau.
|
|
local command = "cd " .. shellEscape(root)
|
|
.. " && find . -mindepth 1"
|
|
.. " \\( -type d -name '.*' -prune \\)"
|
|
.. " -o \\( -name '*.gpg' ! -name '.*' -print0 \\)"
|
|
|
|
scanInFlight = true
|
|
|
|
local accepted = noctalia.runAsync(command, function(result)
|
|
scanInFlight = false
|
|
|
|
if result.exitCode ~= 0 then
|
|
noctalia.log("Failed to scan " .. root .. ": " .. (result.stderr or "unknown error"))
|
|
return
|
|
end
|
|
|
|
if result.stdoutTruncated then
|
|
noctalia.log("Failed to scan " .. root .. ": command output was truncated")
|
|
return
|
|
end
|
|
|
|
local nextEntries = {}
|
|
|
|
for file in (result.stdout or ""):gmatch("([^%z]+)%z") do
|
|
-- find returns paths as ./folder/entry.gpg.
|
|
local path = file:sub(3, -5)
|
|
local title = path
|
|
local subtitle = ""
|
|
local lastSlash = path:match("^.*()/")
|
|
|
|
if lastSlash then
|
|
subtitle = path:sub(1, lastSlash - 1)
|
|
title = path:sub(lastSlash + 1)
|
|
end
|
|
|
|
table.insert(nextEntries, {
|
|
id = path,
|
|
path = path,
|
|
title = title,
|
|
subtitle = subtitle,
|
|
})
|
|
end
|
|
|
|
table.sort(nextEntries, function(a, b)
|
|
return a.title:lower() < b.title:lower()
|
|
end)
|
|
|
|
entries = nextEntries
|
|
publish()
|
|
end, 60000)
|
|
|
|
if not accepted then
|
|
scanInFlight = false
|
|
noctalia.log("Failed to start password-store scan")
|
|
end
|
|
end
|
|
|
|
function update()
|
|
rebuild()
|
|
end
|
|
|
|
local refresh = noctalia.getConfig("refresh_interval") or 30
|
|
|
|
noctalia.setUpdateInterval(refresh * 1000)
|
|
|
|
rebuild()
|