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:
1 parent
897421e7c4
commit
09c5f78061
5 files changed
+28
-16
No files matched your search
@@ -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())
|
||||||
@@ -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="/")
|
||||||
|
|
||||||
|
|||||||
@@ -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),
|
||||||
|
|||||||
@@ -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}
|
||||||
|
|||||||
Reference in new issue
Block a user