fix: improve onboarding checks and errors

This commit is contained in:
Brad Stein 2026-01-23 18:23:06 -03:00
parent c7d2b81ad8
commit b017775bdb
3 changed files with 105 additions and 4 deletions

View File

@ -255,6 +255,53 @@ def register(app) -> None:
jellyfin_sync_status = "unknown" jellyfin_sync_status = "unknown"
jellyfin_sync_detail = "unavailable" jellyfin_sync_detail = "unavailable"
if (
username
and not vaultwarden_master_set_at
and vaultwarden_status in {"", "invited", "needs provisioning"}
and settings.PORTAL_DATABASE_URL
):
try:
with connect() as conn:
row = conn.execute(
"""
SELECT request_code
FROM access_requests
WHERE username = %s AND status IN ('awaiting_onboarding', 'ready')
ORDER BY created_at DESC
LIMIT 1
""",
(username,),
).fetchone()
if not row:
row = conn.execute(
"""
SELECT request_code
FROM access_requests
WHERE username = %s
ORDER BY created_at DESC
LIMIT 1
""",
(username,),
).fetchone()
if row and isinstance(row, dict):
request_code = str(row.get("request_code") or "").strip()
if request_code:
step = conn.execute(
"""
SELECT 1
FROM access_request_onboarding_steps
WHERE request_code = %s AND step = %s
LIMIT 1
""",
(request_code, "vaultwarden_master_password"),
).fetchone()
if step:
vaultwarden_master_set_at = "confirmed"
vaultwarden_status = "ready"
except Exception:
pass
mailu_username = mailu_email or (f"{username}@{settings.MAILU_DOMAIN}" if username else "") mailu_username = mailu_email or (f"{username}@{settings.MAILU_DOMAIN}" if username else "")
firefly_username = mailu_username firefly_username = mailu_username
vaultwarden_username = vaultwarden_email or mailu_username vaultwarden_username = vaultwarden_email or mailu_username
@ -460,6 +507,16 @@ def register(app) -> None:
return jsonify({"status": "ok", "password": password}) return jsonify({"status": "ok", "password": password})
@app.route("/api/account/wger/rotation/check", methods=["POST"])
@require_auth
def account_wger_rotation_check() -> Any:
ok, resp = require_account_access()
if not ok:
return resp
if ariadne_client.enabled():
return ariadne_client.proxy("POST", "/api/account/wger/rotation/check")
return jsonify({"error": "server not configured"}), 503
@app.route("/api/account/firefly/reset", methods=["POST"]) @app.route("/api/account/firefly/reset", methods=["POST"])
@require_auth @require_auth
def account_firefly_reset() -> Any: def account_firefly_reset() -> Any:
@ -513,6 +570,16 @@ def register(app) -> None:
return jsonify({"status": "ok", "password": password}) return jsonify({"status": "ok", "password": password})
@app.route("/api/account/firefly/rotation/check", methods=["POST"])
@require_auth
def account_firefly_rotation_check() -> Any:
ok, resp = require_account_access()
if not ok:
return resp
if ariadne_client.enabled():
return ariadne_client.proxy("POST", "/api/account/firefly/rotation/check")
return jsonify({"error": "server not configured"}), 503
@app.route("/api/account/nextcloud/mail/sync", methods=["POST"]) @app.route("/api/account/nextcloud/mail/sync", methods=["POST"])
@require_auth @require_auth
def account_nextcloud_mail_sync() -> Any: def account_nextcloud_mail_sync() -> Any:

View File

@ -675,6 +675,16 @@ function formatName(req) {
return parts.length ? parts.join(" ") : "unknown"; return parts.length ? parts.join(" ") : "unknown";
} }
function formatActionError(err, fallback) {
const message = err?.message || "";
if (!message) return fallback;
const normalized = message.toLowerCase();
if (normalized.includes("ariadne unavailable") || normalized.includes("status 502") || normalized.includes("status 503")) {
return "Ariadne is busy. Please try again in a moment.";
}
return message;
}
function toggleFlag(username, flag, event) { function toggleFlag(username, flag, event) {
const checked = Boolean(event?.target?.checked); const checked = Boolean(event?.target?.checked);
const selected = Array.isArray(admin.selectedFlags[username]) ? [...admin.selectedFlags[username]] : []; const selected = Array.isArray(admin.selectedFlags[username]) ? [...admin.selectedFlags[username]] : [];
@ -709,7 +719,7 @@ async function rotateMailu() {
} }
await refreshOverview(); await refreshOverview();
} catch (err) { } catch (err) {
mailu.error = err.message || "Rotation failed"; mailu.error = formatActionError(err, "Rotation failed");
} finally { } finally {
mailu.rotating = false; mailu.rotating = false;
} }
@ -728,7 +738,7 @@ async function resetWger() {
} }
await refreshOverview(); await refreshOverview();
} catch (err) { } catch (err) {
wger.error = err.message || "Reset failed"; wger.error = formatActionError(err, "Reset failed");
} finally { } finally {
wger.resetting = false; wger.resetting = false;
} }
@ -747,7 +757,7 @@ async function resetFirefly() {
} }
await refreshOverview(); await refreshOverview();
} catch (err) { } catch (err) {
firefly.error = err.message || "Reset failed"; firefly.error = formatActionError(err, "Reset failed");
} finally { } finally {
firefly.resetting = false; firefly.resetting = false;
} }
@ -766,7 +776,7 @@ async function syncNextcloudMail() {
if (!resp.ok) throw new Error(data.error || `status ${resp.status}`); if (!resp.ok) throw new Error(data.error || `status ${resp.status}`);
await refreshOverview(); await refreshOverview();
} catch (err) { } catch (err) {
nextcloudMail.error = err.message || "Sync failed"; nextcloudMail.error = formatActionError(err, "Sync failed");
} finally { } finally {
nextcloudMail.syncing = false; nextcloudMail.syncing = false;
} }

View File

@ -945,6 +945,12 @@ async function confirmStep(step) {
return; return;
} }
if (step.action === "auto") { if (step.action === "auto") {
if (step.id === "firefly_password_rotated") {
await runRotationCheck("firefly");
}
if (step.id === "wger_password_rotated") {
await runRotationCheck("wger");
}
await check(); await check();
return; return;
} }
@ -956,11 +962,29 @@ async function confirmStep(step) {
return; return;
} }
await setStepCompletion(step.id, true); await setStepCompletion(step.id, true);
} catch (err) {
error.value = err?.message || "Failed to confirm step";
} finally { } finally {
confirmingStepId.value = ""; confirmingStepId.value = "";
} }
} }
async function runRotationCheck(service) {
if (!auth.authenticated) {
throw new Error("Log in to update onboarding steps.");
}
const endpoint =
service === "firefly"
? "/api/account/firefly/rotation/check"
: "/api/account/wger/rotation/check";
const resp = await authFetch(endpoint, { method: "POST" });
const data = await resp.json().catch(() => ({}));
if (!resp.ok) {
throw new Error(data.error || resp.statusText || `status ${resp.status}`);
}
return data;
}
async function requestKeycloakPasswordRotation() { async function requestKeycloakPasswordRotation() {
if (!requestCode.value.trim()) { if (!requestCode.value.trim()) {
error.value = "Request code is missing."; error.value = "Request code is missing.";