SHSH_Messenger

Форк
0
/
main.py 
510 строк · 18.3 Кб
1
from fastapi import FastAPI, Depends, HTTPException, Request, Form
2
from sqlalchemy.sql import func
3
from fastapi.responses import HTMLResponse, RedirectResponse
4
from fastapi.templating import Jinja2Templates
5
from fastapi import FastAPI, Depends, HTTPException, WebSocket, WebSocketDisconnect
6
from sqlalchemy.orm import Session
7
from models import Base, UploadedImage, engine, SessionLocal, User, Message, Chat, UserProfileModel
8
from pydantic import BaseModel
9
from passlib.context import CryptContext
10
from typing import List
11
import uvicorn
12
from fastapi.middleware.cors import CORSMiddleware
13

14
from fastapi import FastAPI, File, UploadFile
15
from fastapi.responses import JSONResponse
16
import shutil
17
import os
18

19
import uuid
20
from datetime import datetime
21

22
app = FastAPI()
23

24
origins = ["*"]
25
app.add_middleware(
26
    CORSMiddleware,
27
    allow_origins=origins,
28
    allow_credentials=True,
29
    allow_methods=["*"],
30
    allow_headers=["*"],
31
)
32

33
Base.metadata.create_all(bind=engine)
34
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
35

36
templates = Jinja2Templates(directory="templates")
37

38
class UserCreate(BaseModel):
39
    username: str
40
    password: str
41

42
class UserLogin(BaseModel):
43
    username: str
44
    password: str
45

46
class ProfileResponse(BaseModel):
47
    first_name: str
48
    last_name: str
49
    profile_photo: str
50

51
class UserResponse(BaseModel):
52
    id: int
53
    username: str
54
    profile: ProfileResponse
55

56
    class Config:
57
        orm_mode = True
58

59
class UserProfile(BaseModel):
60
    first_name: str
61
    last_name: str
62
    profile_photo: str
63

64
class UserProfileResponse(BaseModel):
65
    user_id: int
66
    first_name: str
67
    last_name: str
68
    profile_photo: str
69
    last_seen: str
70

71
    class Config:
72
        orm_mode = True
73

74
class MessageCreate(BaseModel):
75
    sender_id: int
76
    receiver_id: int
77
    chat_id: int
78
    content: str
79

80
class MessageResponse(BaseModel):
81
    id: int
82
    sender_id: int
83
    receiver_id: int
84
    content: str
85
    timestamp: str
86

87
class ChatCreate(BaseModel):
88
    user1_id: int
89
    user2_id: int
90

91
class ChatResponse(BaseModel):
92
    id: int
93
    user1_id: int
94
    user2_id: int
95
    name: str
96
    profileImage: str
97
    last_message: str
98
    timestamp: str
99
    is_read: bool
100

101
    class Config:
102
        orm_mode = True
103

104
class LastMessageUpdate(BaseModel):
105
    last_message: str
106
    timestamp: str
107

108

109
def get_db():
110
    db = SessionLocal()
111
    try:
112
        yield db
113
    finally:
114
        db.close()
115

116
@app.post("/login/")
117
def login(user: UserLogin, db: Session = Depends(get_db)):
118
    db_user = db.query(User).filter(User.username == user.username).first()
119
    if not db_user or not pwd_context.verify(user.password, db_user.password):
120
        raise HTTPException(status_code=400, detail="Invalid credentials")
121
    return {"message": "Login successful", "userId": db_user.id}
122

123
@app.post("/users/", response_model=UserResponse)
124
def create_user(user: UserCreate, db: Session = Depends(get_db)):
125
    hashed_password = pwd_context.hash(user.password)
126
    db_user = User(username=user.username, password=hashed_password)
127
    db.add(db_user)
128
    db.commit()
129
    db.refresh(db_user)
130
    db_profile = UserProfileModel(user_id=db_user.id, first_name="", last_name="", profile_photo="")
131
    db.add(db_profile)
132
    db.commit()
133
    db.refresh(db_profile)
