fix(frontend,backend): all stories panel groups by peer again, missing trashed file answers 404, access log hides the token

This commit is contained in:
hh committed 2026-10-14 03:53:56 +02:00
1 parent 897421e7c4
commit 09c5f78061
5 files changed
+28 -16

No files matched your search

+9 -5
View File
@@ -1,19 +1,23 @@
import logging import logging
import re
QUERYLESS_PREFIXES = ("/mcp",) QUERYLESS_PREFIXES = ("/mcp",)
_PATH_ARG = 2 _PATH_ARG = 2
_TOKEN = re.compile(r"(?<=[?&]token=)[^&]+")
class DropQueryFilter(logging.Filter): class ScrubQueryFilter(logging.Filter):
def filter(self, record: logging.LogRecord) -> bool: def filter(self, record: logging.LogRecord) -> bool:
args = record.args args = record.args
if isinstance(args, tuple) and len(args) > _PATH_ARG: if isinstance(args, tuple) and len(args) > _PATH_ARG:
path = str(args[_PATH_ARG]) path = str(args[_PATH_ARG])
if path.startswith(QUERYLESS_PREFIXES): if path.startswith(QUERYLESS_PREFIXES):
bare = path.partition("?")[0] path = path.partition("?")[0]
record.args = (*args[:_PATH_ARG], bare, *args[_PATH_ARG + 1 :]) else:
path = _TOKEN.sub("***", path)
record.args = (*args[:_PATH_ARG], path, *args[_PATH_ARG + 1 :])
return True return True
def hide_mcp_query() -> None: def scrub_access_log() -> None:
logging.getLogger("uvicorn.access").addFilter(DropQueryFilter()) logging.getLogger("uvicorn.access").addFilter(ScrubQueryFilter())
+2 -2
View File
@@ -10,7 +10,7 @@ from fastapi.responses import FileResponse, RedirectResponse
from fastmcp.utilities.lifespan import combine_lifespans from fastmcp.utilities.lifespan import combine_lifespans
from starlette.applications import Starlette from starlette.applications import Starlette
from api.access_log import hide_mcp_query from api.access_log import scrub_access_log
from api.auth import BearerAuthMiddleware from api.auth import BearerAuthMiddleware
from api.mcp.server import mcp from api.mcp.server import mcp
from api.realtime import hub from api.realtime import hub
@@ -50,7 +50,7 @@ if env.auth.token is None:
raise RuntimeError(msg) raise RuntimeError(msg)
_token = env.auth.token.get_secret_value() _token = env.auth.token.get_secret_value()
hide_mcp_query() scrub_access_log()
mcp_app = mcp.http_app(path="/") mcp_app = mcp.http_app(path="/")
+1 -1
View File
@@ -98,7 +98,7 @@ async def serve_trashed(
storage_key: str, storage_key: str,
) -> FileResponse: ) -> FileResponse:
item = await storage_read.get_trash_item(pool, storage_key) item = await storage_read.get_trash_item(pool, storage_key)
if item is None: if item is None or not storage.trashed(storage_key):
raise HTTPException(status_code=404, detail="file is not in trash") raise HTTPException(status_code=404, detail="file is not in trash")
return FileResponse( return FileResponse(
storage.trash_url(storage_key), storage.trash_url(storage_key),
+3
View File
@@ -53,5 +53,8 @@ class ContentAddressedStorage:
def purge(self, key: str) -> None: def purge(self, key: str) -> None:
self._trash_path(key).unlink(missing_ok=True) self._trash_path(key).unlink(missing_ok=True)
def trashed(self, key: str) -> bool:
return self._trash_path(key).is_file()
def trash_url(self, key: str) -> str: def trash_url(self, key: str) -> str:
return str(self._trash_path(key)) return str(self._trash_path(key))
@@ -32,10 +32,14 @@
let groups = $state<Group[]>([]); let groups = $state<Group[]>([]);
let loading = $state(false); let loading = $state(false);
const previews = $state<Record<number, string | null>>({}); const previews = $state<Record<string, string | null>>({});
const expanded = $state<Record<number, boolean>>({}); const expanded = $state<Record<number, boolean>>({});
let token = 0; let token = 0;
function previewKey(story: StoryView): string {
return `${story.peer_id}:${story.story_id}`;
}
let viewerOpen = $state(false); let viewerOpen = $state(false);
let viewerIndex = $state(0); let viewerIndex = $state(0);
let viewerItems = $state<StoryView[]>([]); let viewerItems = $state<StoryView[]>([]);
@@ -78,7 +82,7 @@
kind: "peer" as const, kind: "peer" as const,
name: peerName(peer) || `ID ${peerId}`, name: peerName(peer) || `ID ${peerId}`,
peerId, peerId,
stories, stories: peerStories,
}; };
} }
const chat = chatById.get(peerId); const chat = chatById.get(peerId);
@@ -87,7 +91,7 @@
kind: "chat" as const, kind: "chat" as const,
name: chat?.title ?? `Chat ${peerId}`, name: chat?.title ?? `Chat ${peerId}`,
peerId, peerId,
stories, stories: peerStories,
}; };
}); });
} finally { } finally {
@@ -106,7 +110,7 @@
groups = []; groups = [];
loading = false; loading = false;
for (const key of Object.keys(previews)) { for (const key of Object.keys(previews)) {
delete previews[Number(key)]; delete previews[key];
} }
for (const key of Object.keys(expanded)) { for (const key of Object.keys(expanded)) {
delete expanded[Number(key)]; delete expanded[Number(key)];
@@ -122,13 +126,14 @@
let active = true; let active = true;
untrack(() => { untrack(() => {
for (const item of list) { for (const item of list) {
if (!item.downloaded || previews[item.story_id] !== undefined) { const key = previewKey(item);
if (!item.downloaded || previews[key] !== undefined) {
continue; continue;
} }
previews[item.story_id] = null; previews[key] = null;
loadStoryMedia(item.peer_id, item.story_id).then((url) => { loadStoryMedia(item.peer_id, item.story_id).then((url) => {
if (active) { if (active) {
previews[item.story_id] = url; previews[key] = url;
} }
}); });
} }
@@ -210,7 +215,7 @@
{#each group.stories as item, index (item.story_id)} {#each group.stories as item, index (item.story_id)}
<StoryTile <StoryTile
onopen={() => openViewer(group, index)} onopen={() => openViewer(group, index)}
preview={previews[item.story_id]} preview={previews[previewKey(item)]}
story={item} story={item}
/> />
{/each} {/each}