feat: add user settings page (change username/password) and remove public registration
This commit is contained in:
@@ -1,209 +0,0 @@
|
||||
from pathlib import Path
|
||||
from uuid import uuid4
|
||||
|
||||
from fastapi import APIRouter, Depends, File, HTTPException, UploadFile
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from app.auth import get_current_user_api, hash_password, verify_password
|
||||
from app.config import UPLOAD_DIR
|
||||
from app.database import get_db
|
||||
from app.models import ApiKey, Postcard, Profile, User
|
||||
from app.schemas import (
|
||||
ApiKeyOut,
|
||||
LoginRequest,
|
||||
PostcardCreate,
|
||||
PostcardOut,
|
||||
PostcardUpdate,
|
||||
ProfileCreate,
|
||||
ProfileOut,
|
||||
RegisterRequest,
|
||||
)
|
||||
|
||||
router = APIRouter(prefix="/api", tags=["api"])
|
||||
|
||||
|
||||
# ---------- Auth ----------
|
||||
|
||||
@router.post("/register", response_model=dict)
|
||||
def api_register(body: RegisterRequest, db: Session = Depends(get_db)):
|
||||
if db.query(User).filter(User.username == body.username).first():
|
||||
raise HTTPException(400, "Username already exists")
|
||||
user = User(username=body.username, password_hash=hash_password(body.password))
|
||||
db.add(user)
|
||||
db.commit()
|
||||
return {"message": "ok"}
|
||||
|
||||
|
||||
@router.post("/login", response_model=dict)
|
||||
def api_login(body: LoginRequest, db: Session = Depends(get_db)):
|
||||
user = db.query(User).filter(User.username == body.username).first()
|
||||
if not user or not verify_password(body.password, user.password_hash):
|
||||
raise HTTPException(401, "Invalid credentials")
|
||||
api_key = ApiKey(user_id=user.id, key=ApiKey.generate_key())
|
||||
db.add(api_key)
|
||||
db.commit()
|
||||
return {"api_key": api_key.key}
|
||||
|
||||
|
||||
# ---------- Profile ----------
|
||||
|
||||
def _get_profile(profile_id: int, user: User, db: Session) -> Profile:
|
||||
p = db.query(Profile).filter(Profile.id == profile_id, Profile.user_id == user.id).first()
|
||||
if not p:
|
||||
raise HTTPException(404, "Profile not found")
|
||||
return p
|
||||
|
||||
|
||||
@router.get("/profiles", response_model=list[ProfileOut])
|
||||
def api_list_profiles(user: User = Depends(get_current_user_api), db: Session = Depends(get_db)):
|
||||
return db.query(Profile).filter(Profile.user_id == user.id).all()
|
||||
|
||||
|
||||
@router.post("/profiles", response_model=ProfileOut, status_code=201)
|
||||
def api_create_profile(body: ProfileCreate, user: User = Depends(get_current_user_api), db: Session = Depends(get_db)):
|
||||
p = Profile(user_id=user.id, nickname=body.nickname)
|
||||
db.add(p)
|
||||
db.commit()
|
||||
db.refresh(p)
|
||||
return p
|
||||
|
||||
|
||||
@router.put("/profiles/{profile_id}", response_model=ProfileOut)
|
||||
def api_update_profile(profile_id: int, body: ProfileCreate, user: User = Depends(get_current_user_api), db: Session = Depends(get_db)):
|
||||
p = _get_profile(profile_id, user, db)
|
||||
p.nickname = body.nickname
|
||||
db.commit()
|
||||
db.refresh(p)
|
||||
return p
|
||||
|
||||
|
||||
@router.delete("/profiles/{profile_id}")
|
||||
def api_delete_profile(profile_id: int, user: User = Depends(get_current_user_api), db: Session = Depends(get_db)):
|
||||
p = _get_profile(profile_id, user, db)
|
||||
db.delete(p)
|
||||
db.commit()
|
||||
return {"message": "ok"}
|
||||
|
||||
|
||||
# ---------- Postcard ----------
|
||||
|
||||
def _get_postcard(postcard_id: int, user: User, db: Session) -> Postcard:
|
||||
pc = (
|
||||
db.query(Postcard)
|
||||
.join(Profile)
|
||||
.filter(Postcard.id == postcard_id, Profile.user_id == user.id)
|
||||
.first()
|
||||
)
|
||||
if not pc:
|
||||
raise HTTPException(404, "Postcard not found")
|
||||
return pc
|
||||
|
||||
|
||||
@router.get("/profiles/{profile_id}/postcards", response_model=list[PostcardOut])
|
||||
def api_list_postcards(
|
||||
profile_id: int,
|
||||
status: str | None = None,
|
||||
country: str | None = None,
|
||||
user: User = Depends(get_current_user_api),
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
_get_profile(profile_id, user, db)
|
||||
q = db.query(Postcard).filter(Postcard.profile_id == profile_id)
|
||||
if status:
|
||||
q = q.filter(Postcard.status == status)
|
||||
if country:
|
||||
q = q.filter(Postcard.country.ilike(f"%{country}%"))
|
||||
return q.order_by(Postcard.created_at.desc()).all()
|
||||
|
||||
|
||||
@router.post("/profiles/{profile_id}/postcards", response_model=PostcardOut, status_code=201)
|
||||
def api_create_postcard(
|
||||
profile_id: int,
|
||||
body: PostcardCreate,
|
||||
user: User = Depends(get_current_user_api),
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
_get_profile(profile_id, user, db)
|
||||
pc = Postcard(profile_id=profile_id, **body.model_dump())
|
||||
db.add(pc)
|
||||
db.commit()
|
||||
db.refresh(pc)
|
||||
return pc
|
||||
|
||||
|
||||
@router.get("/postcards/{postcard_id}", response_model=PostcardOut)
|
||||
def api_get_postcard(postcard_id: int, user: User = Depends(get_current_user_api), db: Session = Depends(get_db)):
|
||||
return _get_postcard(postcard_id, user, db)
|
||||
|
||||
|
||||
@router.put("/postcards/{postcard_id}", response_model=PostcardOut)
|
||||
def api_update_postcard(
|
||||
postcard_id: int,
|
||||
body: PostcardUpdate,
|
||||
user: User = Depends(get_current_user_api),
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
pc = _get_postcard(postcard_id, user, db)
|
||||
for k, v in body.model_dump(exclude_unset=True).items():
|
||||
setattr(pc, k, v)
|
||||
db.commit()
|
||||
db.refresh(pc)
|
||||
return pc
|
||||
|
||||
|
||||
@router.delete("/postcards/{postcard_id}")
|
||||
def api_delete_postcard(postcard_id: int, user: User = Depends(get_current_user_api), db: Session = Depends(get_db)):
|
||||
pc = _get_postcard(postcard_id, user, db)
|
||||
db.delete(pc)
|
||||
db.commit()
|
||||
return {"message": "ok"}
|
||||
|
||||
|
||||
# ---------- Image Upload ----------
|
||||
|
||||
@router.post("/postcards/{postcard_id}/image/{side}", response_model=PostcardOut)
|
||||
async def api_upload_image(
|
||||
postcard_id: int,
|
||||
side: str,
|
||||
file: UploadFile = File(...),
|
||||
user: User = Depends(get_current_user_api),
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
if side not in ("front", "back"):
|
||||
raise HTTPException(400, "side must be 'front' or 'back'")
|
||||
pc = _get_postcard(postcard_id, user, db)
|
||||
ext = Path(file.filename or "upload.jpg").suffix or ".jpg"
|
||||
filename = f"{uuid4().hex}{ext}"
|
||||
dest = UPLOAD_DIR / filename
|
||||
content = await file.read()
|
||||
dest.write_bytes(content)
|
||||
setattr(pc, f"image_{side}", f"/uploads/{filename}")
|
||||
db.commit()
|
||||
db.refresh(pc)
|
||||
return pc
|
||||
|
||||
|
||||
# ---------- API Key ----------
|
||||
|
||||
@router.get("/api-keys", response_model=list[ApiKeyOut])
|
||||
def api_list_keys(user: User = Depends(get_current_user_api), db: Session = Depends(get_db)):
|
||||
return db.query(ApiKey).filter(ApiKey.user_id == user.id).all()
|
||||
|
||||
|
||||
@router.post("/api-keys", response_model=ApiKeyOut, status_code=201)
|
||||
def api_create_key(user: User = Depends(get_current_user_api), db: Session = Depends(get_db)):
|
||||
ak = ApiKey(user_id=user.id, key=ApiKey.generate_key())
|
||||
db.add(ak)
|
||||
db.commit()
|
||||
db.refresh(ak)
|
||||
return ak
|
||||
|
||||
|
||||
@router.delete("/api-keys/{key_id}")
|
||||
def api_delete_key(key_id: int, user: User = Depends(get_current_user_api), db: Session = Depends(get_db)):
|
||||
ak = db.query(ApiKey).filter(ApiKey.id == key_id, ApiKey.user_id == user.id).first()
|
||||
if not ak:
|
||||
raise HTTPException(404, "API key not found")
|
||||
db.delete(ak)
|
||||
db.commit()
|
||||
return {"message": "ok"}
|
||||
Reference in New Issue
Block a user