change modal on error
This commit is contained in:
Generated
+1
-1
@@ -2581,7 +2581,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustpad"
|
name = "rustpad"
|
||||||
version = "0.2.39"
|
version = "0.2.40"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"argon2",
|
"argon2",
|
||||||
"aws-config",
|
"aws-config",
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "rustpad"
|
name = "rustpad"
|
||||||
version = "0.2.39"
|
version = "0.2.40"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
rust-version = "1.94"
|
rust-version = "1.94"
|
||||||
description = "Collaborative Markdown notepad built with Axum, WebSockets and SQLite, PostgreSQL and MySQL"
|
description = "Collaborative Markdown notepad built with Axum, WebSockets and SQLite, PostgreSQL and MySQL"
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ import { bindNoteFiles } from "@rustpad/note-files";
|
|||||||
import { currentShareUrl, readEditorState, writeEditorState } from "@rustpad/url-state";
|
import { currentShareUrl, readEditorState, writeEditorState } from "@rustpad/url-state";
|
||||||
import { toast } from "@rustpad/toast";
|
import { toast } from "@rustpad/toast";
|
||||||
import { getTheme } from "@rustpad/theme";
|
import { getTheme } from "@rustpad/theme";
|
||||||
|
import { isResourceAccessError } from "@rustpad/security";
|
||||||
|
|
||||||
export function startNoteEditor(adapter) {
|
export function startNoteEditor(adapter) {
|
||||||
const editor = document.querySelector("#editor"), preview = document.querySelector("#preview"), editorWorkspace = document.querySelector("#editor-workspace"), gutter = document.querySelector("#line-gutter"), ownerLabels = document.querySelector("#owner-labels"), authorshipLayer = document.querySelector("#authorship-layer");
|
const editor = document.querySelector("#editor"), preview = document.querySelector("#preview"), editorWorkspace = document.querySelector("#editor-workspace"), gutter = document.querySelector("#line-gutter"), ownerLabels = document.querySelector("#owner-labels"), authorshipLayer = document.querySelector("#authorship-layer");
|
||||||
@@ -1353,7 +1354,6 @@ export function startNoteEditor(adapter) {
|
|||||||
onError: message => {
|
onError: message => {
|
||||||
hideConnectionNotice();
|
hideConnectionNotice();
|
||||||
const friendly = /read-only access/i.test(message) ? "This note is read only. Enter the password or ask the owner to grant write access." : message;
|
const friendly = /read-only access/i.test(message) ? "This note is read only. Enter the password or ask the owner to grant write access." : message;
|
||||||
document.querySelector("#password-error").textContent = friendly;
|
|
||||||
if (/read-only access/i.test(message)) {
|
if (/read-only access/i.test(message)) {
|
||||||
toast(friendly);
|
toast(friendly);
|
||||||
accessLevel.textContent = "Access: read only";
|
accessLevel.textContent = "Access: read only";
|
||||||
@@ -1368,9 +1368,17 @@ export function startNoteEditor(adapter) {
|
|||||||
if (saveState.textContent === "Saving…") saveState.textContent = "Save failed";
|
if (saveState.textContent === "Saving…") saveState.textContent = "Save failed";
|
||||||
if (/nickname|session|account/i.test(message)) {
|
if (/nickname|session|account/i.test(message)) {
|
||||||
if (!identityDialog.open) identityDialog.showModal();
|
if (!identityDialog.open) identityDialog.showModal();
|
||||||
} else if (info?.protected && !passwordDialog.open) {
|
return;
|
||||||
passwordDialog.showModal();
|
|
||||||
}
|
}
|
||||||
|
if (info?.protected && isResourceAccessError(message)) {
|
||||||
|
resourceUnlocked = false;
|
||||||
|
setDocumentReadOnly(true, "Password required");
|
||||||
|
document.querySelector("#password-error").textContent = friendly;
|
||||||
|
if (!passwordDialog.open) passwordDialog.showModal();
|
||||||
|
document.querySelector("#open-password")?.focus();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
toast(friendly);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
socket.connect();
|
socket.connect();
|
||||||
|
|||||||
@@ -7,6 +7,14 @@
|
|||||||
* See LICENSE file in repository root for details.
|
* See LICENSE file in repository root for details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
const RESOURCE_ACCESS_ERROR_PATTERN = /(?:invalid password|password required|authentication required|access expired or revoked)/i;
|
||||||
|
|
||||||
|
export function isResourceAccessError(error) {
|
||||||
|
const status = Number(error?.status);
|
||||||
|
const message = typeof error === "string" ? error : error?.message;
|
||||||
|
return status === 401 || RESOURCE_ACCESS_ERROR_PATTERN.test(String(message || ""));
|
||||||
|
}
|
||||||
|
|
||||||
export function safeAppUrl(value, fallback = "/") {
|
export function safeAppUrl(value, fallback = "/") {
|
||||||
try {
|
try {
|
||||||
const url = new URL(String(value || ""), location.origin);
|
const url = new URL(String(value || ""), location.origin);
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ import { copyText } from "@rustpad/clipboard";
|
|||||||
import { getNickname, getAccessToken, getAuthToken, getGuestId, setAccessToken } from "@rustpad/session";
|
import { getNickname, getAccessToken, getAuthToken, getGuestId, setAccessToken } from "@rustpad/session";
|
||||||
import { bindIdentityDialog, validateCurrentSession } from "@rustpad/auth-ui";
|
import { bindIdentityDialog, validateCurrentSession } from "@rustpad/auth-ui";
|
||||||
import { askConfirm } from "@rustpad/modal";
|
import { askConfirm } from "@rustpad/modal";
|
||||||
import { safeAppUrl } from "@rustpad/security";
|
import { isResourceAccessError, safeAppUrl } from "@rustpad/security";
|
||||||
import { toast } from "@rustpad/toast";
|
import { toast } from "@rustpad/toast";
|
||||||
|
|
||||||
const parts = location.pathname.split("/").filter(Boolean);
|
const parts = location.pathname.split("/").filter(Boolean);
|
||||||
@@ -116,7 +116,8 @@ function connectWorkspaceWatch() {
|
|||||||
if (message.type === "error") {
|
if (message.type === "error") {
|
||||||
workspaceWatchIntentionalClose = true;
|
workspaceWatchIntentionalClose = true;
|
||||||
socket.close();
|
socket.close();
|
||||||
if (/password/i.test(message.message || "")) lockWorkspaceForPassword(message.message);
|
if (isResourceAccessError(message.message)) lockWorkspaceForPassword(message.message);
|
||||||
|
else document.querySelector("#workspace-error").textContent = message.message || "Workspace connection error";
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
socket.addEventListener("close", () => {
|
socket.addEventListener("close", () => {
|
||||||
@@ -234,7 +235,7 @@ async function openWorkspace() {
|
|||||||
if (dialog.open) dialog.close();
|
if (dialog.open) dialog.close();
|
||||||
connectWorkspaceWatch();
|
connectWorkspaceWatch();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (info?.protected || e.message.toLowerCase().includes("password")) {
|
if (info?.protected && isResourceAccessError(e)) {
|
||||||
lockWorkspaceForPassword(e.message);
|
lockWorkspaceForPassword(e.message);
|
||||||
} else if (e.status === 403 || e.status === 404) {
|
} else if (e.status === 403 || e.status === 404) {
|
||||||
await showSystemNotFound();
|
await showSystemNotFound();
|
||||||
|
|||||||
Reference in New Issue
Block a user