diff --git a/backend/main.py b/backend/main.py index 3033e8d..aa09dd6 100644 --- a/backend/main.py +++ b/backend/main.py @@ -266,6 +266,45 @@ async def login(form_data: LoginRequest, db: Session = Depends(get_db)): async def get_me(current_user: User = Depends(get_current_user)): return current_user + +class ResetPasswordRequest(BaseModel): + email: str + new_password: str + + +@app.post("/api/auth/reset-password") +async def reset_password( + payload: ResetPasswordRequest, + db: Session = Depends(get_db), + current_user: User = Depends(get_current_admin), +): + """Admin-only: reset another user's password. + + Body: {"email": "", "new_password": ""} + """ + if not payload.email or not payload.new_password: + raise HTTPException(status_code=400, detail="email and new_password are required") + if len(payload.new_password) < 6: + raise HTTPException(status_code=400, detail="new_password must be at least 6 characters") + + target = db.query(User).filter(User.email == payload.email).first() + if not target: + raise HTTPException(status_code=404, detail=f"User not found: {payload.email}") + + target.password_hash = get_password_hash(payload.new_password) + db.commit() + db.refresh(target) + logger.info( + "password reset by admin", + extra={"context": {"admin_id": current_user.id, "target_user_id": target.id, "email": payload.email}}, + ) + return { + "ok": True, + "email": target.email, + "id": target.id, + "reset_by": current_user.email, + } + # ============ Excel File Storage ============ def get_excel_path(section: str) -> str: """Get path to the stored Excel file for a section"""