0.2.89
This commit is contained in:
1 parent
9e133628e5
commit
0abc8e871d
6 files changed
+47
-7
No files matched your search
Generated
+1
-1
@@ -2674,7 +2674,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rustpad"
|
||||
version = "0.2.88"
|
||||
version = "0.2.89"
|
||||
dependencies = [
|
||||
"argon2",
|
||||
"aws-config",
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "rustpad"
|
||||
version = "0.2.88"
|
||||
version = "0.2.89"
|
||||
edition = "2024"
|
||||
rust-version = "1.94"
|
||||
description = "Collaborative Markdown notepad built with Axum, WebSockets and SQLite, PostgreSQL and MySQL"
|
||||
|
||||
+4
-3
@@ -1830,9 +1830,10 @@ pub async fn resource_mentions(
|
||||
.await
|
||||
.map_err(AuthError::database)?;
|
||||
if access.is_none() {
|
||||
return Err(AuthError::forbidden(
|
||||
"This resource was not shared with your account.",
|
||||
));
|
||||
// A signed-in visitor can read public resources without a direct share.
|
||||
// An empty list keeps member identities private and avoids treating
|
||||
// unavailable mention suggestions as a failed page request.
|
||||
return Ok(Json(serde_json::json!({ "users": [] })));
|
||||
}
|
||||
}
|
||||
let rows = sqlx::query(queries::get(state.db.kind(), query))
|
||||
|
||||
+3
-1
@@ -164,7 +164,9 @@ export async function api(path, options = {}) {
|
||||
logWarn("api.timeout", { method: options.method || "GET", path });
|
||||
throw new Error(translateApiMessage("Timed out"));
|
||||
}
|
||||
logError("api.network_error", error, { method: options.method || "GET", path });
|
||||
// HTTP failures have already been logged as api.failed above.
|
||||
// Only transport failures should be reported as network errors.
|
||||
if (!error?.status) logError("api.network_error", error, { method: options.method || "GET", path });
|
||||
if (options.body instanceof FormData && error instanceof TypeError) {
|
||||
throw new Error(translateApiMessage("Upload failed before the server returned a response. The file may exceed the server or proxy upload limit."));
|
||||
}
|
||||
|
||||
@@ -277,7 +277,9 @@ export function startNoteEditor(adapter) {
|
||||
setMarkdownMentions([]);
|
||||
mentionAutocomplete.setUsers([]);
|
||||
mentionNavigator.setUsers([]);
|
||||
if (!accountSession) { render(); return; }
|
||||
// Session validation can finish before loadNoteInfo(); avoid a premature
|
||||
// request (and a second request when note info becomes available).
|
||||
if (!accountSession || !info) { render(); return; }
|
||||
try {
|
||||
const params = new URLSearchParams({ kind: adapter.access.kind, slug: adapter.access.key });
|
||||
const response = await api(`/api/auth/resources/mentions?${params}`);
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
import assert from 'node:assert/strict';
|
||||
import { readFileSync } from 'node:fs';
|
||||
|
||||
const events = globalThis.__apiEvents = [];
|
||||
let source = readFileSync('static/js/api.js', 'utf8')
|
||||
.replace('import { logDebug, logError, logWarn } from "@rustpad/logger";',
|
||||
'const logDebug = () => {}; const logError = (...args) => globalThis.__apiEvents.push(["error", ...args]); const logWarn = (...args) => globalThis.__apiEvents.push(["warn", ...args]);')
|
||||
.replace('import { formatNumber, restoreGuestLanguage, translateApiMessage } from "@rustpad/i18n";',
|
||||
'const formatNumber = value => String(value); const restoreGuestLanguage = () => {}; const translateApiMessage = message => message;')
|
||||
.replace('import { restoreGuestTheme } from "@rustpad/theme";',
|
||||
'const restoreGuestTheme = () => {};');
|
||||
const { api } = await import('data:text/javascript;charset=utf-8,' + encodeURIComponent(source));
|
||||
|
||||
globalThis.window = {};
|
||||
globalThis.fetch = async () => new Response(JSON.stringify({ error: 'Access denied.' }), {
|
||||
status: 403,
|
||||
headers: { 'content-type': 'application/json' },
|
||||
});
|
||||
await assert.rejects(api('/api/auth/resources/mentions?kind=pad&slug=test'), error => error.status === 403);
|
||||
assert.equal(events.filter(([level, name]) => level === 'warn' && name === 'api.failed').length, 1);
|
||||
assert.equal(events.filter(([level, name]) => level === 'error' && name === 'api.network_error').length, 0);
|
||||
|
||||
events.length = 0;
|
||||
globalThis.fetch = async () => new Response(JSON.stringify({ users: [] }), {
|
||||
status: 200,
|
||||
headers: { 'content-type': 'application/json' },
|
||||
});
|
||||
assert.deepEqual(await api('/api/auth/resources/mentions?kind=pad&slug=test'), { users: [] });
|
||||
assert.equal(events.length, 0);
|
||||
|
||||
events.length = 0;
|
||||
globalThis.fetch = async () => { throw new TypeError('Network down'); };
|
||||
await assert.rejects(api('/api/auth/resources/mentions?kind=pad&slug=test'), TypeError);
|
||||
assert.equal(events.filter(([level, name]) => level === 'error' && name === 'api.network_error').length, 1);
|
||||
console.log('PASS: HTTP 403 logged once, empty mentions succeeds, transport failures logged as network errors');
|
||||
Reference in new issue
Block a user