|
|
|
@ -1,8 +1,8 @@
|
|
|
|
|
from fastapi import FastAPI, Depends, HTTPException, status
|
|
|
|
|
from fastapi.responses import JSONResponse
|
|
|
|
|
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
|
|
|
|
|
from typing import Optional
|
|
|
|
|
from datetime import datetime, timedelta
|
|
|
|
|
from pymongo import MongoClient
|
|
|
|
|
from jose import JWTError, jwt
|
|
|
|
|
from bson.objectid import ObjectId
|
|
|
|
|
from app.utils import get_password_hash, verify_password
|
|
|
|
@ -20,8 +20,10 @@ from app.dto import *
|
|
|
|
|
# Contains all constants
|
|
|
|
|
from app.config import *
|
|
|
|
|
|
|
|
|
|
import pymongo
|
|
|
|
|
|
|
|
|
|
# Database setup
|
|
|
|
|
client = MongoClient(MONGODB_URL, username=MONGODB_USERNAME, password=MONGODB_PASSWORD)
|
|
|
|
|
client = pymongo.MongoClient(MONGODB_URL, username=MONGODB_USERNAME, password=MONGODB_PASSWORD)
|
|
|
|
|
db = client[MONGODB_DATABASE]
|
|
|
|
|
|
|
|
|
|
# FastAPI app instance
|
|
|
|
@ -73,7 +75,11 @@ async def get_current_user(token: str = Depends(oauth2_scheme)) -> User:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Routes - TODO: find workaround to display 401/409/... HTTP error codes in openapi.json
|
|
|
|
|
@app.post("/register", response_model=Token)
|
|
|
|
|
@app.post(
|
|
|
|
|
path="/register",
|
|
|
|
|
response_model=Token,
|
|
|
|
|
responses={409: {"model": Message}}
|
|
|
|
|
)
|
|
|
|
|
async def register(user: UserRegisterDTO):
|
|
|
|
|
user_exists = users_collection.find_one({"username": user.username})
|
|
|
|
|
if user_exists:
|
|
|
|
@ -90,7 +96,11 @@ async def register(user: UserRegisterDTO):
|
|
|
|
|
|
|
|
|
|
return {"access_token": access_token, "token_type": "bearer", "user_id": str(user["_id"])}
|
|
|
|
|
|
|
|
|
|
@app.post("/login", response_model=Token)
|
|
|
|
|
@app.post(
|
|
|
|
|
path="/login",
|
|
|
|
|
response_model=Token,
|
|
|
|
|
responses={401: {"model": Message}}
|
|
|
|
|
)
|
|
|
|
|
async def login(form_data: OAuth2PasswordRequestForm = Depends()):
|
|
|
|
|
user = users_collection.find_one({"username": form_data.username})
|
|
|
|
|
if not user or not verify_password(form_data.password, user["password"]):
|
|
|
|
@ -105,12 +115,18 @@ async def login(form_data: OAuth2PasswordRequestForm = Depends()):
|
|
|
|
|
|
|
|
|
|
return {"access_token": access_token, "token_type": "bearer", "user_id": str(user["_id"])}
|
|
|
|
|
|
|
|
|
|
@app.get("/logout")
|
|
|
|
|
@app.get(
|
|
|
|
|
path="/logout",
|
|
|
|
|
responses={401: {"model": Message}}
|
|
|
|
|
)
|
|
|
|
|
async def logout(current_user: User = Depends(get_current_user)):
|
|
|
|
|
# TODO: find usecase / what to do ??
|
|
|
|
|
return {"message": "Logged out"}
|
|
|
|
|
|
|
|
|
|
@app.get("/pin/{id}")
|
|
|
|
|
@app.get(
|
|
|
|
|
path="/pin/{id}",
|
|
|
|
|
responses={401: {"model": Message}, 404: {"model": Message}}
|
|
|
|
|
)
|
|
|
|
|
async def get_pin(id: str, current_user: User = Depends(get_current_user)):
|
|
|
|
|
pin = pins_collection.find_one({"_id": ObjectId(id)})
|
|
|
|
|
if pin is None:
|
|
|
|
@ -118,7 +134,10 @@ async def get_pin(id: str, current_user: User = Depends(get_current_user)):
|
|
|
|
|
|
|
|
|
|
return pin
|
|
|
|
|
|
|
|
|
|
@app.patch("/pin/{id}")
|
|
|
|
|
@app.patch(
|
|
|
|
|
path="/pin/{id}",
|
|
|
|
|
responses={401: {"model": Message}, 404: {"model": Message}}
|
|
|
|
|
)
|
|
|
|
|
async def update_pin(id: str, pin: Pin, current_user: User = Depends(get_current_user)):
|
|
|
|
|
result = pins_collection.update_one({"_id": ObjectId(id)}, {"$set": pin.model_dump()})
|
|
|
|
|
if result.matched_count == 0:
|
|
|
|
@ -126,17 +145,26 @@ async def update_pin(id: str, pin: Pin, current_user: User = Depends(get_current
|
|
|
|
|
|
|
|
|
|
return {"message": "Pin updated"}
|
|
|
|
|
|
|
|
|
|
@app.post("/pin/add")
|
|
|
|
|
@app.post(
|
|
|
|
|
path="/pin/add",
|
|
|
|
|
responses={401: {"model": Message}}
|
|
|
|
|
)
|
|
|
|
|
async def add_pin(pin: Pin, current_user: User = Depends(get_current_user)):
|
|
|
|
|
pin_id = pins_collection.insert_one(pin.model_dump()).inserted_id
|
|
|
|
|
return {"id": str(pin_id)}
|
|
|
|
|
|
|
|
|
|
@app.get("/pins")
|
|
|
|
|
@app.get(
|
|
|
|
|
path="/pins",
|
|
|
|
|
responses={401: {"model": Message}}
|
|
|
|
|
)
|
|
|
|
|
async def list_pins(current_user: User = Depends(get_current_user)):
|
|
|
|
|
pins = pins_serialize(pins_collection.find().to_list())
|
|
|
|
|
return pins
|
|
|
|
|
|
|
|
|
|
@app.get("/friend/{id}")
|
|
|
|
|
@app.get(
|
|
|
|
|
path="/friend/{id}",
|
|
|
|
|
responses={401: {"model": Message}, 404: {"model": Message}}
|
|
|
|
|
)
|
|
|
|
|
async def get_friend(id: str, current_user: User = Depends(get_current_user)):
|
|
|
|
|
friend = friends_collection.find_one({"_id": ObjectId(id)})
|
|
|
|
|
if friend is None:
|
|
|
|
@ -144,7 +172,10 @@ async def get_friend(id: str, current_user: User = Depends(get_current_user)):
|
|
|
|
|
|
|
|
|
|
return friend
|
|
|
|
|
|
|
|
|
|
@app.post("/friend/add")
|
|
|
|
|
@app.post(
|
|
|
|
|
path="/friend/add",
|
|
|
|
|
responses={401: {"model": Message}, 409: {"model": Message}}
|
|
|
|
|
)
|
|
|
|
|
async def add_friend(friendAdd: FriendAddDTO, current_user: User = Depends(get_current_user)):
|
|
|
|
|
# TODO: test if exists
|
|
|
|
|
friend: Friend = friendAdd.model_dump()
|
|
|
|
@ -161,7 +192,10 @@ async def add_friend(friendAdd: FriendAddDTO, current_user: User = Depends(get_c
|
|
|
|
|
friend_id = friends_collection.insert_one(friend).inserted_id
|
|
|
|
|
return {"id": str(friend_id)}
|
|
|
|
|
|
|
|
|
|
@app.delete("/friend/{id}/delete")
|
|
|
|
|
@app.delete(
|
|
|
|
|
path="/friend/{id}/delete",
|
|
|
|
|
responses={401: {"model": Message}, 404: {"model": Message}}
|
|
|
|
|
)
|
|
|
|
|
async def delete_friend(id: str, current_user: User = Depends(get_current_user)):
|
|
|
|
|
result = friends_collection.delete_one({"_id": ObjectId(id)})
|
|
|
|
|
if result.deleted_count == 0:
|
|
|
|
@ -169,7 +203,10 @@ async def delete_friend(id: str, current_user: User = Depends(get_current_user))
|
|
|
|
|
|
|
|
|
|
return {"message": "Friend deleted"}
|
|
|
|
|
|
|
|
|
|
@app.patch("/friend/{id}/accept")
|
|
|
|
|
@app.patch(
|
|
|
|
|
path="/friend/{id}/accept",
|
|
|
|
|
responses={401: {"model": Message}, 404: {"model": Message}}
|
|
|
|
|
)
|
|
|
|
|
async def accept_friend(id: str, current_user: User = Depends(get_current_user)):
|
|
|
|
|
result = friends_collection.update_one({"_id": ObjectId(id)}, {"$set": {"status": "accepted"}})
|
|
|
|
|
if result.matched_count == 0:
|
|
|
|
@ -177,7 +214,10 @@ async def accept_friend(id: str, current_user: User = Depends(get_current_user))
|
|
|
|
|
|
|
|
|
|
return {"message": "Friend request accepted"}
|
|
|
|
|
|
|
|
|
|
@app.post("/friend/{id}/deny")
|
|
|
|
|
@app.post(
|
|
|
|
|
path="/friend/{id}/deny",
|
|
|
|
|
responses={401: {"model": Message}, 404: {"model": Message}}
|
|
|
|
|
)
|
|
|
|
|
async def deny_friend(id: str, current_user: User = Depends(get_current_user)):
|
|
|
|
|
result = friends_collection.update_one({"_id": ObjectId(id)}, {"$set": {"status": "denied"}})
|
|
|
|
|
if result.matched_count == 0:
|
|
|
|
@ -185,12 +225,23 @@ async def deny_friend(id: str, current_user: User = Depends(get_current_user)):
|
|
|
|
|
|
|
|
|
|
return {"message": "Friend request denied"}
|
|
|
|
|
|
|
|
|
|
@app.get("/friends", response_model=FriendListDTO)
|
|
|
|
|
@app.get(
|
|
|
|
|
path="/friends",
|
|
|
|
|
response_model=FriendListDTO,
|
|
|
|
|
responses={401: {"model": Message}}
|
|
|
|
|
)
|
|
|
|
|
async def list_friends(current_user: User = Depends(get_current_user)):
|
|
|
|
|
return FriendListDTO(**friends_serialize(friends_collection.find({"user_id": current_user.uid}).to_list(), friends_collection.find({"friend_user_id": current_user.uid})))
|
|
|
|
|
|
|
|
|
|
@app.get("/users")
|
|
|
|
|
@app.get(
|
|
|
|
|
path="/users",
|
|
|
|
|
responses={401: {"model": Message}, 422: {"model": Message}},
|
|
|
|
|
response_model=list[UserDTO]
|
|
|
|
|
)
|
|
|
|
|
async def search_users(name: str, current_user: User = Depends(get_current_user)):
|
|
|
|
|
# TODO: /!\ pymongo.errors.OperationFailure if regex is poop
|
|
|
|
|
users = users_serialize(users_collection.find({"username": {"$regex": name, "$options": "i"}}).to_list())
|
|
|
|
|
try:
|
|
|
|
|
users = users_serialize(users_collection.find({"username": {"$regex": name, "$options": "i"}}).to_list())
|
|
|
|
|
except pymongo.errors.OperationFailure:
|
|
|
|
|
raise HTTPException(status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, detail="Regex may be wrongly formatted")
|
|
|
|
|
|
|
|
|
|
return users
|