Skip to content

Commit 8313fcf

Browse files
authored
Merge pull request #85 from shinonomeow/shino_aio
fix poster api error
2 parents 47d90f2 + 9e09f23 commit 8313fcf

2 files changed

Lines changed: 50 additions & 52 deletions

File tree

backend/src/main.py

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,16 @@
11
import logging
22
import os
3+
from pathlib import Path
34

45
import uvicorn
5-
from fastapi import FastAPI, Request
6+
from fastapi import Depends, FastAPI, HTTPException, Request
67
from fastapi.responses import FileResponse, RedirectResponse
78
from fastapi.staticfiles import StaticFiles
89
from fastapi.templating import Jinja2Templates
910
from module.api import lifespan, v1
1011
from module.conf import VERSION, settings, setup_logger
12+
from module.network import load_image
13+
from module.security.api import get_current_user
1114

1215
setup_logger(reset=True)
1316
logger = logging.getLogger(__name__)
@@ -38,6 +41,51 @@ def create_app() -> FastAPI:
3841
app = create_app()
3942

4043

44+
@app.get("/posters/{path:path}", dependencies=[Depends(get_current_user)])
45+
async def get_poster(path: str):
46+
"""
47+
安全的poster图片访问端点
48+
- 添加了用户鉴权
49+
- 防止路径遍历攻击
50+
- 限制只能访问posters目录下的文件
51+
"""
52+
# 验证路径安全性 - 阻止路径遍历
53+
if ".." in path or path.startswith("/") or "\\" in path:
54+
logger.warning(f"[Poster] Blocked path traversal attempt: {path}")
55+
raise HTTPException(status_code=400, detail="Invalid path")
56+
57+
# 构建安全的文件路径
58+
poster_dir = Path("data/posters")
59+
post_path = poster_dir / Path(path)
60+
61+
# 确保解析后的路径仍在预期目录内
62+
try:
63+
post_path.resolve().relative_to(poster_dir.resolve())
64+
except ValueError:
65+
logger.warning(f"[Poster] Path outside allowed directory: {path}")
66+
raise HTTPException(status_code=400, detail="Path outside allowed directory")
67+
68+
logger.debug(f"[Poster] Accessing poster: {post_path}")
69+
70+
# 如果文件不存在,尝试下载
71+
if not post_path.exists():
72+
try:
73+
await load_image(path)
74+
except Exception as e:
75+
logger.warning(f"[Poster] Failed to load image {path}: {e}")
76+
77+
# 返回文件
78+
if post_path.exists() and post_path.is_file():
79+
return FileResponse(
80+
post_path,
81+
media_type="image/jpeg",
82+
headers={"Cache-Control": "public, max-age=86400"}, # 缓存1天
83+
)
84+
else:
85+
logger.warning(f"[Poster] File not found: {post_path}")
86+
raise HTTPException(status_code=404, detail="Poster not found")
87+
88+
4189
if VERSION != "DEV_VERSION":
4290
app.mount("/assets", StaticFiles(directory="dist/assets"), name="assets")
4391
app.mount("/images", StaticFiles(directory="dist/images"), name="images")

backend/src/module/api/bangumi.py

Lines changed: 1 addition & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,12 @@
11
import logging
2-
from pathlib import Path
32

43
from fastapi import APIRouter, Depends, HTTPException
5-
from fastapi.responses import FileResponse, JSONResponse
4+
from fastapi.responses import JSONResponse
65
from sqlalchemy.util.concurrency import asyncio
76

87
from module.database import Database, engine
98
from module.manager import BangumiManager
109
from module.models import APIResponse, Bangumi, BangumiUpdate, ResponseModel
11-
from module.network import load_image
1210
from module.security.api import get_current_user
1311

1412
from .response import u_response
@@ -17,11 +15,6 @@
1715
logger = logging.getLogger(__name__)
1816

1917

20-
# def str_to_list(data: Bangumi):
21-
# data.exclude_filter = data.exclude_filter.split(",") if data.exclude_filter else []
22-
# data.include_filter = data.include_filter.split(",") if data.include_filter else []
23-
# data.rss_link = data.rss_link.split(",") if data.rss_link else []
24-
# return data
2518

2619

2720
@router.get(
@@ -233,46 +226,3 @@ async def reset_all():
233226
)
234227

235228

236-
@router.get("/poster/{path:path}", dependencies=[Depends(get_current_user)])
237-
async def get_poster(path: str):
238-
"""
239-
安全的poster图片访问端点
240-
- 添加了用户鉴权
241-
- 防止路径遍历攻击
242-
- 限制只能访问posters目录下的文件
243-
"""
244-
# 验证路径安全性 - 阻止路径遍历
245-
if ".." in path or path.startswith("/") or "\\" in path:
246-
logger.warning(f"[Poster] Blocked path traversal attempt: {path}")
247-
raise HTTPException(status_code=400, detail="Invalid path")
248-
249-
# 构建安全的文件路径
250-
poster_dir = Path("data/posters")
251-
post_path = poster_dir / Path(path)
252-
253-
# 确保解析后的路径仍在预期目录内
254-
try:
255-
post_path.resolve().relative_to(poster_dir.resolve())
256-
except ValueError:
257-
logger.warning(f"[Poster] Path outside allowed directory: {path}")
258-
raise HTTPException(status_code=400, detail="Path outside allowed directory")
259-
260-
logger.debug(f"[Poster] Accessing poster: {post_path}")
261-
262-
# 如果文件不存在,尝试下载
263-
if not post_path.exists():
264-
try:
265-
await load_image(path)
266-
except Exception as e:
267-
logger.warning(f"[Poster] Failed to load image {path}: {e}")
268-
269-
# 返回文件
270-
if post_path.exists() and post_path.is_file():
271-
return FileResponse(
272-
post_path,
273-
media_type="image/jpeg",
274-
headers={"Cache-Control": "public, max-age=86400"}, # 缓存1天
275-
)
276-
else:
277-
logger.warning(f"[Poster] File not found: {post_path}")
278-
raise HTTPException(status_code=404, detail="Poster not found")

0 commit comments

Comments
 (0)