134
    return UserResponse(
135
        id=db_user.id,
136
        username=db_user.username,
137
        profile=ProfileResponse(
138
            first_name=db_profile.first_name,
139
            last_name=db_profile.last_name,
140
            profile_photo=db_profile.profile_photo
141
        )
142
    )
143

144
@app.get("/users/", response_model=List[UserResponse])
145
def get_users(db: Session = Depends(get_db)):
146
    users = db.query(User).all()
147
    result = []
148
    for user in users:
149
        profile = db.query(UserProfileModel).filter(UserProfileModel.user_id == user.id).first()
150
        user_response = UserResponse(
151
            id=user.id,
152
            username=user.username,
153
            profile=ProfileResponse(
154
                first_name=profile.first_name,
155
                last_name=profile.last_name,
156
                profile_photo=profile.profile_photo
157
            ) if profile else None
158
        )
159
        result.append(user_response)
160
    return result
161

162
@app.put("/users/{user_id}/profile", response_model=UserProfileResponse)
163
def update_user_profile(user_id: int, profile_update: UserProfile, db: Session = Depends(get_db)):
164
    try:
165
        db_user = db.query(User).filter(User.id == user_id).first()
166
        if not db_user:
167
            raise HTTPException(status_code=404, detail="User not found")
168

169
        db_profile = db.query(UserProfileModel).filter(UserProfileModel.user_id == user_id).first()
170
        if not db_profile:
171
            db_profile = UserProfileModel(user_id=user_id, first_name=profile_update.first_name, last_name=profile_update.last_name, profile_photo=profile_update.profile_photo)
172
            db.add(db_profile)
173
        else:
174
            db_profile.first_name = profile_update.first_name
175
            db_profile.last_name = profile_update.last_name
176
            db_profile.profile_photo = profile_update.profile_photo
177

178
        db.commit()
179
        db.refresh(db_profile)
180

181
        return UserProfileResponse(
182
            user_id=db_profile.user_id,
183
            first_name=db_profile.first_name,
184
            last_name=db_profile.last_name,
185
            profile_photo=db_profile.profile_photo,
186
            last_seen=str(db_user.last_seen))
187
    except Exception as e:
188
        # Логирование ошибки для дальнейшего анализа
189
        print(f"Failed to update user profile for user ID {user_id}: {str(e)}")
190
        return {"success": False, "error": str(e)}
191

192
@app.get("/users/{user_id}/profile", response_model=UserProfileResponse)
193
def get_user_profile(user_id: int, db: Session = Depends(get_db)):
194
    user = db.query(User).filter(User.id == user_id).first()
195
    if user is None:
196
        raise HTTPException(status_code=404, detail="User not found")
197
    profile = db.query(UserProfileModel).filter(UserProfileModel.user_id == user_id).first()
198
    if profile is None:
199
        raise HTTPException(status_code=404, detail="Profile not found")
200
    return UserProfileResponse(
201
        user_id=user.id,
202
        first_name=profile.first_name,
203
        last_name=profile.last_name,
204
        profile_photo=profile.profile_photo,
205
        last_seen=getTime()   # Преобразование datetime в строку
206
    )
207

208
@app.put("/chats/{chat_id}/last_message")
209
def update_last_message(chat_id: int, last_message_update: LastMessageUpdate, db: Session = Depends(get_db)):
210
    chat = db.query(Chat).filter(Chat.id == chat_id).first()
211
    if not chat:
212
        raise HTTPException(status_code=404, detail="Chat not found")
213
    
214
    logging.info(f"Updating chat ID {chat_id} with last_message: {last_message_update.last_message}, timestamp: {last_message_update.timestamp}")
215
    chat.last_message = last_message_update.last_message
216
    chat.timestamp = last_message_update.timestamp
217

218
    try:
219
        db.commit()
220
        logging.info(f"Chat ID {chat_id} successfully updated in the database")
221
    except Exception as e:
222
        db.rollback()
223
        logging.error(f"Failed to update chat ID {chat_id}: {e}")
224
        raise HTTPException(status_code=500, detail="Failed to update last message")
225

226
    return {"message": "Last message updated successfully"}
227

228

229

230
import logging
231

232
@app.post("/messages/", response_model=MessageResponse)
233
def create_message(message_create: MessageCreate, db: Session = Depends(get_db)):
234
    db_message = Message(
235
        sender_id=message_create.sender_id,
236
        receiver_id=message_create.receiver_id,
237
        chat_id=message_create.chat_id,
238
        content=message_create.content
239
    )
240
    db.add(db_message)
241
    db.commit()
242
    db.refresh(db_message)
243
    
244
    # Логирование
245
    logging.info(f"Создано сообщение ID {db_message.id} в чате {db_message.chat_id}")
246

247
    # Обновление последнего сообщения в чате
248
    chat = db.query(Chat).filter(Chat.id == message_create.chat_id).first()
249
    if chat:
250
        chat.last_message = db_message.content
251
        chat.timestamp = db_message.timestamp
252
        db.commit()
253
        logging.info(f"Обновлено последнее сообщение для чата ID {chat.id}: {chat.last_message} на {chat.timestamp}")
254
    
255
    return MessageResponse(
256
        id=db_message.id,
257
        sender_id=db_message.sender_id,
258
        receiver_id=db_message.receiver_id,
259
        content=db_message.content,
260
        timestamp=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])
267
def get_messages(chat_id: int, db: Session = Depends(get_db)):
268
    messages = db.query(Message).filter(Message.chat_id == chat_id).all()
269
    return [
270
        MessageResponse(
271
            id=message.id,
272
            sender_id=message.sender_id,
273
            receiver_id=message.receiver_id,
274
            content=message.content,
275
            timestamp=str(message.timestamp.strftime("%Y-%m-%d %H:%M:%S"))
276
        ) for message in messages
277
    ]
278

279
@app.post("/chats/", response_model=ChatResponse)
280
def create_chat(chat: ChatCreate, db: Session = Depends(get_db)):
281
    existing_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()
285
    if existing_chat:
286
        last_message = db.query(Message).filter(Message.chat_id == existing_chat.id).order_by(Message.timestamp.desc()).first()
287
        chat_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()
288
        profile = db.query(UserProfileModel).filter(UserProfileModel.user_id == chat_user.id).first()
289
        return ChatResponse(
290
            id=existing_chat.id,
291
            user1_id=existing_chat.user1_id,
292
            user2_id=existing_chat.user2_id,
293
            name=chat_user.username,
294
            profileImage=profile.profile_photo,
295
            last_message=last_message.content if last_message else "",
296
            timestamp=(last_message.timestamp.strftime("%Y-%m-%d %H:%M:%S")) if last_message else "",
297
            is_read=last_message.sender_id != chat.user1_id if last_message else True
298
        )
299
    db_chat = Chat(user1_id=chat.user1_id, user2_id=chat.user2_id)
300
    db.add(db_chat)
301
    db.commit()
302
    db.refresh(db_chat)
303
    chat_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()
304
    profile = db.query(UserProfileModel).filter(UserProfileModel.user_id == chat_user.id).first()
305
    return ChatResponse(
306
        id=db_chat.id,
307
        user1_id=db_chat.user1_id,
308
        user2_id=db_chat.user2_id,
309
        name=chat_user.username,
310
        profileImage=profile.profile_photo,
311
        last_message="_",
312
        timestamp="",
313
        is_read=True
314
    )
315

316
@app.get("/chats/{user_id}", response_model=List[ChatResponse])
317
def get_chats(user_id: int, db: Session = Depends(get_db)):
318
    chats = db.query(Chat).filter((Chat.user1_id == user_id) | (Chat.user2_id == user_id)).all()
319
    chat_list = []
320

321
    for chat in chats:
322
        last_message = db.query(Message).filter(Message.chat_id == chat.id).order_by(Message.timestamp.desc()).first()
323
        chat_user = db.query(User).filter(User.id == (chat.user1_id if chat.user1_id != user_id else chat.user2_id)).first()
324
        profile = db.query(UserProfileModel).filter(UserProfileModel.user_id == chat_user.id).first()
325
        chat_list.append(ChatResponse(
326
            id=chat.id,
327
            user1_id=chat.user1_id,
328
            user2_id=chat.user2_id,
329
            name=f"{profile.first_name} {profile.last_name}",
330
            profileImage=profile.profile_photo,
331
            last_message=last_message.content if last_message else "",
332
            timestamp=str(last_message.timestamp.strftime("%Y-%m-%d %H:%M:%S")) if last_message else "",
333
            is_read=last_message.sender_id != user_id if last_message else True
334
        ))
335

336
    logging.info(f"Получены чаты для пользователя ID {user_id}: {chat_list}")
337

338
    return chat_list
339

340

341

342

343
@app.put("/users/{user_id}/last_seen")
344
def update_last_seen(user_id: int, db: Session = Depends(get_db)):
345
    db_user = db.query(User).filter(User.id == user_id).first()
346
    if not db_user:
347
        raise HTTPException(status_code=404, detail="User not found")
348
    db_user.last_seen = getTime() 
349
    db.commit()
350
    return {"message": "Last seen updated"}
351

352
# --- ADMIN PANEL ---
353
@app.get("/admin/login", response_class=HTMLResponse)
354
def admin_login_page(request: Request):
355
    return templates.TemplateResponse("login.html", {"request": request})
356

357
@app.post("/admin/login", response_class=HTMLResponse)
358
def admin_login(request: Request, password: str = Form(...)):
359
    if password == "admin":
360
        response = RedirectResponse(url="/admin/users", status_code=302)
361
        response.set_cookie(key="admin", value="true")
362
        return response
363
    else:
364
        return templates.TemplateResponse("login.html", {"request": request, "error": "Invalid password"})
365

366
def verify_admin(request: Request):
367
    if request.cookies.get("admin") != "true":
368
        raise HTTPException(status_code=403, detail="Not authenticated")
369

370
@app.get("/admin/users", response_class=HTMLResponse)
371
def admin_get_users(request: Request, db: Session = Depends(get_db)):
372
    verify_admin(request)
373
    users = db.query(User).all()
374
    return templates.TemplateResponse("users.html", {"request": request, "users": users})
375

376
@app.get("/admin/messages", response_class=HTMLResponse)
377
def admin_get_messages(request: Request, db: Session = Depends(get_db)):
378
    verify_admin(request)
379
    messages = db.query(Message).all()
380
    return templates.TemplateResponse("messages.html", {"request": request, "messages": messages})
381

382
@app.get("/admin/chats", response_class=HTMLResponse)
383
def admin_get_chats(request: Request, db: Session = Depends(get_db)):
384
    verify_admin(request)
385
    chats = db.query(Chat).all()
386
    return templates.TemplateResponse("chats.html", {"request": request, "chats": chats})
387

388
from fastapi.staticfiles import StaticFiles
389

390
app.mount("/uploaded_images", StaticFiles(directory="uploaded_images"), name="uploaded_images")
391

392

393
@app.get("/admin/images", response_class=HTMLResponse)
394
def admin_get_images(request: Request, db: Session = Depends(get_db)):
395
    verify_admin(request)
396
    images = db.query(UploadedImage).all()
397
    return templates.TemplateResponse("images.html", {"request": request, "images": images})
398

399

400
# Profile Photo Upload Directory
401
UPLOAD_DIRECTORY = "./uploaded_images/"
402

403
# Ensure the upload directory exists
404
if not os.path.exists(UPLOAD_DIRECTORY):
405
    os.makedirs(UPLOAD_DIRECTORY)
406
    logging.info(f"Upload directory created at {UPLOAD_DIRECTORY}")
407

408

409
@app.post("/upload-image/")
410
async def upload_image(file: UploadFile = File(...), db: Session = Depends(get_db)):
411
    try:
