diff --git a/config.py b/config.py index f44aa7f..455a968 100644 --- a/config.py +++ b/config.py @@ -4,10 +4,10 @@ from pathlib import Path load_dotenv() -DEBUG = True +DEBUG = False BASE_URL = "https://xiaobei.yinghuaonline.com" -HTTPX_TIMEOUT = 10 +HTTPX_TIMEOUT = 30 DEFAULT_USER_AGENT = "Mozilla/5.0 (iPhone; CPU iPhone OS 16_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 Html5Plus/1.0 (Immersed/20) uni-app" BASE_DIR = Path(__file__).resolve().parent diff --git a/exceptions/unauthorized.py b/exceptions/unauthorized.py new file mode 100644 index 0000000..cbd8834 --- /dev/null +++ b/exceptions/unauthorized.py @@ -0,0 +1,5 @@ +# exceptions.py +class UnauthorizedError(Exception): + def __init__(self, message="登录已失效"): + self.message = message + super().__init__(message) diff --git a/routers/auth.py b/routers/auth.py index 6be6f01..e18de8b 100644 --- a/routers/auth.py +++ b/routers/auth.py @@ -1,12 +1,14 @@ from datetime import datetime +from pathlib import Path from fastapi import APIRouter, Request, Response from fastapi.responses import JSONResponse, RedirectResponse from models import User from services.services import XBXS from starlette.templating import Jinja2Templates +from config import BASE_DIR router = APIRouter() -templates = Jinja2Templates(directory="templates") +templates = Jinja2Templates(directory=str(BASE_DIR / "templates")) @router.get("/") @@ -23,6 +25,20 @@ async def index(request: Request): ) +@router.get("/logout") +async def logout(request: Request): + token = request.cookies.get("xbxs_token") + if token: + request.cookies.clear() + return templates.TemplateResponse( + "login.html", + { + "request": request, + "year": datetime.now().year, + }, + ) + + @router.post("/login") async def login(user: User, response: Response): xbxs = XBXS() diff --git a/routers/home.py b/routers/home.py index ae8f36d..c0ea464 100644 --- a/routers/home.py +++ b/routers/home.py @@ -1,6 +1,7 @@ from datetime import datetime import json import os +from pathlib import Path from typing import List from uuid import uuid4 from fastapi import ( @@ -16,9 +17,11 @@ from fastapi import ( from fastapi.responses import HTMLResponse, RedirectResponse from starlette.templating import Jinja2Templates from services.services import XBXS +from config import BASE_DIR +from exceptions.unauthorized import UnauthorizedError router = APIRouter() -templates = Jinja2Templates(directory="templates") +templates = Jinja2Templates(directory=str(BASE_DIR / "templates")) def get_xbxs(request: Request) -> XBXS: @@ -53,16 +56,21 @@ async def profile( request: Request, xbxs: XBXS = Depends(get_xbxs), ): - student = await xbxs.get_student_info_cached() - - return templates.TemplateResponse( - "profile.html", - { - "request": request, - "student": student, - "active": "profile", - }, - ) + try: + student = await xbxs.get_student_info_cached() + return templates.TemplateResponse( + "profile.html", + { + "request": request, + "student": student, + "active": "profile", + }, + ) + except UnauthorizedError as e: + raise HTTPException( + status_code=401, + detail=e.message, + ) @router.get("/sign") @@ -70,7 +78,13 @@ async def sign_list( request: Request, xbxs: XBXS = Depends(get_xbxs), ): - result = await xbxs.get_know_list() + try: + result = await xbxs.get_know_list() + except UnauthorizedError as e: + raise HTTPException( + status_code=401, + detail=e.message, + ) rows = result.get("rows", []) @@ -98,7 +112,13 @@ async def sign_detail( request: Request, xbxs: XBXS = Depends(get_xbxs), ): - know_detail = await xbxs.get_know(str(knowing_id)) + try: + know_detail = await xbxs.get_know(str(knowing_id)) + except UnauthorizedError as e: + raise HTTPException( + status_code=401, + detail=e.message, + ) data = know_detail.get("data", {}) # 获取具体的数据 @@ -147,10 +167,13 @@ async def complete_sign( if not remark: raise HTTPException(status_code=400, detail="备注是必填项") - print(location, remark, know_id, images, address) - try: await xbxs.update_student_know(str(know_id), remark, address, location, images) + except UnauthorizedError as e: + raise HTTPException( + status_code=401, + detail=e.message, + ) except Exception as e: raise HTTPException(status_code=500, detail="保存签到数据失败") diff --git a/services/services.py b/services/services.py index 6827483..4e2a46d 100644 --- a/services/services.py +++ b/services/services.py @@ -5,6 +5,7 @@ from fastapi import UploadFile import httpx import config from services.student_cache import get_student_by_token, save_student +from exceptions.unauthorized import UnauthorizedError SEX_MAP = { @@ -22,8 +23,18 @@ class XBXS: self.token = token self.client = httpx.AsyncClient( + trust_env=False, base_url=config.BASE_URL, - timeout=config.HTTPX_TIMEOUT, + timeout=httpx.Timeout( + connect=10.0, + read=30.0, + write=30.0, + pool=10.0, + ), + limits=httpx.Limits( + max_connections=1, # 每个请求使用单独连接,不使用连接池 + max_keepalive_connections=1, + ), headers={ "user-agent": UA, "accept": "*/*", @@ -74,6 +85,8 @@ class XBXS: resp = await self.client.get("/xiaobei-api/getInfo") resp.raise_for_status() result = resp.json() + if result.get("code") == "401": + raise UnauthorizedError("token 已失效,请重新登录") return { "userName": result.get("user", {}).get("userName", ""), "nickName": result.get("user", {}).get("nickName", ""), @@ -92,6 +105,9 @@ class XBXS: sex = result.get("data", {}).get("studentSex", "") sex_text = SEX_MAP.get(sex, "未知") + if result.get("code") == "401": + raise UnauthorizedError("token 已失效,请重新登录") + return { "id": result.get("data", {}).get("id", ""), "studentNumber": result.get("data", {}).get("studentNumber", ""), @@ -114,18 +130,30 @@ class XBXS: resp = await self.client.get("/xiaobei-api/student/deptId") resp.raise_for_status() result = resp.json() + + if result.get("code") == "401": + raise UnauthorizedError("token 已失效,请重新登录") + return result async def get_user_sig2(self): resp = await self.client.get("/xiaobei-api/trtc/genUserSig2") resp.raise_for_status() result = resp.json() + + if result.get("code") == "401": + raise UnauthorizedError("token 已失效,请重新登录") + return result async def get_know_list(self): resp = await self.client.get("/xiaobei-api/student/know/list") resp.raise_for_status() result = resp.json() + + if result.get("code") == "401": + raise UnauthorizedError("token 已失效,请重新登录") + if result.get("total", 0) > 0: pass return result @@ -134,6 +162,10 @@ class XBXS: resp = await self.client.get(f"/xiaobei-api/student/know/{know}") resp.raise_for_status() result = resp.json() + + if result.get("code") == "401": + raise UnauthorizedError("token 已失效,请重新登录") + if result.get("total", 0) > 0: pass return result @@ -146,14 +178,20 @@ class XBXS: location: str, images: List[UploadFile], ): - files = {} + files = [] + for i, image in enumerate(images): - # 为每个图片生成一个唯一的 UUID 文件名 - files[f"file{i}"] = ( - str(uuid4()), # 这里使用 UUID 作为文件名 - await image.read(), - "image/jpeg", # 假设是 jpeg 格式,按需修改 + files.append( + ( + f"file{i}", + ( + f"{uuid4()}.jpg", + image.file, + image.content_type or "image/jpeg", + ), + ) ) + resp = await self.client.post( "/xiaobei-api/student/know/updateStudentKnow", data={ @@ -161,7 +199,6 @@ class XBXS: "remark": remark, "knowingId": knowingID, "location": location, - # "location": "106.5346788194445:29.343291015625", "size": len(images), }, files=files, diff --git a/services/student_cache.py b/services/student_cache.py index a695026..11ea4ec 100644 --- a/services/student_cache.py +++ b/services/student_cache.py @@ -1,9 +1,7 @@ import time import json import sqlite3 - -DB_PATH = "data.db" - +from config import DB_PATH def get_student_by_token(token: str): conn = sqlite3.connect(DB_PATH) diff --git a/templates/sign_detail.html b/templates/sign_detail.html index d8138b2..7fea90c 100644 --- a/templates/sign_detail.html +++ b/templates/sign_detail.html @@ -53,8 +53,6 @@ - - {% if is_finish_know == 0 and is_check == 0 %}