Add File
This commit is contained in:
243
src/landppt/auth/routes.py
Normal file
243
src/landppt/auth/routes.py
Normal file
@@ -0,0 +1,243 @@
|
|||||||
|
"""
|
||||||
|
Authentication routes for LandPPT
|
||||||
|
"""
|
||||||
|
|
||||||
|
from fastapi import APIRouter, Request, Form, Depends, HTTPException
|
||||||
|
from fastapi.responses import HTMLResponse, RedirectResponse
|
||||||
|
from fastapi.templating import Jinja2Templates
|
||||||
|
from sqlalchemy.orm import Session
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from .auth_service import get_auth_service, AuthService
|
||||||
|
from .middleware import get_current_user_optional, get_current_user_required, get_current_user
|
||||||
|
from ..database.database import get_db
|
||||||
|
from ..database.models import User
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
router = APIRouter()
|
||||||
|
templates = Jinja2Templates(directory="src/landppt/web/templates")
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/auth/login", response_class=HTMLResponse)
|
||||||
|
async def login_page(
|
||||||
|
request: Request,
|
||||||
|
error: str = None,
|
||||||
|
success: str = None,
|
||||||
|
username: str = None
|
||||||
|
):
|
||||||
|
"""Login page"""
|
||||||
|
# Check if user is already logged in using request.state.user set by middleware
|
||||||
|
user = get_current_user(request)
|
||||||
|
if user:
|
||||||
|
return RedirectResponse(url="/dashboard", status_code=302)
|
||||||
|
|
||||||
|
return templates.TemplateResponse("login.html", {
|
||||||
|
"request": request,
|
||||||
|
"error": error,
|
||||||
|
"success": success,
|
||||||
|
"username": username
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
@router.post("/auth/login")
|
||||||
|
async def login(
|
||||||
|
request: Request,
|
||||||
|
username: str = Form(...),
|
||||||
|
password: str = Form(...),
|
||||||
|
db: Session = Depends(get_db),
|
||||||
|
auth_service: AuthService = Depends(get_auth_service)
|
||||||
|
):
|
||||||
|
"""Handle login form submission"""
|
||||||
|
try:
|
||||||
|
# Authenticate user
|
||||||
|
user = auth_service.authenticate_user(db, username, password)
|
||||||
|
|
||||||
|
if not user:
|
||||||
|
return templates.TemplateResponse("login.html", {
|
||||||
|
"request": request,
|
||||||
|
"error": "用户名或密码错误",
|
||||||
|
"username": username
|
||||||
|
})
|
||||||
|
|
||||||
|
# Create session
|
||||||
|
session_id = auth_service.create_session(db, user)
|
||||||
|
|
||||||
|
# Redirect to dashboard
|
||||||
|
response = RedirectResponse(url="/dashboard", status_code=302)
|
||||||
|
|
||||||
|
# Set cookie max_age based on session expiration
|
||||||
|
# If session_expire_minutes is 0, set cookie to never expire (None means session cookie)
|
||||||
|
current_expire_minutes = auth_service._get_current_expire_minutes()
|
||||||
|
cookie_max_age = None if current_expire_minutes == 0 else current_expire_minutes * 60
|
||||||
|
|
||||||
|
response.set_cookie(
|
||||||
|
key="session_id",
|
||||||
|
value=session_id,
|
||||||
|
max_age=cookie_max_age,
|
||||||
|
httponly=True,
|
||||||
|
secure=False, # Set to True in production with HTTPS
|
||||||
|
samesite="lax"
|
||||||
|
)
|
||||||
|
|
||||||
|
logger.info(f"User {username} logged in successfully")
|
||||||
|
return response
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Login error: {e}")
|
||||||
|
return templates.TemplateResponse("login.html", {
|
||||||
|
"request": request,
|
||||||
|
"error": "登录过程中发生错误,请重试",
|
||||||
|
"username": username
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/auth/logout")
|
||||||
|
async def logout(
|
||||||
|
request: Request,
|
||||||
|
db: Session = Depends(get_db),
|
||||||
|
auth_service: AuthService = Depends(get_auth_service)
|
||||||
|
):
|
||||||
|
"""Logout user"""
|
||||||
|
session_id = request.cookies.get("session_id")
|
||||||
|
|
||||||
|
if session_id:
|
||||||
|
auth_service.logout_user(db, session_id)
|
||||||
|
|
||||||
|
response = RedirectResponse(url="/auth/login?success=已成功退出登录", status_code=302)
|
||||||
|
response.delete_cookie("session_id")
|
||||||
|
|
||||||
|
return response
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/auth/profile", response_class=HTMLResponse)
|
||||||
|
async def profile_page(
|
||||||
|
request: Request,
|
||||||
|
user: User = Depends(get_current_user_required)
|
||||||
|
):
|
||||||
|
"""User profile page"""
|
||||||
|
return templates.TemplateResponse("profile.html", {
|
||||||
|
"request": request,
|
||||||
|
"user": user.to_dict()
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
@router.post("/auth/change-password")
|
||||||
|
async def change_password(
|
||||||
|
request: Request,
|
||||||
|
current_password: str = Form(...),
|
||||||
|
new_password: str = Form(...),
|
||||||
|
confirm_password: str = Form(...),
|
||||||
|
user: User = Depends(get_current_user_required),
|
||||||
|
db: Session = Depends(get_db),
|
||||||
|
auth_service: AuthService = Depends(get_auth_service)
|
||||||
|
):
|
||||||
|
"""Change user password"""
|
||||||
|
try:
|
||||||
|
# Validate current password
|
||||||
|
if not user.check_password(current_password):
|
||||||
|
return templates.TemplateResponse("profile.html", {
|
||||||
|
"request": request,
|
||||||
|
"user": user.to_dict(),
|
||||||
|
"error": "当前密码错误"
|
||||||
|
})
|
||||||
|
|
||||||
|
# Validate new password
|
||||||
|
if new_password != confirm_password:
|
||||||
|
return templates.TemplateResponse("profile.html", {
|
||||||
|
"request": request,
|
||||||
|
"user": user.to_dict(),
|
||||||
|
"error": "新密码和确认密码不匹配"
|
||||||
|
})
|
||||||
|
|
||||||
|
if len(new_password) < 6:
|
||||||
|
return templates.TemplateResponse("profile.html", {
|
||||||
|
"request": request,
|
||||||
|
"user": user.to_dict(),
|
||||||
|
"error": "密码长度至少6位"
|
||||||
|
})
|
||||||
|
|
||||||
|
# Update password
|
||||||
|
if auth_service.update_user_password(db, user, new_password):
|
||||||
|
return templates.TemplateResponse("profile.html", {
|
||||||
|
"request": request,
|
||||||
|
"user": user.to_dict(),
|
||||||
|
"success": "密码修改成功"
|
||||||
|
})
|
||||||
|
else:
|
||||||
|
return templates.TemplateResponse("profile.html", {
|
||||||
|
"request": request,
|
||||||
|
"user": user.to_dict(),
|
||||||
|
"error": "密码修改失败,请重试"
|
||||||
|
})
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Change password error: {e}")
|
||||||
|
return templates.TemplateResponse("profile.html", {
|
||||||
|
"request": request,
|
||||||
|
"user": user.to_dict(),
|
||||||
|
"error": "修改密码过程中发生错误"
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
# API endpoints for authentication
|
||||||
|
@router.post("/api/auth/login")
|
||||||
|
async def api_login(
|
||||||
|
username: str = Form(...),
|
||||||
|
password: str = Form(...),
|
||||||
|
db: Session = Depends(get_db),
|
||||||
|
auth_service: AuthService = Depends(get_auth_service)
|
||||||
|
):
|
||||||
|
"""API login endpoint"""
|
||||||
|
user = auth_service.authenticate_user(db, username, password)
|
||||||
|
|
||||||
|
if not user:
|
||||||
|
raise HTTPException(status_code=401, detail="用户名或密码错误")
|
||||||
|
|
||||||
|
session_id = auth_service.create_session(db, user)
|
||||||
|
|
||||||
|
return {
|
||||||
|
"success": True,
|
||||||
|
"session_id": session_id,
|
||||||
|
"user": user.to_dict()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@router.post("/api/auth/logout")
|
||||||
|
async def api_logout(
|
||||||
|
request: Request,
|
||||||
|
db: Session = Depends(get_db),
|
||||||
|
auth_service: AuthService = Depends(get_auth_service)
|
||||||
|
):
|
||||||
|
"""API logout endpoint"""
|
||||||
|
session_id = request.cookies.get("session_id")
|
||||||
|
|
||||||
|
if session_id:
|
||||||
|
auth_service.logout_user(db, session_id)
|
||||||
|
|
||||||
|
return {"success": True, "message": "已成功退出登录"}
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/api/auth/me")
|
||||||
|
async def api_current_user(
|
||||||
|
user: User = Depends(get_current_user_required)
|
||||||
|
):
|
||||||
|
"""Get current user info"""
|
||||||
|
return {
|
||||||
|
"success": True,
|
||||||
|
"user": user.to_dict()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/api/auth/check")
|
||||||
|
async def api_check_auth(
|
||||||
|
request: Request,
|
||||||
|
db: Session = Depends(get_db)
|
||||||
|
):
|
||||||
|
"""Check authentication status"""
|
||||||
|
user = get_current_user_optional(request, db)
|
||||||
|
|
||||||
|
return {
|
||||||
|
"authenticated": user is not None,
|
||||||
|
"user": user.to_dict() if user else None
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user