keyringstorage: properly handle keyring fetching (#2108)

- if auto lock enabled, don't do anything and wait for lock password
- not try to overwrite and don't consider loaded when unlocking fails
- retry unlock and re fetch on demand (ai request)
This commit is contained in:
end-4
2025-11-02 23:20:01 +01:00
parent 525108dd95
commit 1ee08fca51
6 changed files with 66 additions and 25 deletions
@@ -13,10 +13,16 @@ import Quickshell.Hyprland
Scope {
id: root
Process {
id: unlockKeyringProc
onExited: (exitCode, exitStatus) => {
KeyringStorage.fetchKeyringData();
}
}
function unlockKeyring() {
Quickshell.execDetached({
unlockKeyringProc.exec({
environment: ({
UNLOCK_PASSWORD: root.currentText
"UNLOCK_PASSWORD": lockContext.currentText
}),
command: ["bash", "-c", Quickshell.shellPath("scripts/keyring/unlock.sh")]
})
@@ -24,7 +30,7 @@ Scope {
property var windowData: []
function saveWindowPositionAndTile() {
Hyprland.dispatch(`keyword dwindle:pseudotile true`)
Quickshell.execDetached(["hyprctl", "keyword", "dwindle:pseudotile", "true"])
root.windowData = HyprlandData.windowList.filter(w => (w.floating && w.workspace.id === HyprlandData.activeWorkspace.id))
root.windowData.forEach(w => {
Hyprland.dispatch(`pseudo address:${w.address}`)
@@ -38,7 +44,7 @@ Scope {
Hyprland.dispatch(`movewindowpixel exact ${w.at[0]} ${w.at[1]}, address:${w.address}`)
Hyprland.dispatch(`pseudo address:${w.address}`)
})
Hyprland.dispatch(`keyword dwindle:pseudotile false`)
Quickshell.execDetached(["hyprctl", "keyword", "dwindle:pseudotile", "false"])
}
// This stores all the information shared between the lock surfaces on each screen.
@@ -156,20 +162,24 @@ Scope {
}
}
function initIfReady() {
if (!Config.ready || !Persistent.ready) return;
if (Config.options.lock.launchOnStartup && Persistent.isNewHyprlandInstance) {
Hyprland.dispatch("global quickshell:lock")
} else {
KeyringStorage.fetchKeyringData();
}
}
Connections {
target: Config
function onReadyChanged() {
if (Config.options.lock.launchOnStartup && Config.ready && Persistent.ready && Persistent.isNewHyprlandInstance) {
Hyprland.dispatch("global quickshell:lock")
}
root.initIfReady();
}
}
Connections {
target: Persistent
function onReadyChanged() {
if (Config.options.lock.launchOnStartup && Config.ready && Persistent.ready && Persistent.isNewHyprlandInstance) {
Hyprland.dispatch("global quickshell:lock")
}
root.initIfReady();
}
}
}
+11
View File
@@ -0,0 +1,11 @@
#!/usr/bin/env bash
locked_state=$(busctl --user get-property org.freedesktop.secrets \
/org/freedesktop/secrets/collection/login \
org.freedesktop.Secret.Collection Locked)
if [[ "${locked_state}" == "b false" ]]; then
echo 'Keyring is unlocked' >&2
exit 0
else
echo 'Keyring is locked' >&2
exit 1
fi
+15
View File
@@ -0,0 +1,15 @@
#!/usr/bin/env bash
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
data=$(secret-tool lookup 'application' 'illogical-impulse')
if [[ -z "$data" ]]; then
if "${SCRIPT_DIR}/is_unlocked.sh"; then
echo 'not found'
exit 1
else
echo 'locked'
exit 2
fi
fi
echo "$data"
@@ -1,12 +1,10 @@
#!/usr/bin/env bash
# Based on https://unix.stackexchange.com/a/602935
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# Skip if already unlocked
locked_state=$(busctl --user get-property org.freedesktop.secrets \
/org/freedesktop/secrets/collection/login \
org.freedesktop.Secret.Collection Locked)
if [[ "${locked_state}" == "b false" ]]; then
echo 'Keyring is already unlocked.' >&2
if "${SCRIPT_DIR}/is_unlocked.sh"; then
exit 1
fi
+4 -2
View File
@@ -532,8 +532,6 @@ Singleton {
modelId = modelId.toLowerCase()
if (modelList.indexOf(modelId) !== -1) {
const model = models[modelId]
// Fetch API keys if needed
if (model?.requires_key) KeyringStorage.fetchKeyringData();
// See if policy prevents online models
if (Config.options.policies.ai === 2 && !model.endpoint.includes("localhost")) {
root.addMessage(
@@ -641,6 +639,10 @@ Singleton {
function makeRequest() {
const model = models[currentModelId];
// Fetch API keys if needed
if (model?.requires_key && !KeyringStorage.loaded) KeyringStorage.fetchKeyringData();
requester.currentStrategy = root.currentApiStrategy;
requester.currentStrategy.reset(); // Reset strategy state
@@ -1,6 +1,7 @@
pragma Singleton
pragma ComponentBehavior: Bound
import qs
import qs.modules.common
import qs.modules.common.functions
import Quickshell;
@@ -89,11 +90,13 @@ Singleton {
Process {
id: getData
command: [ // We need to use echo for a newline so splitparser does parse
"bash", "-c", `echo $(secret-tool lookup 'application' 'illogical-impulse')`,
"bash", "-c", `${Directories.scriptPath}/keyring/try_lookup.sh 2> /dev/null`,
]
stdout: SplitParser {
onRead: data => {
if(data.length === 0) return;
stdout: StdioCollector {
id: keyringDataOutputCollector
onStreamFinished: {
const data = keyringDataOutputCollector.text;
if (data.length === 0 || !data.startsWith("{")) return;
try {
root.keyringData = JSON.parse(data);
// console.log("[KeyringStorage] Keyring data fetched:", JSON.stringify(root.keyringData));
@@ -105,13 +108,15 @@ Singleton {
}
}
onExited: (exitCode, exitStatus) => {
// console.log("[KeyringStorage] Keyring data fetch process exited with code:", exitCode);
if (exitCode !== 0) {
console.error("[KeyringStorage] Failed to get keyring data, reinitializing.");
console.log("[KeyringStorage] Keyring data fetch process exited with code:", exitCode);
if (exitCode === 1) {
console.error("[KeyringStorage] Entry not found, initializing.");
root.keyringData = {};
saveKeyringData()
}
root.loaded = true;
if (exitCode !== 2) {
root.loaded = true;
}
}
}