goofish / src /api /routes /accounts.py
host1syan's picture
Upload 212 files
5378afe verified
"""
闲鱼账号管理路由
"""
import json
import os
import re
import aiofiles
from fastapi import APIRouter, HTTPException
from pydantic import BaseModel
from typing import List
from src.infrastructure.config.env_manager import env_manager
router = APIRouter(prefix="/api/accounts", tags=["accounts"])
ACCOUNT_NAME_RE = re.compile(r"^[a-zA-Z0-9_-]{1,50}$")
class AccountCreate(BaseModel):
name: str
content: str
class AccountUpdate(BaseModel):
content: str
def _strip_quotes(value: str) -> str:
if not value:
return value
if value.startswith(("\"", "'")) and value.endswith(("\"", "'")):
return value[1:-1]
return value
def _state_dir() -> str:
raw = env_manager.get_value("ACCOUNT_STATE_DIR", "state") or "state"
return _strip_quotes(raw.strip())
def _ensure_state_dir(path: str) -> None:
os.makedirs(path, exist_ok=True)
def _validate_name(name: str) -> str:
trimmed = name.strip()
if not trimmed or not ACCOUNT_NAME_RE.match(trimmed):
raise HTTPException(status_code=400, detail="账号名称只能包含字母、数字、下划线或短横线。")
return trimmed
def _account_path(name: str) -> str:
filename = f"{name}.json"
return os.path.join(_state_dir(), filename)
def _validate_json(content: str) -> None:
try:
json.loads(content)
except json.JSONDecodeError:
raise HTTPException(status_code=400, detail="提供的内容不是有效的JSON格式。")
@router.get("", response_model=List[dict])
async def list_accounts():
state_dir = _state_dir()
if not os.path.isdir(state_dir):
return []
files = [f for f in os.listdir(state_dir) if f.endswith(".json")]
accounts = []
for filename in sorted(files):
name = filename[:-5]
accounts.append({
"name": name,
"path": os.path.join(state_dir, filename),
})
return accounts
@router.get("/{name}", response_model=dict)
async def get_account(name: str):
account_name = _validate_name(name)
path = _account_path(account_name)
if not os.path.exists(path):
raise HTTPException(status_code=404, detail="账号不存在")
async with aiofiles.open(path, "r", encoding="utf-8") as f:
content = await f.read()
return {"name": account_name, "path": path, "content": content}
@router.post("", response_model=dict)
async def create_account(data: AccountCreate):
account_name = _validate_name(data.name)
_validate_json(data.content)
state_dir = _state_dir()
_ensure_state_dir(state_dir)
path = _account_path(account_name)
if os.path.exists(path):
raise HTTPException(status_code=409, detail="账号已存在")
async with aiofiles.open(path, "w", encoding="utf-8") as f:
await f.write(data.content)
return {"message": "账号已添加", "name": account_name, "path": path}
@router.put("/{name}", response_model=dict)
async def update_account(name: str, data: AccountUpdate):
account_name = _validate_name(name)
_validate_json(data.content)
state_dir = _state_dir()
_ensure_state_dir(state_dir)
path = _account_path(account_name)
if not os.path.exists(path):
raise HTTPException(status_code=404, detail="账号不存在")
async with aiofiles.open(path, "w", encoding="utf-8") as f:
await f.write(data.content)
return {"message": "账号已更新", "name": account_name, "path": path}
@router.delete("/{name}", response_model=dict)
async def delete_account(name: str):
account_name = _validate_name(name)
path = _account_path(account_name)
if not os.path.exists(path):
raise HTTPException(status_code=404, detail="账号不存在")
os.remove(path)
return {"message": "账号已删除"}