Split backend into multiple files

This commit is contained in:
2025-08-17 19:27:47 +03:00
Unverified
parent 1156345feb
commit cc819257a3
7 changed files with 187 additions and 167 deletions
+9 -167
View File
@@ -1,172 +1,14 @@
from fastapi import FastAPI, Depends, HTTPException, status
from fastapi.middleware.cors import CORSMiddleware
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
from sqlalchemy import create_engine, Column, Integer, String, Text, Boolean, DateTime, ForeignKey
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker, relationship, Session
from datetime import datetime, timedelta
import jwt
import re
from typing import Optional
from pydantic import BaseModel
import bcrypt
from fastapi import Depends, HTTPException, status
from sqlalchemy.orm import Session
from datetime import datetime
from constants import *
# Инициализация FastAPI
app = FastAPI(title="PixelChat")
# CORS
app.add_middleware(
CORSMiddleware,
allow_origins=["*"], # В продакшене замените на нужные домены
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# Настройка базы данных
engine = create_engine(DATABASE_URL, connect_args={"check_same_thread": False})
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
Base = declarative_base()
# Модели базы данных
class User(Base):
__tablename__ = "user"
id = Column(Integer, primary_key=True, index=True)
username = Column(String(50), unique=True, nullable=False, index=True)
password_hash = Column(String(200), nullable=False)
online = Column(Boolean, default=False)
last_seen = Column(DateTime, default=datetime.now)
created_at = Column(DateTime, default=datetime.now)
messages = relationship("Message", back_populates="author", lazy="select")
class Message(Base):
__tablename__ = "message"
id = Column(Integer, primary_key=True, index=True)
content = Column(Text, nullable=False)
timestamp = Column(DateTime, default=datetime.now)
user_id = Column(Integer, ForeignKey("user.id"), nullable=False)
is_read = Column(Boolean, default=False)
author = relationship("User", back_populates="messages")
# Валидация данных
def is_valid_username(username: str) -> bool:
if len(username) < 3 or len(username) > 20:
return False
if re.search(r'[\s\u180E\u200B-\u200D\u2060\uFEFF]', username):
return False
return True
def is_valid_password(password: str) -> bool:
if len(password) < 5 or len(password) > 50:
return False
if re.search(r'[\s\u180E\u200B-\u200D\u2060\uFEFF]', password):
return False
return True
# JWT Helper Functions
def create_token(user_id: int, username: str) -> str:
expire = datetime.utcnow() + timedelta(hours=ACCESS_TOKEN_EXPIRE_HOURS)
payload = {
"user_id": user_id,
"username": username,
"exp": expire
}
return jwt.encode(payload, JWT_SECRET_KEY, algorithm=JWT_ALGORITHM)
def verify_token(token: str) -> Optional[dict]:
try:
payload = jwt.decode(token, JWT_SECRET_KEY, algorithms=[JWT_ALGORITHM])
return payload
except jwt.ExpiredSignatureError:
return None
except jwt.InvalidTokenError:
return None
def verify_password(plain_password: str, hashed_password: str) -> bool:
return bcrypt.checkpw(plain_password.encode("utf-8"), hashed_password.encode("utf-8"))
def get_password_hash(password: str) -> str:
return bcrypt.hashpw(password.encode("utf-8"), bcrypt.gensalt()).decode("utf-8")
# Security для FastAPI
security = HTTPBearer()
# Зависимость для получения текущего пользователя
def get_current_user(
credentials: HTTPAuthorizationCredentials = Depends(security),
db: Session = Depends(lambda: SessionLocal())
):
token = credentials.credentials
payload = verify_token(token)
if not payload:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Invalid or expired token",
headers={"WWW-Authenticate": "Bearer"},
)
user = db.query(User).filter(User.id == payload["user_id"]).first()
if not user:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="User not found",
headers={"WWW-Authenticate": "Bearer"},
)
return {"user_id": user.id, "username": user.username}
# Зависимость для получения сессии БД
def get_db():
db = SessionLocal()
try:
yield db
finally:
db.close()
# Pydantic модели
class LoginRequest(BaseModel):
username: str
password: str
class RegisterRequest(BaseModel):
username: str
password: str
confirm_password: str
class SendMessageRequest(BaseModel):
content: str
class MessageResponse(BaseModel):
id: int
content: str
timestamp: datetime
is_author: bool
is_read: bool
username: str
class Config:
from_attributes = True
# Создание таблиц
Base.metadata.create_all(bind=engine)
from db import *
from models import *
from validation import *
from utils import *
from dependencies import *
from app import *
# API маршруты