412
        unique_filename = f"{datetime.now().strftime('%Y%m%d%H%M%S')}_{uuid.uuid4().hex}_{file.filename}"
413
        file_location = os.path.join(UPLOAD_DIRECTORY, unique_filename)
414
        
415
        with open(file_location, "wb") as buffer:
416
            shutil.copyfileobj(file.file, buffer)
417

418
        # Log the file path
419
        logging.info(f"File saved to {file_location}")
420

421
        # Save image info to the database
422
        uploaded_image = UploadedImage(filename=unique_filename, file_path=file_location)
423
        db.add(uploaded_image)
424
        db.commit()
425

426
        return JSONResponse(content={"filePath": file_location})
427
    except Exception as e:
428
        logging.error(f"Failed to upload image: {e}")
429
        raise HTTPException(status_code=500, detail="Failed to upload image")
430

431
from fastapi.responses import StreamingResponse
432

433
from fastapi.responses import StreamingResponse, FileResponse
434

435
import logging
436

437
logging.basicConfig(level=logging.INFO)
438

439
@app.put("/users/{user_id}/profile", response_model=UserProfileResponse)
440
def update_user_profile(user_id: int, profile_update: UserProfile, db: Session = Depends(get_db)):
441
    try:
442
        db_user = db.query(User).filter(User.id == user_id).first()
443
        if not db_user:
444
            raise HTTPException(status_code=404, detail="User not found")
445

446
        db_profile = db.query(UserProfileModel).filter(UserProfileModel.user_id == user_id).first()
447
        if not db_profile:
448
            db_profile = UserProfileModel(user_id=user_id, first_name=profile_update.first_name, last_name=profile_update.last_name, profile_photo=profile_update.profile_photo)
449
            db.add(db_profile)
450
        else:
451
            db_profile.first_name = profile_update.first_name
452
            db_profile.last_name = profile_update.last_name
453
            db_profile.profile_photo = profile_update.profile_photo
454

455
        db.commit()
456
        db.refresh(db_profile)
457
        
458
        return UserProfileResponse(
459
            user_id=db_profile.user_id,
460
            first_name=db_profile.first_name,
461
            last_name=db_profile.last_name,
462
            profile_photo=db_profile.profile_photo,
463
            last_seen=getTime() 
464
        )
465
    except Exception as e:
466
        logging.error(f"Failed to update user profile for user ID {user_id}: {str(e)}")
467
        raise HTTPException(status_code=500, detail=str(e))
468

469

470
@app.get("/users/{user_id}/profile-image")
471
def get_profile_image(user_id: int, db: Session = Depends(get_db)):
472
    profile = db.query(UserProfileModel).filter(UserProfileModel.user_id == user_id).first()
473
    if profile is None or not profile.profile_photo:
474
        raise HTTPException(status_code=404, detail="Profile photo not found")
475

476
    file_path = os.path.join(profile.profile_photo)
477
    if not os.path.exists(file_path):
478
        raise HTTPException(status_code=404, detail="Profile photo not found")
479

480
    return FileResponse(file_path)
481

482

483

484
# WebSocket logic
485
connected_clients = {}
486

487
@app.websocket("/ws/{chat_id}/{user_id}")
488
async def websocket_endpoint(websocket: WebSocket, chat_id: int, user_id: int):
489
    await websocket.accept()
490
    if chat_id not in connected_clients:
491
        connected_clients[chat_id] = {}
492
    connected_clients[chat_id][user_id] = websocket
493
    
494
    try:
495
        while True:
496
            data = await websocket.receive_text()
497
            for uid, client in connected_clients[chat_id].items():
498
                if uid != user_id:
499
                    await client.send_text(data)
500
    except WebSocketDisconnect:
501
        del connected_clients[chat_id][user_id]
502
        if not connected_clients[chat_id]:
503
            del connected_clients[chat_id]
504

505

506
def getTime() -> str:
507
    return datetime.now().strftime('%H:%M')
508

509
if __name__ == "__main__":
510
    uvicorn.run(app, host="0.0.0.0", port=8000)
511

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.