135 lines
4.3 KiB
Python
135 lines
4.3 KiB
Python
from fastapi import APIRouter, Header, HTTPException
|
|
|
|
from app.schemas import (
|
|
AuthLogin, AuthRegister, AuthResponse, Destination,
|
|
FavoriteRequest, FavoriteResponse, ProfileStats, UserProfile,
|
|
UserPlanPayload,
|
|
)
|
|
from app.services.auth import (
|
|
get_favorites, get_plan, get_user_by_token,
|
|
login_demo, login_user, register_user, save_plan, toggle_favorite,
|
|
)
|
|
from app.services.pocketbase import pb
|
|
|
|
router = APIRouter(prefix="/auth", tags=["auth"])
|
|
|
|
|
|
@router.post("/register", response_model=AuthResponse)
|
|
async def register(body: AuthRegister):
|
|
result = register_user(body.email, body.password, body.name)
|
|
if not result:
|
|
raise HTTPException(400, "该邮箱已注册")
|
|
return AuthResponse(**result)
|
|
|
|
|
|
@router.post("/login", response_model=AuthResponse)
|
|
async def login(body: AuthLogin):
|
|
result = login_user(body.email, body.password)
|
|
if not result:
|
|
raise HTTPException(401, "邮箱或密码错误")
|
|
return AuthResponse(**result)
|
|
|
|
|
|
@router.get("/me", response_model=UserProfile)
|
|
async def me(authorization: str | None = Header(None)):
|
|
token = _extract_token(authorization)
|
|
user = get_user_by_token(token)
|
|
if not user:
|
|
raise HTTPException(401, "未登录")
|
|
return UserProfile(**user)
|
|
|
|
|
|
@router.get("/demo", response_model=AuthResponse)
|
|
async def demo_login():
|
|
"""一键体验演示账号"""
|
|
result = login_demo()
|
|
if not result:
|
|
raise HTTPException(500, "演示账号不可用")
|
|
return AuthResponse(**result)
|
|
|
|
|
|
@router.get("/favorites", response_model=FavoriteResponse)
|
|
async def list_favorites(authorization: str | None = Header(None)):
|
|
token = _extract_token(authorization)
|
|
if not get_user_by_token(token):
|
|
raise HTTPException(401, "未登录")
|
|
return FavoriteResponse(slugs=get_favorites(token))
|
|
|
|
|
|
@router.get("/favorites/detail", response_model=list[Destination])
|
|
async def favorite_details(authorization: str | None = Header(None)):
|
|
token = _extract_token(authorization)
|
|
user = get_user_by_token(token)
|
|
if not user:
|
|
raise HTTPException(401, "未登录")
|
|
slugs = get_favorites(token)
|
|
all_dests = await pb.get_destinations()
|
|
return [Destination(**d) for d in all_dests if d["slug"] in slugs]
|
|
|
|
|
|
@router.get("/profile/stats", response_model=ProfileStats)
|
|
async def profile_stats(authorization: str | None = Header(None)):
|
|
token = _extract_token(authorization)
|
|
user = get_user_by_token(token)
|
|
if not user:
|
|
raise HTTPException(401, "未登录")
|
|
favs = get_favorites(token)
|
|
plan = get_plan(token) or {}
|
|
cities = len(plan.get("items") or [])
|
|
levels = [(0, "🌱 新手游民"), (1, "🎒 背包客"), (2, "✈️ 飞行游民"), (3, "🌍 环球游民")]
|
|
explored = max(len(favs), cities)
|
|
level = levels[min(explored, 3)][1]
|
|
return ProfileStats(
|
|
favorites_count=len(favs),
|
|
destinations_explored=explored,
|
|
member_since="2026",
|
|
nomad_level=level,
|
|
)
|
|
|
|
|
|
@router.post("/favorites", response_model=FavoriteResponse)
|
|
async def add_favorite(body: FavoriteRequest, authorization: str | None = Header(None)):
|
|
token = _extract_token(authorization)
|
|
if not get_user_by_token(token):
|
|
raise HTTPException(401, "未登录")
|
|
dest = await pb.get_destination(body.destination_slug)
|
|
if not dest:
|
|
raise HTTPException(404, "目的地不存在")
|
|
slugs = toggle_favorite(token, body.destination_slug)
|
|
return FavoriteResponse(slugs=slugs)
|
|
|
|
|
|
@router.get("/plan", response_model=UserPlanPayload)
|
|
async def read_plan(authorization: str | None = Header(None)):
|
|
token = _extract_token(authorization)
|
|
if not get_user_by_token(token):
|
|
raise HTTPException(401, "未登录")
|
|
plan = get_plan(token)
|
|
if not plan:
|
|
raise HTTPException(401, "未登录")
|
|
return UserPlanPayload(**plan)
|
|
|
|
|
|
@router.put("/plan", response_model=UserPlanPayload)
|
|
async def write_plan(body: UserPlanPayload, authorization: str | None = Header(None)):
|
|
token = _extract_token(authorization)
|
|
if not get_user_by_token(token):
|
|
raise HTTPException(401, "未登录")
|
|
saved = save_plan(
|
|
token,
|
|
[item.model_dump() for item in body.items],
|
|
body.meta.model_dump(),
|
|
body.updated_at or None,
|
|
)
|
|
if not saved:
|
|
raise HTTPException(401, "未登录")
|
|
return UserPlanPayload(**saved)
|
|
|
|
|
|
def _extract_token(authorization: str | None) -> str:
|
|
if not authorization:
|
|
return ""
|
|
if authorization.startswith("Bearer "):
|
|
return authorization[7:]
|
|
return authorization
|