Add profile

This commit is contained in:
2025-08-22 15:51:32 +03:00
Unverified
parent 803f685180
commit deb02a5e95
25 changed files with 885 additions and 116 deletions
+3 -2
View File
@@ -1,7 +1,7 @@
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from routes import account, messaging
from routes import account, messaging, profile
# Инициализация FastAPI
app = FastAPI(title="PixelChat")
@@ -17,4 +17,5 @@ app.add_middleware(
# Routes
app.include_router(account.router)
app.include_router(messaging.router)
app.include_router(messaging.router)
app.include_router(profile.router)
+2 -2
View File
@@ -1,4 +1,4 @@
DATABASE_URL = "sqlite:///./pixelchat.db"
JWT_SECRET_KEY = "pixelchat-jwt-secret"
DATABASE_URL = "sqlite:///./data/database.db"
JWT_SECRET_KEY = "fromchat-jwt-secret"
JWT_ALGORITHM = "HS256"
ACCESS_TOKEN_EXPIRE_HOURS = 24
+4
View File
@@ -1,6 +1,10 @@
import os
from sqlalchemy.orm import sessionmaker
from sqlalchemy import create_engine
from constants import DATABASE_URL
# Ensure data directory exists
os.makedirs("data", exist_ok=True)
engine = create_engine(DATABASE_URL, connect_args={"check_same_thread": False})
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
+2
View File
@@ -15,6 +15,7 @@ class User(Base):
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)
profile_picture = Column(String(255), nullable=True)
online = Column(Boolean, default=False)
last_seen = Column(DateTime, default=datetime.now)
created_at = Column(DateTime, default=datetime.now)
@@ -56,6 +57,7 @@ class MessageResponse(BaseModel):
is_author: bool
is_read: bool
username: str
profile_picture: str | None
class Config:
from_attributes = True
+3 -1
View File
@@ -3,4 +3,6 @@ fastapi[standard]>=0.116.1
pydantic>=2.11.7
sqlalchemy>=2.0.43
bcrypt>=4.3.0
websockets>=15.0.1
websockets>=15.0.1
Pillow>=10.0.0
python-multipart>=0.0.6
+2 -1
View File
@@ -15,7 +15,8 @@ def convert_message(msg: Message) -> dict:
"content": msg.content,
"timestamp": msg.timestamp.isoformat(),
"is_read": msg.is_read,
"username": msg.author.username
"username": msg.author.username,
"profile_picture": msg.author.profile_picture
}
async def get_messages_inner(db: Session):
+98
View File
@@ -0,0 +1,98 @@
from pathlib import Path
from fastapi import APIRouter, Depends, HTTPException, UploadFile, File
from sqlalchemy.orm import Session
from PIL import Image
import os
import uuid
import io
from dependencies import get_db, get_current_user
from models import User
router = APIRouter()
# Create uploads directory if it doesn't exist
PROFILE_PICTURES_DIR = Path("data/uploads/pfp")
os.makedirs(PROFILE_PICTURES_DIR, exist_ok=True)
@router.post("/upload-profile-picture")
async def upload_profile_picture(
profile_picture: UploadFile = File(...),
current_user: User = Depends(get_current_user),
db: Session = Depends(get_db)
):
"""
Upload and process a profile picture
"""
# Validate file type
if not profile_picture.content_type.startswith('image/'):
raise HTTPException(status_code=400, detail="File must be an image")
# Validate file size (max 5MB)
if profile_picture.size > 5 * 1024 * 1024:
raise HTTPException(status_code=400, detail="File size must be less than 5MB")
try:
# Read and process the image
image_data = await profile_picture.read()
# Open image with PIL
image = Image.open(io.BytesIO(image_data))
# Convert to RGB if necessary
if image.mode != 'RGB':
image = image.convert('RGB')
# Resize to a reasonable size (200x200)
image.thumbnail((200, 200), Image.Resampling.LANCZOS)
# Generate unique filename
filename = f"{current_user.id}_{uuid.uuid4().hex}.jpg"
filepath = os.path.join(PROFILE_PICTURES_DIR, filename)
# Save the processed image
image.save(filepath, 'JPEG', quality=85)
# Update user's profile picture in database
profile_picture_url = f"/api/profile-picture/{filename}"
current_user.profile_picture = profile_picture_url
db.commit()
return {
"message": "Profile picture uploaded successfully",
"profile_picture_url": profile_picture_url
}
except Exception as e:
raise HTTPException(status_code=500, detail=f"Error processing image: {str(e)}")
@router.get("/profile-picture/{filename}")
async def get_profile_picture(filename: str):
"""
Serve profile picture files
"""
filepath = os.path.join(PROFILE_PICTURES_DIR, filename)
if not os.path.exists(filepath):
raise HTTPException(status_code=404, detail="Profile picture not found")
from fastapi.responses import FileResponse
return FileResponse(filepath, media_type="image/jpeg")
@router.get("/user/profile")
async def get_user_profile(
current_user: User = Depends(get_current_user),
db: Session = Depends(get_db)
):
"""
Get current user's profile information
"""
return {
"id": current_user.id,
"username": current_user.username,
"profile_picture": current_user.profile_picture,
"online": current_user.online,
"last_seen": current_user.last_seen,
"created_at": current_user.created_at
}