Spaces:
Sleeping
Sleeping
from fastapi import FastAPI, HTTPException, Form, Request | |
from fastapi.middleware.cors import CORSMiddleware | |
from fastapi.responses import JSONResponse | |
from fastapi_socketio import SocketManager | |
from typing import Optional | |
import pymongo | |
from bson.objectid import ObjectId | |
import redis | |
import json | |
from redis.exceptions import RedisError | |
from pydantic import BaseModel, validator | |
from pymongo.errors import ConnectionFailure | |
# Initialize FastAPI app | |
app = FastAPI() | |
socket_manager = SocketManager(app=app, cors_allowed_origins="*", mount_location="/socket.io") | |
# Add CORS middleware | |
app.add_middleware( | |
CORSMiddleware, | |
allow_origins=["*"], | |
allow_credentials=True, | |
allow_methods=["*"], | |
allow_headers=["*"], | |
) | |
# Sửa phần khởi tạo Redis client và thêm biến để kiểm tra Redis status | |
redis_available = False | |
try: | |
redis_client = redis.Redis(host='localhost', port=6379, db=0) | |
redis_client.ping() # Kiểm tra kết nối | |
redis_available = True | |
except redis.ConnectionError: | |
print("Redis không khả dụng - hệ thống sẽ chạy không có cache") | |
redis_client = None | |
CACHE_EXPIRE_TIME = 300 # 5 minutes | |
# MongoDB connection | |
mongo_url = "mongodb+srv://ip6ofme:[email protected]/" | |
client = pymongo.MongoClient(mongo_url) | |
db = client["test"] | |
pdf_collection = db["PdfDetails"] | |
voter_collection = db["Voters"] | |
# Kiểm tra kết nối MongoDB khi khởi động | |
try: | |
# The ismaster command is cheap and does not require auth. | |
client.admin.command('ismaster') | |
except ConnectionFailure: | |
print("Server not available") | |
raise | |
# Pydantic models for request validation | |
class VoterRegistration(BaseModel): | |
name: str | |
group: str | |
role: str | |
class VoteRequest(BaseModel): | |
voter_id: str | |
file_id: str | |
vote_count: Optional[int] = 1 | |
class Config: | |
def validate_vote_count(cls, v): | |
if v <= 0: | |
raise ValueError('Vote count must be greater than 0') | |
return v | |
async def upload_file( | |
title: str = Form(...), | |
group: str = Form(...), | |
url: str = Form(...) | |
): | |
new_pdf = { | |
'title': title, | |
'group': group, | |
'url': url, | |
'votes': 0 | |
} | |
result = pdf_collection.insert_one(new_pdf) | |
return {'status': 'ok', 'id': str(result.inserted_id)} | |
async def get_votes(file_id: str): | |
try: | |
file = pdf_collection.find_one({'_id': ObjectId(file_id)}) | |
if not file: | |
raise HTTPException(status_code=404, detail="File not found") | |
return {'status': 'ok', 'votes': file.get('votes', 0)} | |
except Exception as e: | |
raise HTTPException(status_code=500, detail=str(e)) | |
async def get_files(): | |
try: | |
files = pdf_collection.find({}) | |
file_list = [ | |
{ | |
'id': str(file['_id']), | |
'title': file['title'], | |
'group': file['group'], | |
'url': file['url'], | |
'votes': file.get('votes', 0) | |
} | |
for file in files | |
] | |
return {'status': 'ok', 'data': file_list} | |
except Exception as e: | |
raise HTTPException(status_code=500, detail=str(e)) | |
async def register_voter(voter: VoterRegistration): | |
# Xác định số lượng vote tối đa dựa vào role | |
max_votes = 10 if voter.role == 'judge' else 2 | |
new_voter = { | |
'name': voter.name, | |
'group': voter.group, | |
'role': voter.role, | |
'number_of_votes': max_votes # Khởi tạo với số vote tối đa | |
} | |
result = voter_collection.insert_one(new_voter) | |
return {'status': 'ok', 'id': str(result.inserted_id)} | |
async def vote_by_voter(vote_request: VoteRequest): | |
if vote_request.vote_count <= 0: | |
raise HTTPException(status_code=400, detail="Vote count must be greater than 0") | |
voter = voter_collection.find_one({'_id': ObjectId(vote_request.voter_id)}) | |
if not voter: | |
raise HTTPException(status_code=404, detail="Voter not found") | |
remaining_votes = voter['number_of_votes'] | |
if vote_request.vote_count > remaining_votes: | |
raise HTTPException(status_code=400, detail=f"Not enough votes remaining. You have {remaining_votes} votes left") | |
try: | |
# Cập nhật MongoDB | |
voter_collection.update_one( | |
{'_id': ObjectId(vote_request.voter_id)}, | |
{'$inc': {'number_of_votes': -vote_request.vote_count}} | |
) | |
pdf_collection.update_one( | |
{'_id': ObjectId(vote_request.file_id)}, | |
{'$inc': {'votes': vote_request.vote_count}} | |
) | |
# Chỉ xóa cache nếu Redis khả dụng | |
if redis_available: | |
try: | |
redis_client.delete('all_files') | |
redis_client.delete(f'voter:{vote_request.voter_id}') | |
except redis.RedisError: | |
print("Không thể xóa cache Redis") | |
# Emit socket event | |
updated_file = pdf_collection.find_one({'_id': ObjectId(vote_request.file_id)}) | |
await socket_manager.emit('vote_update', { | |
'file_id': vote_request.file_id, | |
'votes': updated_file.get('votes', 0) | |
}) | |
return {'status': 'ok', 'message': f'Vote recorded successfully with {vote_request.vote_count} votes'} | |
except Exception as e: | |
raise HTTPException(status_code=500, detail=str(e)) | |
async def get_voter(id: str): | |
try: | |
# Chỉ check cache nếu Redis khả dụng | |
if redis_available: | |
try: | |
cached_voter = redis_client.get(f'voter:{id}') | |
if cached_voter: | |
return json.loads(cached_voter) | |
except redis.RedisError: | |
print("Không thể đọc cache Redis") | |
voter = voter_collection.find_one({'_id': ObjectId(id)}) | |
if not voter: | |
raise HTTPException(status_code=404, detail="Voter not found") | |
voter_data = { | |
'status': 'ok', | |
'name': voter['name'], | |
'group': voter['group'], | |
'role': voter['role'], | |
'number_of_votes': voter['number_of_votes'] | |
} | |
# Chỉ lưu cache nếu Redis khả dụng | |
if redis_available: | |
try: | |
redis_client.setex(f'voter:{id}', CACHE_EXPIRE_TIME, json.dumps(voter_data)) | |
except redis.RedisError: | |
print("Không thể lưu cache Redis") | |
return voter_data | |
except Exception as e: | |
print(f"Error in get_voter: {str(e)}") | |
raise HTTPException(status_code=500, detail=str(e)) | |
async def handle_connect(sid, environ): | |
print(f'Client connected: {sid}') | |
async def handle_disconnect(sid): | |
print(f'Client disconnected: {sid}') | |
async def index(): | |
return {'status': 'ok', 'message': 'Server is running'} | |
async def shutdown_event(): | |
# Close MongoDB connection | |
client.close() | |
# Close Redis connection if available | |
if redis_available and redis_client: | |
redis_client.close() | |
if __name__ == "__main__": | |
import uvicorn | |
uvicorn.run(app, host="0.0.0.0", port=5000) |