mirror of
https://github.com/photoprism/photoprism.git
synced 2026-07-17 16:49:04 +00:00
This commit is contained in:
parent
a2e8592dd5
commit
0575659c08
7 changed files with 86 additions and 25 deletions
|
|
@ -30,6 +30,8 @@
|
|||
"session.provider",
|
||||
"session.scope",
|
||||
"session.error",
|
||||
"session.messageId",
|
||||
"session.messageParams",
|
||||
];
|
||||
const storages = [localStorage, sessionStorage].filter(Boolean);
|
||||
const preferredStorage =
|
||||
|
|
@ -48,27 +50,23 @@
|
|||
const setItem = (key, value) => {
|
||||
preferredStorage.setItem(namespacedKey(key), value);
|
||||
};
|
||||
const removeItem = (key) => {
|
||||
storages.forEach((storage) => storage.removeItem(namespacedKey(key)));
|
||||
};
|
||||
|
||||
{{/* clearSessionData() removes every session.* key (including session.error,
|
||||
session.messageId, and session.messageParams), so each branch only sets
|
||||
the keys it wants to persist. */}}
|
||||
{{ if eq .status "success" }}
|
||||
clearSessionData();
|
||||
setItem("session.id", {{ .session_id }});
|
||||
setItem("session.token", {{ .access_token }});
|
||||
setItem("session.provider", {{ .provider }});
|
||||
setItem("session.user", JSON.stringify({{ .user }}));
|
||||
removeItem("session.error");
|
||||
{{ else if eq .status "failed" }}
|
||||
clearSessionData();
|
||||
setItem("session.error", {{ .error }});
|
||||
{{ if .messageId }}setItem("session.messageId", {{ .messageId }});{{ end }}
|
||||
{{ if .messageParams }}setItem("session.messageParams", JSON.stringify({{ .messageParams }}));{{ end }}
|
||||
{{ else }}
|
||||
clearSessionData();
|
||||
removeItem("session.id");
|
||||
removeItem("session.token");
|
||||
removeItem("session.provider");
|
||||
removeItem("session.user");
|
||||
removeItem("session.error");
|
||||
{{ end }}
|
||||
})();
|
||||
window.location.href = {{ .config.LoginUri }};
|
||||
|
|
|
|||
|
|
@ -30,26 +30,34 @@ let ajaxPending = 0;
|
|||
let ajaxCallbacks = [];
|
||||
|
||||
const $notify = {
|
||||
info: function (message) {
|
||||
$event.publish("notify.info", { message });
|
||||
// info publishes an info notification.
|
||||
info: function (message, messageId, messageParams) {
|
||||
$event.publish("notify.info", { message, messageId, messageParams });
|
||||
},
|
||||
warn: function (message) {
|
||||
$event.publish("notify.warning", { message });
|
||||
// warn publishes a warning notification.
|
||||
warn: function (message, messageId, messageParams) {
|
||||
$event.publish("notify.warning", { message, messageId, messageParams });
|
||||
},
|
||||
error: function (message) {
|
||||
$event.publish("notify.error", { message });
|
||||
// error publishes an error notification; the optional messageId/messageParams
|
||||
// let notify.vue render it in the current UI locale via Tp.
|
||||
error: function (message, messageId, messageParams) {
|
||||
$event.publish("notify.error", { message, messageId, messageParams });
|
||||
},
|
||||
success: function (message) {
|
||||
$event.publish("notify.success", { message });
|
||||
// success publishes a success notification.
|
||||
success: function (message, messageId, messageParams) {
|
||||
$event.publish("notify.success", { message, messageId, messageParams });
|
||||
},
|
||||
logout: function (message) {
|
||||
$event.publish("notify.error", { message });
|
||||
$event.publish("session.logout", { message });
|
||||
// logout publishes an error notification and triggers a session.logout event.
|
||||
logout: function (message, messageId, messageParams) {
|
||||
$event.publish("notify.error", { message, messageId, messageParams });
|
||||
$event.publish("session.logout", { message, messageId, messageParams });
|
||||
},
|
||||
// ajaxStart marks an AJAX request as started and emits ajax.start.
|
||||
ajaxStart: function () {
|
||||
ajaxPending++;
|
||||
$event.publish("ajax.start");
|
||||
},
|
||||
// ajaxEnd marks an AJAX request as finished, emits ajax.end, and runs queued idle callbacks.
|
||||
ajaxEnd: function () {
|
||||
ajaxPending--;
|
||||
$event.publish("ajax.end");
|
||||
|
|
@ -60,6 +68,7 @@ const $notify = {
|
|||
callbacks.forEach((cb) => cb());
|
||||
}
|
||||
},
|
||||
// ajaxBusy reports whether any AJAX requests are still pending.
|
||||
ajaxBusy: function () {
|
||||
if (ajaxPending < 0) {
|
||||
ajaxPending = 0;
|
||||
|
|
@ -67,6 +76,7 @@ const $notify = {
|
|||
|
||||
return ajaxPending > 0;
|
||||
},
|
||||
// ajaxWait resolves once no AJAX requests are pending or the timeout elapses.
|
||||
ajaxWait: function (idleDelay = 64, timeout = 8000) {
|
||||
return new Promise((resolve) => {
|
||||
const start = Date.now();
|
||||
|
|
@ -94,6 +104,7 @@ const $notify = {
|
|||
settle();
|
||||
});
|
||||
},
|
||||
// blockUI shows the busy overlay, optionally setting its CSS class.
|
||||
blockUI: function (className) {
|
||||
const el = document.getElementById("busy-overlay");
|
||||
|
||||
|
|
@ -104,6 +115,7 @@ const $notify = {
|
|||
}
|
||||
}
|
||||
},
|
||||
// unblockUI hides the busy overlay.
|
||||
unblockUI: function () {
|
||||
const el = document.getElementById("busy-overlay");
|
||||
|
||||
|
|
@ -112,9 +124,11 @@ const $notify = {
|
|||
el.className = "";
|
||||
}
|
||||
},
|
||||
// wait shows a "Please wait" info notification.
|
||||
wait: function () {
|
||||
this.info($gettext("Please wait…"));
|
||||
},
|
||||
// busy shows a "Busy, please wait" warning notification.
|
||||
busy: function () {
|
||||
this.warn($gettext("Busy, please wait…"));
|
||||
},
|
||||
|
|
|
|||
|
|
@ -351,6 +351,8 @@ export default class Session {
|
|||
this.clearLegacyKey("session.scope");
|
||||
this.clearLegacyKey("authError");
|
||||
this.clearLegacyKey("session.error");
|
||||
this.clearLegacyKey("session.messageId");
|
||||
this.clearLegacyKey("session.messageParams");
|
||||
|
||||
delete $api.defaults.headers.common[RequestHeader];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,6 +38,8 @@ const defaultLegacyClearKeys = new Set([
|
|||
"session.provider",
|
||||
"session.scope",
|
||||
"session.error",
|
||||
"session.messageId",
|
||||
"session.messageParams",
|
||||
"clipboard",
|
||||
"clipboard.photos",
|
||||
"clipboard.albums",
|
||||
|
|
|
|||
|
|
@ -274,10 +274,24 @@ export default {
|
|||
// stashed by the auth bridge before its full-page redirect. In mounted() so
|
||||
// the notification component has already subscribed (its created() runs
|
||||
// before any mounted()).
|
||||
const messageId = getAppStorage().getItem("session.messageId");
|
||||
const authError = getAppStorage().getItem("session.error");
|
||||
if (authError) {
|
||||
if (messageId || authError) {
|
||||
let messageParams = [];
|
||||
const rawParams = getAppStorage().getItem("session.messageParams");
|
||||
if (rawParams) {
|
||||
try {
|
||||
messageParams = JSON.parse(rawParams);
|
||||
} catch (e) {
|
||||
messageParams = [];
|
||||
}
|
||||
}
|
||||
getAppStorage().removeItem("session.error");
|
||||
this.$notify.error(authError);
|
||||
getAppStorage().removeItem("session.messageId");
|
||||
getAppStorage().removeItem("session.messageParams");
|
||||
// Render in the current UI locale via the message key (notify.vue applies
|
||||
// Tp); the server-rendered string stays as the fallback.
|
||||
this.$notify.error(authError, messageId, messageParams);
|
||||
}
|
||||
},
|
||||
unmounted() {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import { describe, it } from "vitest";
|
||||
import { describe, it, expect, vi } from "vitest";
|
||||
import "../fixtures";
|
||||
import $notify from "common/notify";
|
||||
import $event from "common/event";
|
||||
|
||||
describe("common/alert", () => {
|
||||
it("should call alert.info", () => {
|
||||
|
|
@ -19,6 +20,20 @@ describe("common/alert", () => {
|
|||
$notify.success("message");
|
||||
});
|
||||
|
||||
it("error forwards an optional message id and params for UI-locale rendering", async () => {
|
||||
const handler = vi.fn();
|
||||
const token = $event.subscribe("notify.error", handler);
|
||||
$notify.error("fallback", "Registration disabled", ["x"]);
|
||||
// pubsub.js delivers asynchronously, so wait a tick before asserting.
|
||||
await new Promise((resolve) => setTimeout(resolve, 0));
|
||||
$event.unsubscribe(token);
|
||||
expect(handler).toHaveBeenCalledWith("notify.error", {
|
||||
message: "fallback",
|
||||
messageId: "Registration disabled",
|
||||
messageParams: ["x"],
|
||||
});
|
||||
});
|
||||
|
||||
it("should call wait", () => {
|
||||
$notify.wait();
|
||||
});
|
||||
|
|
|
|||
|
|
@ -173,9 +173,25 @@ describe("page/auth/login", () => {
|
|||
|
||||
const { wrapper } = mountLogin({ oidcEnabled: true });
|
||||
|
||||
// Consumed on mount so it does not reappear on the next reload.
|
||||
// Consumed on mount so it does not reappear on the next reload. With no
|
||||
// messageId, the server-rendered fallback string is surfaced as-is.
|
||||
expect(window.localStorage.getItem(`${storagePrefix}session.error`)).toBeNull();
|
||||
expect(wrapper.vm.$notify.error).toHaveBeenCalledWith("You do not have access to this instance.");
|
||||
expect(wrapper.vm.$notify.error).toHaveBeenCalledWith("You do not have access to this instance.", null, []);
|
||||
});
|
||||
|
||||
it("forwards a stored OIDC error by message key so it renders in the current UI locale", () => {
|
||||
window.localStorage.setItem(`${storagePrefix}session.error`, "Registration disabled");
|
||||
window.localStorage.setItem(`${storagePrefix}session.messageId`, "Registration disabled");
|
||||
window.localStorage.setItem(`${storagePrefix}session.messageParams`, JSON.stringify([]));
|
||||
|
||||
const { wrapper } = mountLogin({ oidcEnabled: true });
|
||||
|
||||
// All three keys are consumed on mount so the toast does not reappear.
|
||||
expect(window.localStorage.getItem(`${storagePrefix}session.error`)).toBeNull();
|
||||
expect(window.localStorage.getItem(`${storagePrefix}session.messageId`)).toBeNull();
|
||||
expect(window.localStorage.getItem(`${storagePrefix}session.messageParams`)).toBeNull();
|
||||
// The message key is forwarded so notify.vue translates it via Tp.
|
||||
expect(wrapper.vm.$notify.error).toHaveBeenCalledWith("Registration disabled", "Registration disabled", []);
|
||||
});
|
||||
|
||||
// Auto-OIDC redirect for unauthenticated visitors now lives in the
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue