添加用户鉴权

This commit is contained in:
ChengCan
2026-03-26 11:06:10 +08:00
parent f7b264e6c8
commit 3e3f4390a6
12 changed files with 379 additions and 29 deletions

View File

@@ -0,0 +1,95 @@
import hashlib
from fastapi import APIRouter, Depends, HTTPException, status
from sqlalchemy import text
from sqlalchemy.orm import Session
try:
# For module mode: `uvicorn app.main:app`
from app.db import get_db
from app.schemas.auth import LoginRequest, LoginResponse
from app.security import create_access_token
except ModuleNotFoundError:
# For script mode: `python app/main.py` or VS Code "Run Python File"
from db import get_db
from schemas.auth import LoginRequest, LoginResponse
from security import create_access_token
router = APIRouter(prefix="/auth", tags=["auth"])
@router.post("/login", response_model=LoginResponse)
def login(payload: LoginRequest, db: Session = Depends(get_db)) -> LoginResponse:
with db.begin():
row = (
db.execute(
text(
"""
SELECT
ua.id AS user_auth_id,
ua.user_id,
ua.password_hash,
ua.status AS auth_status,
u.status AS user_status
FROM chat_user_auth ua
JOIN chat_user u ON u.id = ua.user_id
WHERE ua.auth_type = 6
AND ua.auth_identifier = :username
LIMIT 1
"""
),
{"username": payload.username},
)
.mappings()
.first()
)
if not row:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="invalid username or password",
)
if int(row["auth_status"]) != 1 or int(row["user_status"]) != 1:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="account is disabled",
)
# Seed data currently stores sha256; migrate to bcrypt/argon2 in production.
password_hash = hashlib.sha256(payload.password.encode("utf-8")).hexdigest()
if not row["password_hash"] or password_hash != row["password_hash"]:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="invalid username or password",
)
db.execute(
text(
"""
UPDATE chat_user_auth
SET last_login_at = CURRENT_TIMESTAMP(3),
updated_at = CURRENT_TIMESTAMP(3)
WHERE id = :user_auth_id
"""
),
{"user_auth_id": int(row["user_auth_id"])},
)
db.execute(
text(
"""
UPDATE chat_user
SET last_login_at = CURRENT_TIMESTAMP(3),
updated_at = CURRENT_TIMESTAMP(3)
WHERE id = :user_id
"""
),
{"user_id": int(row["user_id"])},
)
access_token, expires_in = create_access_token(user_id=int(row["user_id"]))
return LoginResponse(
access_token=access_token,
expires_in=expires_in,
user_id=int(row["user_id"]),
)

View File

@@ -9,6 +9,7 @@ from sqlalchemy.orm import Session
try:
# For module mode: `uvicorn app.main:app`
from app.db import get_db
from app.security import get_current_user_id
from app.schemas.message import (
MessageCreateRequest,
MessageCreateResponse,
@@ -18,6 +19,7 @@ try:
except ModuleNotFoundError:
# For script mode: `python app/main.py` or VS Code "Run Python File"
from db import get_db
from security import get_current_user_id
from schemas.message import (
MessageCreateRequest,
MessageCreateResponse,
@@ -106,14 +108,48 @@ def _get_existing_message(
)
def _assert_conversation_access(
db: Session,
conversation_id: int,
current_user_id: int,
) -> None:
conversation_row = (
db.execute(
text(
"""
SELECT id, user_low_id, user_high_id
FROM chat_conversation
WHERE id = :conversation_id
LIMIT 1
"""
),
{"conversation_id": conversation_id},
)
.mappings()
.first()
)
if not conversation_row:
raise HTTPException(status_code=404, detail="conversation not found")
if current_user_id not in (
int(conversation_row["user_low_id"]),
int(conversation_row["user_high_id"]),
):
raise HTTPException(status_code=403, detail="no permission for this conversation")
@router.post("", response_model=MessageCreateResponse, status_code=status.HTTP_201_CREATED)
def create_message(
payload: MessageCreateRequest,
response: Response,
current_user_id: int = Depends(get_current_user_id),
db: Session = Depends(get_db),
) -> MessageCreateResponse:
sender_user_id = payload.sender_user_id
sender_user_id = current_user_id
peer_user_id = payload.peer_user_id
if sender_user_id == peer_user_id:
raise HTTPException(status_code=422, detail="peer_user_id cannot be same as current user")
user_low_id = min(sender_user_id, peer_user_id)
user_high_id = max(sender_user_id, peer_user_id)
@@ -303,17 +339,14 @@ def list_messages(
conversation_id: int = Query(gt=0),
cursor_seq: int | None = Query(default=None, ge=1),
limit: int = Query(default=20, ge=1, le=100),
current_user_id: int = Depends(get_current_user_id),
db: Session = Depends(get_db),
) -> MessageListResponse:
conversation_exists = (
db.execute(
text("SELECT 1 FROM chat_conversation WHERE id = :conversation_id LIMIT 1"),
{"conversation_id": conversation_id},
).first()
is not None
_assert_conversation_access(
db=db,
conversation_id=conversation_id,
current_user_id=current_user_id,
)
if not conversation_exists:
raise HTTPException(status_code=404, detail="conversation not found")
sql = """
SELECT