Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 8 additions & 6 deletions backend/src/api/skins_routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -797,23 +797,25 @@ def get_thumbnail(
_OWNER_HIDDEN_FILES = frozenset({"meta.json", "review_sheet.png", "name_preview.png"})


@skins_router.get("/submissions/{submission_id}/files/{filename}")
# The name is a query parameter: Cloudflare caches paths ending in .png by URL alone,
# which would hand one response (a 401 or 404 included) to every later request.
@skins_router.get("/submissions/{submission_id}/files")
def owner_file(
submission_id: str,
filename: str,
name: str,
authorization: str | None = Header(default=None),
):
"""A texture or model the owner uploaded, for the 3D viewer on their skin's page."""
session = _session_from_auth(authorization)
name = filename.lower()
if name in _OWNER_HIDDEN_FILES or not name.endswith(_OWNER_FILE_SUFFIXES):
lowered = name.lower()
if lowered in _OWNER_HIDDEN_FILES or not lowered.endswith(_OWNER_FILE_SUFFIXES):
raise HTTPException(status_code=404, detail="File not found")
if get_submission_for_owner(submission_id, session["player_uuid"]) is None:
raise HTTPException(status_code=404, detail="Submission not found")
path = _resolve_submission_file_safely(submission_id, filename)
path = _resolve_submission_file_safely(submission_id, name)
if path is None:
raise HTTPException(status_code=404, detail="File not found")
media = "image/png" if name.endswith(".png") else "application/json"
media = "image/png" if lowered.endswith(".png") else "application/json"
return FileResponse(path, media_type=media, headers={"Cache-Control": "private, max-age=300"})


Expand Down
2 changes: 1 addition & 1 deletion backend/src/api/test_skin_owner_files.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
"""GET /skins/submissions/{id}/files/{name} serves a skin's uploads to its owner only."""
"""GET /skins/submissions/{id}/files?name= serves a skin's uploads to its owner only."""

from __future__ import annotations

Expand Down
2 changes: 1 addition & 1 deletion frontend/lib/skins/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -431,7 +431,7 @@ export async function getSkinThumbnail(id: string, sessionToken: string): Promis
/** One of the owner's uploaded textures or models, or null when the skin has no such file. */
export async function getSubmissionFile(id: string, filename: string, sessionToken: string): Promise<File | null> {
const res = await apiFetch(
`${getApiBase()}/skins/submissions/${encodeURIComponent(id)}/files/${encodeURIComponent(filename)}`,
`${getApiBase()}/skins/submissions/${encodeURIComponent(id)}/files?name=${encodeURIComponent(filename)}`,
{ headers: { Authorization: `Bearer ${sessionToken}` } },
);
if (!res.ok) return null;
Expand Down
Loading