from fastapi import FastAPI, UploadFile, File, WebSocket, WebSocketDisconnect, HTTPException, Body from fastapi.staticfiles import StaticFiles from fastapi.responses import FileResponse from pydantic import BaseModel from textblob import TextBlob from passlib.context import CryptContext from typing import List, Optional import os import json import base64 app = FastAPI(title="Zenith Platform") # --- CONFIG --- pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto") SUPABASE_URL = os.getenv("SUPABASE_URL") SUPABASE_KEY = os.getenv("SUPABASE_KEY") # ВАЖНО: Если переменная не задана, код будет '1234'. Проверь это! ADMIN_SECRET_CODE = os.getenv("ADMIN_SECRET_CODE", "1234") # --- MODELS --- class CreateUserModel(BaseModel): admin_code: str new_username: str new_password: str class UpdateProfileModel(BaseModel): username: str is_looking: bool class MessageInput(BaseModel): text: str # --- AUTH & ADMIN --- @app.post("/api/admin/create_user") async def create_user(data: CreateUserModel): # Логируем для отладки (будет видно в логах HF) print(f"Admin attempt. Input code: '{data.admin_code}', Expected: '{ADMIN_SECRET_CODE}'") if data.admin_code.strip() != ADMIN_SECRET_CODE: raise HTTPException(status_code=403, detail="ACCESS DENIED: Wrong Code") hashed_pw = pwd_context.hash(data.new_password) return { "username": data.new_username, "password_hash": hashed_pw, "badge": "BETA" } @app.post("/api/user/update_profile") async def update_profile(data: UpdateProfileModel): # Здесь просто возвращаем статус, реальная запись идет через Supabase Client на фронте # или можно расширить этот метод для серверной записи. return {"status": "ok", "looking_for_friends": data.is_looking} # --- CHAT & MOOD --- @app.post("/api/analyze_mood") async def analyze_mood(msg: MessageInput): blob = TextBlob(msg.text) p = blob.sentiment.polarity if p > 0.5: return {"mood_color": "#00f2ea", "emoji": "✨"} elif p > 0: return {"mood_color": "#4facfe", "emoji": "😌"} elif p < -0.5: return {"mood_color": "#ff0055", "emoji": "🔥"} elif p < 0: return {"mood_color": "#8e44ad", "emoji": "🥀"} return {"mood_color": "#ffffff", "emoji": "🌫️"} @app.get("/api/config") async def get_config(): return {"supabase_url": SUPABASE_URL, "supabase_key": SUPABASE_KEY} # --- WEBSOCKET (FIXED) --- class ConnectionManager: def __init__(self): self.active_connections: List[WebSocket] = [] async def connect(self, websocket: WebSocket): await websocket.accept(); self.active_connections.append(websocket) def disconnect(self, websocket: WebSocket): self.active_connections.remove(websocket) async def broadcast(self, message: str, sender: WebSocket): for connection in self.active_connections: if connection != sender: try: await connection.send_text(message) except: pass manager = ConnectionManager() @app.websocket("/ws/signal") async def websocket_endpoint(websocket: WebSocket): await manager.connect(websocket) try: while True: data = await websocket.receive_text() await manager.broadcast(data, websocket) except WebSocketDisconnect: manager.disconnect(websocket) app.mount("/static", StaticFiles(directory="static"), name="static") @app.get("/") async def read_index(): return FileResponse('static/index.html')