security upgrade
This commit is contained in:
+2
-1
@@ -11,7 +11,7 @@ import { installGlobalDiagnostics, logInfo } from "@rustpad/logger";
|
||||
installGlobalDiagnostics();
|
||||
|
||||
import { bindIdentityDialog, handleAccountActionToken, handleAccountConfirmationToken, handleResetToken, handleShareInvitationToken, logoutCurrentSession, validateCurrentSession } from "@rustpad/auth-ui";
|
||||
import { getAuthToken, setAccessToken } from "@rustpad/session";
|
||||
import { getAuthToken, getGuestId, setAccessToken } from "@rustpad/session";
|
||||
import { api } from "@rustpad/api";
|
||||
import { copyText } from "@rustpad/clipboard";
|
||||
import { safeAppUrl } from "@rustpad/security";
|
||||
@@ -38,6 +38,7 @@ function setBusy(button, busy, idleText, busyText) {
|
||||
button.textContent = busy ? busyText : idleText;
|
||||
}
|
||||
|
||||
getGuestId();
|
||||
bindPreview("#pad-name", "#pad-slug-preview", "#pad-name-count", "/p/", "note");
|
||||
bindPreview("#workspace-name", "#workspace-slug-preview", "#workspace-name-count", "/w/", "workspace");
|
||||
|
||||
|
||||
@@ -612,7 +612,9 @@ export function startNoteEditor(adapter) {
|
||||
}
|
||||
|
||||
const { loadFiles } = bindNoteFiles({
|
||||
editor, toast, getAccessToken: () => accessToken, canDelete: () => Boolean(info?.can_delete_files),
|
||||
editor, toast, getAccessToken: () => accessToken,
|
||||
canDelete: () => Boolean(info?.can_delete_files),
|
||||
canUpload: () => Boolean(info?.can_upload_files),
|
||||
endpoints: adapter.fileEndpoints,
|
||||
onFilesChanged: files => updateMarkdownFiles(files, { rerender: true }),
|
||||
});
|
||||
@@ -897,7 +899,7 @@ export function startNoteEditor(adapter) {
|
||||
}
|
||||
|
||||
function scheduleEditorSettingsSave({ personal = false, authorship = false } = {}) {
|
||||
if (personal) pendingPersonalSettingsSave = true;
|
||||
if (personal && info?.personal_editor_settings) pendingPersonalSettingsSave = true;
|
||||
if (authorship && info?.can_manage_authorship) pendingAuthorshipSettingsSave = true;
|
||||
if (!info?.can_save_editor_settings || (!pendingPersonalSettingsSave && !pendingAuthorshipSettingsSave)) return;
|
||||
clearTimeout(editorSettingsSaveTimer);
|
||||
@@ -1141,7 +1143,7 @@ export function startNoteEditor(adapter) {
|
||||
document.querySelector("#open-password")?.focus();
|
||||
}
|
||||
});
|
||||
document.querySelector("#password-form").addEventListener("submit", async e => { e.preventDefault(); try { password = document.querySelector("#open-password").value; const result = await adapter.requestAccess(password); setAccessToken(adapter.access.kind, adapter.access.key, result.granted); accessToken = getAccessToken(adapter.access.kind, adapter.access.key); password = ""; document.querySelector("#open-password").value = ""; document.querySelector("#password-error").textContent = ""; loadFiles(); connect(); } catch (error) { document.querySelector("#password-error").textContent = error.message; } });
|
||||
document.querySelector("#password-form").addEventListener("submit", async e => { e.preventDefault(); try { password = document.querySelector("#open-password").value; const result = await adapter.requestAccess(password); setAccessToken(adapter.access.kind, adapter.access.key, result.granted); accessToken = getAccessToken(adapter.access.kind, adapter.access.key); password = ""; document.querySelector("#open-password").value = ""; document.querySelector("#password-error").textContent = ""; await loadNoteInfo(); loadFiles(); connect(); } catch (error) { document.querySelector("#password-error").textContent = error.message; } });
|
||||
const historyPanel = document.querySelector("#history-panel"); document.querySelector("#history-button").addEventListener("click", async () => { if (info?.protected && !resourceUnlocked) { if (!passwordDialog.open) passwordDialog.showModal(); document.querySelector("#open-password")?.focus(); return; } historyPanel.classList.add("open"); historyPanel.setAttribute("aria-hidden", "false"); document.body.classList.add("history-open"); const list = document.querySelector("#history-list"); list.innerHTML = '<p class="empty">Loading…</p>'; try { const revisions = await adapter.loadHistory(accessToken); list.innerHTML = revisions.length ? revisions.map((r, i) => { const snippet = escapeHtml(r.content.trim().split("\n").slice(0, 3).join(" · ").slice(0, 150) || "Empty note"); const author = r.author || "Unknown author"; return `<article class="revision"><span class="revision__marker" style="--owner:${colorFor(author)}"></span><div><div class="revision__meta"><strong>${escapeHtml(author)}</strong><time>${formatDate(r.created_at)}</time></div><p class="revision__snippet">${snippet}</p><button data-preview="${r.id}">Preview</button><button data-revision="${r.id}">Restore</button><div class="revision__preview" id="preview-${r.id}" hidden></div></div></article>`; }).join("") : '<p class="empty">No history yet.</p>'; for (const r of revisions) { list.querySelector(`[data-preview="${r.id}"]`)?.addEventListener("click", () => { const el = list.querySelector(`#preview-${r.id}`); el.hidden = !el.hidden; el.textContent = r.content; }); list.querySelector(`[data-revision="${r.id}"]`)?.addEventListener("click", async () => { await adapter.restoreRevision(r.id, accessToken); toast("Version restored"); }); } } catch (e) { list.innerHTML = `<p class="error">${escapeHtml(e.message)}</p>`; } }); document.querySelector("#close-history").addEventListener("click", () => { historyPanel.classList.remove("open"); historyPanel.setAttribute("aria-hidden", "true"); document.body.classList.remove("history-open"); });
|
||||
|
||||
const deleteNoteButton = document.querySelector("#delete-note"); if (deleteNoteButton && adapter.deleteNote) deleteNoteButton.addEventListener("click", async () => { try { await adapter.deleteNote(info, accessToken); } catch (error) { toast(error.message); } });
|
||||
|
||||
@@ -11,7 +11,6 @@ import { api, uploadWithProgress } from "@rustpad/api";
|
||||
import { copyText } from "@rustpad/clipboard";
|
||||
import { prepareImageFile } from "@rustpad/image-upload";
|
||||
import { askConfirm } from "@rustpad/modal";
|
||||
import { getAuthToken } from "@rustpad/session";
|
||||
import { safeAppUrl } from "@rustpad/security";
|
||||
import { createUploadToast } from "@rustpad/toast";
|
||||
|
||||
@@ -41,7 +40,7 @@ function markdownCode(url, label, mimeType) {
|
||||
return String(mimeType || "").startsWith("image/") ? `` : `[${label}](${url})`;
|
||||
}
|
||||
|
||||
export function bindNoteFiles({ editor, endpoints, getAccessToken, canDelete, toast, onFilesChanged = () => {} }) {
|
||||
export function bindNoteFiles({ editor, endpoints, getAccessToken, canDelete, canUpload, toast, onFilesChanged = () => {} }) {
|
||||
const dialog = document.querySelector("#files-dialog");
|
||||
const list = document.querySelector("#files-list");
|
||||
const input = document.querySelector("#file-input");
|
||||
@@ -75,8 +74,8 @@ export function bindNoteFiles({ editor, endpoints, getAccessToken, canDelete, to
|
||||
}
|
||||
|
||||
document.querySelector("#upload-button").addEventListener("click", () => {
|
||||
if (!getAuthToken() && !getAccessToken()) {
|
||||
toast("Log in or use a read-write share link to upload files.");
|
||||
if (!canUpload()) {
|
||||
toast("Log in with read-write access to upload files.");
|
||||
return;
|
||||
}
|
||||
input.click();
|
||||
|
||||
@@ -12,7 +12,7 @@ installGlobalDiagnostics();
|
||||
|
||||
import { api } from "@rustpad/api";
|
||||
import { copyText } from "@rustpad/clipboard";
|
||||
import { getNickname, getAccessToken, getAuthToken, setAccessToken } from "@rustpad/session";
|
||||
import { getNickname, getAccessToken, getAuthToken, getGuestId, setAccessToken } from "@rustpad/session";
|
||||
import { bindIdentityDialog, validateCurrentSession } from "@rustpad/auth-ui";
|
||||
import { askConfirm } from "@rustpad/modal";
|
||||
import { safeAppUrl } from "@rustpad/security";
|
||||
@@ -24,6 +24,7 @@ let info;
|
||||
const shareToken = new URLSearchParams(location.search).get("share");
|
||||
let accessToken = shareToken || getAccessToken("workspace", slug);
|
||||
let nickname = getNickname();
|
||||
getGuestId();
|
||||
const dialog = document.querySelector("#password-dialog");
|
||||
const identityDialog = document.querySelector("#identity-dialog");
|
||||
const workspaceContent = document.querySelector("#workspace-content");
|
||||
|
||||
Reference in New Issue
Block a user