SHSH_Messenger
/
main.py
510 строк · 18.3 Кб
1from fastapi import FastAPI, Depends, HTTPException, Request, Form
2from sqlalchemy.sql import func
3from fastapi.responses import HTMLResponse, RedirectResponse
4from fastapi.templating import Jinja2Templates
5from fastapi import FastAPI, Depends, HTTPException, WebSocket, WebSocketDisconnect
6from sqlalchemy.orm import Session
7from models import Base, UploadedImage, engine, SessionLocal, User, Message, Chat, UserProfileModel
8from pydantic import BaseModel
9from passlib.context import CryptContext
10from typing import List
11import uvicorn
12from fastapi.middleware.cors import CORSMiddleware
13
14from fastapi import FastAPI, File, UploadFile
15from fastapi.responses import JSONResponse
16import shutil
17import os
18
19import uuid
20from datetime import datetime
21
22app = FastAPI()
23
24origins = ["*"]
25app.add_middleware(
26CORSMiddleware,
27allow_origins=origins,
28allow_credentials=True,
29allow_methods=["*"],
30allow_headers=["*"],
31)
32
33Base.metadata.create_all(bind=engine)
34pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
35
36templates = Jinja2Templates(directory="templates")
37
38class UserCreate(BaseModel):
39username: str
40password: str
41
42class UserLogin(BaseModel):
43username: str
44password: str
45
46class ProfileResponse(BaseModel):
47first_name: str
48last_name: str
49profile_photo: str
50
51class UserResponse(BaseModel):
52id: int
53username: str
54profile: ProfileResponse
55
56class Config:
57orm_mode = True
58
59class UserProfile(BaseModel):
60first_name: str
61last_name: str
62profile_photo: str
63
64class UserProfileResponse(BaseModel):
65user_id: int
66first_name: str
67last_name: str
68profile_photo: str
69last_seen: str
70
71class Config:
72orm_mode = True
73
74class MessageCreate(BaseModel):
75sender_id: int
76receiver_id: int
77chat_id: int
78content: str
79
80class MessageResponse(BaseModel):
81id: int
82sender_id: int
83receiver_id: int
84content: str
85timestamp: str
86
87class ChatCreate(BaseModel):
88user1_id: int
89user2_id: int
90
91class ChatResponse(BaseModel):
92id: int
93user1_id: int
94user2_id: int
95name: str
96profileImage: str
97last_message: str
98timestamp: str
99is_read: bool
100
101class Config:
102orm_mode = True
103
104class LastMessageUpdate(BaseModel):
105last_message: str
106timestamp: str
107
108
109def get_db():
110db = SessionLocal()
111try:
112yield db
113finally:
114db.close()
115
116@app.post("/login/")
117def login(user: UserLogin, db: Session = Depends(get_db)):
118db_user = db.query(User).filter(User.username == user.username).first()
119if not db_user or not pwd_context.verify(user.password, db_user.password):
120raise HTTPException(status_code=400, detail="Invalid credentials")
121return {"message": "Login successful", "userId": db_user.id}
122
123@app.post("/users/", response_model=UserResponse)
124def create_user(user: UserCreate, db: Session = Depends(get_db)):
125hashed_password = pwd_context.hash(user.password)
126db_user = User(username=user.username, password=hashed_password)
127db.add(db_user)
128db.commit()
129db.refresh(db_user)
130db_profile = UserProfileModel(user_id=db_user.id, first_name="", last_name="", profile_photo="")
131db.add(db_profile)
132db.commit()
133db.refresh(db_profile)
134return UserResponse(
135id=db_user.id,
136username=db_user.username,
137profile=ProfileResponse(
138first_name=db_profile.first_name,
139last_name=db_profile.last_name,
140profile_photo=db_profile.profile_photo
141)
142)
143
144@app.get("/users/", response_model=List[UserResponse])
145def get_users(db: Session = Depends(get_db)):
146users = db.query(User).all()
147result = []
148for user in users:
149profile = db.query(UserProfileModel).filter(UserProfileModel.user_id == user.id).first()
150user_response = UserResponse(
151id=user.id,
152username=user.username,
153profile=ProfileResponse(
154first_name=profile.first_name,
155last_name=profile.last_name,
156profile_photo=profile.profile_photo
157) if profile else None
158)
159result.append(user_response)
160return result
161
162@app.put("/users/{user_id}/profile", response_model=UserProfileResponse)
163def update_user_profile(user_id: int, profile_update: UserProfile, db: Session = Depends(get_db)):
164try:
165db_user = db.query(User).filter(User.id == user_id).first()
166if not db_user:
167raise HTTPException(status_code=404, detail="User not found")
168
169db_profile = db.query(UserProfileModel).filter(UserProfileModel.user_id == user_id).first()
170if not db_profile:
171db_profile = UserProfileModel(user_id=user_id, first_name=profile_update.first_name, last_name=profile_update.last_name, profile_photo=profile_update.profile_photo)
172db.add(db_profile)
173else:
174db_profile.first_name = profile_update.first_name
175db_profile.last_name = profile_update.last_name
176db_profile.profile_photo = profile_update.profile_photo
177
178db.commit()
179db.refresh(db_profile)
180
181return UserProfileResponse(
182user_id=db_profile.user_id,
183first_name=db_profile.first_name,
184last_name=db_profile.last_name,
185profile_photo=db_profile.profile_photo,
186last_seen=str(db_user.last_seen))
187except Exception as e:
188# Логирование ошибки для дальнейшего анализа
189print(f"Failed to update user profile for user ID {user_id}: {str(e)}")
190return {"success": False, "error": str(e)}
191
192@app.get("/users/{user_id}/profile", response_model=UserProfileResponse)
193def get_user_profile(user_id: int, db: Session = Depends(get_db)):
194user = db.query(User).filter(User.id == user_id).first()
195if user is None:
196raise HTTPException(status_code=404, detail="User not found")
197profile = db.query(UserProfileModel).filter(UserProfileModel.user_id == user_id).first()
198if profile is None:
199raise HTTPException(status_code=404, detail="Profile not found")
200return UserProfileResponse(
201user_id=user.id,
202first_name=profile.first_name,
203last_name=profile.last_name,
204profile_photo=profile.profile_photo,
205last_seen=getTime() # Преобразование datetime в строку
206)
207
208@app.put("/chats/{chat_id}/last_message")
209def update_last_message(chat_id: int, last_message_update: LastMessageUpdate, db: Session = Depends(get_db)):
210chat = db.query(Chat).filter(Chat.id == chat_id).first()
211if not chat:
212raise HTTPException(status_code=404, detail="Chat not found")
213
214logging.info(f"Updating chat ID {chat_id} with last_message: {last_message_update.last_message}, timestamp: {last_message_update.timestamp}")
215chat.last_message = last_message_update.last_message
216chat.timestamp = last_message_update.timestamp
217
218try:
219db.commit()
220logging.info(f"Chat ID {chat_id} successfully updated in the database")
221except Exception as e:
222db.rollback()
223logging.error(f"Failed to update chat ID {chat_id}: {e}")
224raise HTTPException(status_code=500, detail="Failed to update last message")
225
226return {"message": "Last message updated successfully"}
227
228
229
230import logging
231
232@app.post("/messages/", response_model=MessageResponse)
233def create_message(message_create: MessageCreate, db: Session = Depends(get_db)):
234db_message = Message(
235sender_id=message_create.sender_id,
236receiver_id=message_create.receiver_id,
237chat_id=message_create.chat_id,
238content=message_create.content
239)
240db.add(db_message)
241db.commit()
242db.refresh(db_message)
243
244# Логирование
245logging.info(f"Создано сообщение ID {db_message.id} в чате {db_message.chat_id}")
246
247# Обновление последнего сообщения в чате
248chat = db.query(Chat).filter(Chat.id == message_create.chat_id).first()
249if chat:
250chat.last_message = db_message.content
251chat.timestamp = db_message.timestamp
252db.commit()
253logging.info(f"Обновлено последнее сообщение для чата ID {chat.id}: {chat.last_message} на {chat.timestamp}")
254
255return MessageResponse(
256id=db_message.id,
257sender_id=db_message.sender_id,
258receiver_id=db_message.receiver_id,
259content=db_message.content,
260timestamp=str(db_message.timestamp.strftime("%Y-%m-%d %H:%M:%S"))
261)
262
263
264
265
266@app.get("/messages/{chat_id}", response_model=List[MessageResponse])
267def get_messages(chat_id: int, db: Session = Depends(get_db)):
268messages = db.query(Message).filter(Message.chat_id == chat_id).all()
269return [
270MessageResponse(
271id=message.id,
272sender_id=message.sender_id,
273receiver_id=message.receiver_id,
274content=message.content,
275timestamp=str(message.timestamp.strftime("%Y-%m-%d %H:%M:%S"))
276) for message in messages
277]
278
279@app.post("/chats/", response_model=ChatResponse)
280def create_chat(chat: ChatCreate, db: Session = Depends(get_db)):
281existing_chat = db.query(Chat).filter(
282((Chat.user1_id == chat.user1_id) & (Chat.user2_id == chat.user2_id)) |
283((Chat.user1_id == chat.user2_id) & (Chat.user2_id == chat.user1_id))
284).first()
285if existing_chat:
286last_message = db.query(Message).filter(Message.chat_id == existing_chat.id).order_by(Message.timestamp.desc()).first()
287chat_user = db.query(User).filter(User.id == (existing_chat.user1_id if existing_chat.user1_id != chat.user1_id else existing_chat.user2_id)).first()
288profile = db.query(UserProfileModel).filter(UserProfileModel.user_id == chat_user.id).first()
289return ChatResponse(
290id=existing_chat.id,
291user1_id=existing_chat.user1_id,
292user2_id=existing_chat.user2_id,
293name=chat_user.username,
294profileImage=profile.profile_photo,
295last_message=last_message.content if last_message else "",
296timestamp=(last_message.timestamp.strftime("%Y-%m-%d %H:%M:%S")) if last_message else "",
297is_read=last_message.sender_id != chat.user1_id if last_message else True
298)
299db_chat = Chat(user1_id=chat.user1_id, user2_id=chat.user2_id)
300db.add(db_chat)
301db.commit()
302db.refresh(db_chat)
303chat_user = db.query(User).filter(User.id == (db_chat.user1_id if db_chat.user1_id != chat.user1_id else db_chat.user2_id)).first()
304profile = db.query(UserProfileModel).filter(UserProfileModel.user_id == chat_user.id).first()
305return ChatResponse(
306id=db_chat.id,
307user1_id=db_chat.user1_id,
308user2_id=db_chat.user2_id,
309name=chat_user.username,
310profileImage=profile.profile_photo,
311last_message="_",
312timestamp="",
313is_read=True
314)
315
316@app.get("/chats/{user_id}", response_model=List[ChatResponse])
317def get_chats(user_id: int, db: Session = Depends(get_db)):
318chats = db.query(Chat).filter((Chat.user1_id == user_id) | (Chat.user2_id == user_id)).all()
319chat_list = []
320
321for chat in chats:
322last_message = db.query(Message).filter(Message.chat_id == chat.id).order_by(Message.timestamp.desc()).first()
323chat_user = db.query(User).filter(User.id == (chat.user1_id if chat.user1_id != user_id else chat.user2_id)).first()
324profile = db.query(UserProfileModel).filter(UserProfileModel.user_id == chat_user.id).first()
325chat_list.append(ChatResponse(
326id=chat.id,
327user1_id=chat.user1_id,
328user2_id=chat.user2_id,
329name=f"{profile.first_name} {profile.last_name}",
330profileImage=profile.profile_photo,
331last_message=last_message.content if last_message else "",
332timestamp=str(last_message.timestamp.strftime("%Y-%m-%d %H:%M:%S")) if last_message else "",
333is_read=last_message.sender_id != user_id if last_message else True
334))
335
336logging.info(f"Получены чаты для пользователя ID {user_id}: {chat_list}")
337
338return chat_list
339
340
341
342
343@app.put("/users/{user_id}/last_seen")
344def update_last_seen(user_id: int, db: Session = Depends(get_db)):
345db_user = db.query(User).filter(User.id == user_id).first()
346if not db_user:
347raise HTTPException(status_code=404, detail="User not found")
348db_user.last_seen = getTime()
349db.commit()
350return {"message": "Last seen updated"}
351
352# --- ADMIN PANEL ---
353@app.get("/admin/login", response_class=HTMLResponse)
354def admin_login_page(request: Request):
355return templates.TemplateResponse("login.html", {"request": request})
356
357@app.post("/admin/login", response_class=HTMLResponse)
358def admin_login(request: Request, password: str = Form(...)):
359if password == "admin":
360response = RedirectResponse(url="/admin/users", status_code=302)
361response.set_cookie(key="admin", value="true")
362return response
363else:
364return templates.TemplateResponse("login.html", {"request": request, "error": "Invalid password"})
365
366def verify_admin(request: Request):
367if request.cookies.get("admin") != "true":
368raise HTTPException(status_code=403, detail="Not authenticated")
369
370@app.get("/admin/users", response_class=HTMLResponse)
371def admin_get_users(request: Request, db: Session = Depends(get_db)):
372verify_admin(request)
373users = db.query(User).all()
374return templates.TemplateResponse("users.html", {"request": request, "users": users})
375
376@app.get("/admin/messages", response_class=HTMLResponse)
377def admin_get_messages(request: Request, db: Session = Depends(get_db)):
378verify_admin(request)
379messages = db.query(Message).all()
380return templates.TemplateResponse("messages.html", {"request": request, "messages": messages})
381
382@app.get("/admin/chats", response_class=HTMLResponse)
383def admin_get_chats(request: Request, db: Session = Depends(get_db)):
384verify_admin(request)
385chats = db.query(Chat).all()
386return templates.TemplateResponse("chats.html", {"request": request, "chats": chats})
387
388from fastapi.staticfiles import StaticFiles
389
390app.mount("/uploaded_images", StaticFiles(directory="uploaded_images"), name="uploaded_images")
391
392
393@app.get("/admin/images", response_class=HTMLResponse)
394def admin_get_images(request: Request, db: Session = Depends(get_db)):
395verify_admin(request)
396images = db.query(UploadedImage).all()
397return templates.TemplateResponse("images.html", {"request": request, "images": images})
398
399
400# Profile Photo Upload Directory
401UPLOAD_DIRECTORY = "./uploaded_images/"
402
403# Ensure the upload directory exists
404if not os.path.exists(UPLOAD_DIRECTORY):
405os.makedirs(UPLOAD_DIRECTORY)
406logging.info(f"Upload directory created at {UPLOAD_DIRECTORY}")
407
408
409@app.post("/upload-image/")
410async def upload_image(file: UploadFile = File(...), db: Session = Depends(get_db)):
411try:
412unique_filename = f"{datetime.now().strftime('%Y%m%d%H%M%S')}_{uuid.uuid4().hex}_{file.filename}"
413file_location = os.path.join(UPLOAD_DIRECTORY, unique_filename)
414
415with open(file_location, "wb") as buffer:
416shutil.copyfileobj(file.file, buffer)
417
418# Log the file path
419logging.info(f"File saved to {file_location}")
420
421# Save image info to the database
422uploaded_image = UploadedImage(filename=unique_filename, file_path=file_location)
423db.add(uploaded_image)
424db.commit()
425
426return JSONResponse(content={"filePath": file_location})
427except Exception as e:
428logging.error(f"Failed to upload image: {e}")
429raise HTTPException(status_code=500, detail="Failed to upload image")
430
431from fastapi.responses import StreamingResponse
432
433from fastapi.responses import StreamingResponse, FileResponse
434
435import logging
436
437logging.basicConfig(level=logging.INFO)
438
439@app.put("/users/{user_id}/profile", response_model=UserProfileResponse)
440def update_user_profile(user_id: int, profile_update: UserProfile, db: Session = Depends(get_db)):
441try:
442db_user = db.query(User).filter(User.id == user_id).first()
443if not db_user:
444raise HTTPException(status_code=404, detail="User not found")
445
446db_profile = db.query(UserProfileModel).filter(UserProfileModel.user_id == user_id).first()
447if not db_profile:
448db_profile = UserProfileModel(user_id=user_id, first_name=profile_update.first_name, last_name=profile_update.last_name, profile_photo=profile_update.profile_photo)
449db.add(db_profile)
450else:
451db_profile.first_name = profile_update.first_name
452db_profile.last_name = profile_update.last_name
453db_profile.profile_photo = profile_update.profile_photo
454
455db.commit()
456db.refresh(db_profile)
457
458return UserProfileResponse(
459user_id=db_profile.user_id,
460first_name=db_profile.first_name,
461last_name=db_profile.last_name,
462profile_photo=db_profile.profile_photo,
463last_seen=getTime()
464)
465except Exception as e:
466logging.error(f"Failed to update user profile for user ID {user_id}: {str(e)}")
467raise HTTPException(status_code=500, detail=str(e))
468
469
470@app.get("/users/{user_id}/profile-image")
471def get_profile_image(user_id: int, db: Session = Depends(get_db)):
472profile = db.query(UserProfileModel).filter(UserProfileModel.user_id == user_id).first()
473if profile is None or not profile.profile_photo:
474raise HTTPException(status_code=404, detail="Profile photo not found")
475
476file_path = os.path.join(profile.profile_photo)
477if not os.path.exists(file_path):
478raise HTTPException(status_code=404, detail="Profile photo not found")
479
480return FileResponse(file_path)
481
482
483
484# WebSocket logic
485connected_clients = {}
486
487@app.websocket("/ws/{chat_id}/{user_id}")
488async def websocket_endpoint(websocket: WebSocket, chat_id: int, user_id: int):
489await websocket.accept()
490if chat_id not in connected_clients:
491connected_clients[chat_id] = {}
492connected_clients[chat_id][user_id] = websocket
493
494try:
495while True:
496data = await websocket.receive_text()
497for uid, client in connected_clients[chat_id].items():
498if uid != user_id:
499await client.send_text(data)
500except WebSocketDisconnect:
501del connected_clients[chat_id][user_id]
502if not connected_clients[chat_id]:
503del connected_clients[chat_id]
504
505
506def getTime() -> str:
507return datetime.now().strftime('%H:%M')
508
509if __name__ == "__main__":
510uvicorn.run(app, host="0.0.0.0", port=8000)
511