feat(server): add /auth/pass/activate endpoint for pass code activation
This commit is contained in:
@@ -643,3 +643,98 @@ async def validate_token(request: Request, credentials: HTTPAuthorizationCredent
|
||||
except Exception as e:
|
||||
logger.error(f"Token validation error: {e}")
|
||||
raise HTTPException(400, "Invalid request")
|
||||
|
||||
|
||||
class ActivatePassRequest(BaseModel):
|
||||
pass_code: str = Field(..., min_length=3, max_length=64)
|
||||
|
||||
|
||||
@router.post("/pass/activate")
|
||||
async def activate_pass(
|
||||
body: ActivatePassRequest,
|
||||
current_user: dict = Depends(get_current_user),
|
||||
request: Request = None,
|
||||
):
|
||||
"""Activate a pass code for the current user"""
|
||||
ip = request.client.host if request.client else "unknown"
|
||||
|
||||
with get_db() as conn:
|
||||
# Check if pass exists and is active
|
||||
pass_row = conn.execute(
|
||||
"SELECT code, owner, is_active, expires_at, max_uses, uses, activated_by FROM passes WHERE code = ?",
|
||||
(body.pass_code,),
|
||||
).fetchone()
|
||||
|
||||
if not pass_row:
|
||||
raise HTTPException(404, "Проходка не найдена")
|
||||
|
||||
if not pass_row["is_active"]:
|
||||
raise HTTPException(400, "Проходка уже использована или отозвана")
|
||||
|
||||
if pass_row["uses"] >= pass_row["max_uses"]:
|
||||
raise HTTPException(400, "Проходка достигла лимита использований")
|
||||
|
||||
if pass_row["expires_at"] and pass_row["expires_at"] < time.time():
|
||||
raise HTTPException(400, "Проходка истекла")
|
||||
|
||||
# Check if user already has an active pass
|
||||
existing = conn.execute(
|
||||
"SELECT 1 FROM user_passes WHERE user_id = ? AND pass_code = ?",
|
||||
(current_user["id"], body.pass_code),
|
||||
).fetchone()
|
||||
|
||||
if existing:
|
||||
raise HTTPException(409, "Эта проходка уже активирована вами")
|
||||
|
||||
existing_pass = conn.execute("""
|
||||
SELECT 1 FROM user_passes up
|
||||
JOIN passes p ON up.pass_code = p.code
|
||||
WHERE up.user_id = ? AND (p.expires_at IS NULL OR p.expires_at > ?)
|
||||
""", (current_user["id"], time.time())).fetchone()
|
||||
|
||||
if existing_pass:
|
||||
raise HTTPException(409, "У вас уже есть активная проходка")
|
||||
|
||||
now = time.time()
|
||||
|
||||
# Link pass to user
|
||||
conn.execute(
|
||||
"INSERT INTO user_passes (user_id, pass_code, activated_at) VALUES (?, ?, ?)",
|
||||
(current_user["id"], body.pass_code, now),
|
||||
)
|
||||
|
||||
# Increment usage count
|
||||
conn.execute(
|
||||
"UPDATE passes SET uses = uses + 1, activated_by = ?, activated_at = ? WHERE code = ?",
|
||||
(current_user["id"], now, body.pass_code),
|
||||
)
|
||||
|
||||
# Upgrade user role if they don't have a higher role
|
||||
if current_user["role"] < 1:
|
||||
conn.execute(
|
||||
"UPDATE users SET role = 1 WHERE id = ?",
|
||||
(current_user["id"],),
|
||||
)
|
||||
|
||||
conn.commit()
|
||||
|
||||
log_audit(
|
||||
current_user["id"],
|
||||
"pass_activated",
|
||||
f"Pass activated: {body.pass_code[:8]}...",
|
||||
ip,
|
||||
)
|
||||
|
||||
logger.info(
|
||||
"Pass activated",
|
||||
user=current_user["username"],
|
||||
user_id=current_user["id"],
|
||||
pass_code=body.pass_code,
|
||||
ip=ip,
|
||||
)
|
||||
|
||||
return {
|
||||
"success": True,
|
||||
"message": f"Проходка активирована для {current_user['username']}",
|
||||
"role": 1,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user