Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Entrega 5 #4

Merged
merged 47 commits into from
Apr 19, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
2722000
Adicionado estrutura inicial do backend com fastapi e docker.
DionVitor Mar 23, 2022
ce2e588
creating react app
EmersonTeles Mar 23, 2022
7993589
feat: login page
EmersonTeles Mar 24, 2022
f837ed5
feat: router to login
EmersonTeles Mar 24, 2022
d763954
Inserindo um pouco de responsividade
Mar 29, 2022
3454510
Adicionado endpoint inicial de registro de usuários.
DionVitor Mar 29, 2022
de576c3
Merge remote-tracking branch 'origin/entrega_4' into entrega_4
Mar 29, 2022
9be0335
Atualizando os requisitos funcionais e MVP.
berssutti Mar 29, 2022
5749289
feat: Home Page
EmersonTeles Apr 4, 2022
b97b4e8
style: changing login stylesheet
EmersonTeles Apr 4, 2022
16ca1d6
Inicio do registration
berssutti Apr 5, 2022
baaa099
feat: adding card content
EmersonTeles Apr 5, 2022
7de1105
Merge branch 'entrega_4' of github.com:FGAUnB-MDS-GM/2021.2-MyUni int…
EmersonTeles Apr 5, 2022
641d7b4
Atualizando documentação
berssutti Apr 5, 2022
3bc1a38
Registration update
berssutti Apr 5, 2022
3e6bf2f
Registration Update
berssutti Apr 5, 2022
373c390
Merge branch 'entrega_4' of github.com:FGAUnB-MDS-GM/2021.2-MyUni int…
EmersonTeles Apr 6, 2022
b6603e1
Registration update
berssutti Apr 6, 2022
cdb9bd1
Merge branch 'entrega_4' of github.com:FGAUnB-MDS-GM/2021.2-MyUni int…
EmersonTeles Apr 6, 2022
3cb3fb3
Documentation Update
berssutti Apr 6, 2022
49ac07b
style: refactor stylesheet
EmersonTeles Apr 6, 2022
3ed2789
Merge branch 'entrega_4' of github.com:FGAUnB-MDS-GM/2021.2-MyUni int…
EmersonTeles Apr 6, 2022
254fd5e
feat: adding input states
EmersonTeles Apr 7, 2022
1b70a1b
Inserindo telas de visualização de perfil entre outros componentes
Apr 7, 2022
ae99f6c
Added user login endpoint and added rule to unique user email in data…
DionVitor Apr 7, 2022
cfc0a45
Merge branch 'entrega_4' of https://github.com/FGAUnB-MDS-GM/2021.2-M…
DionVitor Apr 7, 2022
6b7495f
Documentation Update
berssutti Apr 7, 2022
eb9be58
Added cors middleware.
DionVitor Apr 7, 2022
70f755f
Merge branch 'entrega_4' of https://github.com/FGAUnB-MDS-GM/2021.2-M…
DionVitor Apr 7, 2022
742c7b7
Fixed user exists method.
DionVitor Apr 7, 2022
f4e9f51
feat: add authentication
EmersonTeles Apr 7, 2022
15a9f1d
Documentation Update
berssutti Apr 7, 2022
fd78589
Added delete and update user.
DionVitor Apr 7, 2022
da573d9
Merge branch 'entrega_4' of https://github.com/FGAUnB-MDS-GM/2021.2-M…
DionVitor Apr 7, 2022
f356248
Documentation Update
berssutti Apr 7, 2022
12ea5a1
Merge branch 'entrega_4' of github.com:FGAUnB-MDS-GM/2021.2-MyUni int…
EmersonTeles Apr 8, 2022
f43cc17
fix: add auth context provider
EmersonTeles Apr 8, 2022
9a2978d
documentation update
berssutti Apr 19, 2022
2527fdf
documentation update
berssutti Apr 19, 2022
40b7c1d
Added get disciplines and register discipline endpoints.
DionVitor Apr 19, 2022
6001bd1
Merge branch 'entrega_4' of https://github.com/FGAUnB-MDS-GM/2021.2-M…
DionVitor Apr 19, 2022
71bf511
inicio da tela do forum
berssutti Apr 19, 2022
3688bc8
Merge branch 'entrega_4' of https://github.com/FGAUnB-MDS-GM/2021.2-M…
berssutti Apr 19, 2022
a71bf2c
feat: forum changes layout
EmersonTeles Apr 19, 2022
c0faa5c
feat: responsividade login
EmersonTeles Apr 19, 2022
d26a3f9
feat: responsiviness of registration
EmersonTeles Apr 19, 2022
92122ea
feat: private routes
EmersonTeles Apr 19, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,5 @@
/.idea/
.idea/
*/.env
*/__pycache__/*
__pycache__/
__pycache__/*
8 changes: 8 additions & 0 deletions backend/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
FROM python:3.10

WORKDIR /code/
COPY . .

RUN pip install -r requirements.txt

CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000", "--reload"]
Binary file added backend/__pycache__/main.cpython-310.pyc
Binary file not shown.
Empty file added backend/controllers/__init__.py
Empty file.
Binary file not shown.
Binary file not shown.
46 changes: 46 additions & 0 deletions backend/controllers/disciplines_controllers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
from fastapi import APIRouter, Depends, status
from fastapi.security import APIKeyHeader
from fastapi.responses import JSONResponse

from utils.jwt_gateway import JWTGateway
from controllers.docs.models import Discipline as DisciplinePydantic
from models.discipline import Discipline, DisciplineModel


disciplines_router = APIRouter(prefix="/disciplines", tags=["Disciplines"])
jwt_scheme = APIKeyHeader(name="Authorization")


@disciplines_router.post("")
def register_discipline(discipline: DisciplinePydantic, jwt: str = Depends(jwt_scheme)):
user_id = JWTGateway().retrieve_payload(jwt).get("user_id")
if not user_id:
return JSONResponse({"message": "Unauthorized"}, status_code=status.HTTP_401_UNAUTHORIZED)

discipline = Discipline(
discipline.code,
discipline.name
)

if not discipline.is_valid():
return JSONResponse(content={"message": "discipline isn't valid"}, status_code=status.HTTP_400_BAD_REQUEST)

discipline_model = DisciplineModel()
if discipline_model.discipline_code_exists(discipline.code):
return JSONResponse(content={"message": "discipline already exists"})

inserted = DisciplineModel().register_discipline(discipline)
message = {"message": "success" if inserted else "error when inserting discipline in database"}
status_code = status.HTTP_201_CREATED if inserted else status.HTTP_400_BAD_REQUEST
return JSONResponse(content=message, status_code=status_code)


@disciplines_router.get("")
def get_disciplines():
disciplines = DisciplineModel().get_disciplines()
data_disciplines = {"data": disciplines} if disciplines else {}

message = {"message": "success" if disciplines else "Not found", **data_disciplines}
status_code = status.HTTP_200_OK if disciplines else status.HTTP_404_NOT_FOUND

return JSONResponse(content=message, status_code=status_code)
Empty file.
20 changes: 20 additions & 0 deletions backend/controllers/docs/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
from pydantic import BaseModel


class RegistrableUser(BaseModel):
name: str
password: str
user_type: int
email: str
college: str
disciplines: list[str] = []


class Login(BaseModel):
email: str
password: str


class Discipline(BaseModel):
code: str
name: str
14 changes: 14 additions & 0 deletions backend/controllers/forum_controllers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
from fastapi import APIRouter


forum_router = APIRouter(prefix="/forum", tags=["Forum"])


@forum_router.get("")
def get_forums():
return {}


@forum_router.post("")
def register_forums():
return {}
57 changes: 57 additions & 0 deletions backend/controllers/user_controller.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
from fastapi import APIRouter, status
from fastapi.responses import JSONResponse

from controllers.docs.models import RegistrableUser, Login
from models.user import User, UserModel
from utils.jwt_gateway import JWTGateway
from utils.hasher import hash_password


user_router = APIRouter(prefix="/user", tags=["User"])


@user_router.post("")
def register_user(user: RegistrableUser):
user_service = UserModel()

if user_service.user_exists_by_email(user.email):
return JSONResponse(content={"message": "email already exists."}, status_code=status.HTTP_400_BAD_REQUEST)

user_entity = User(
user.name,
user.password,
user.user_type,
user.email,
user.college,
user.disciplines
)

if not user_entity.is_valid():
return JSONResponse(content={"message": "user isn't valid"}, status_code=status.HTTP_400_BAD_REQUEST)

inserted_id = user_service.register_user(user_entity)
status_code = status.HTTP_201_CREATED if inserted_id else status.HTTP_400_BAD_REQUEST
response = {
"jwt": JWTGateway().generate_jwt({"user_id": inserted_id})
} if inserted_id else {"message": "error when saving register in database"}

return JSONResponse(content=response, status_code=status_code)


@user_router.post("/login")
def login_user(login: Login):
user_id = UserModel().retrieve_user_by_login(login.email, hash_password(login.password))
response = {"jwt": JWTGateway().generate_jwt({"user_id": user_id})} if user_id else {"message": "Not found"}
status_code = status.HTTP_200_OK if user_id else status.HTTP_404_NOT_FOUND

return JSONResponse(content=response, status_code=status_code)


@user_router.put("")
def update_user():
pass


@user_router.delete("")
def delete_user():
pass
32 changes: 32 additions & 0 deletions backend/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
version: "3.7"

services:
myuni_api:
container_name: myuni_api
build: .
ports:
- "8000:8000"
networks:
- api_network
volumes:
- .:/code
depends_on:
- myuni_db
myuni_db:
container_name: myuni_db
image: mongo:5.0.2
ports:
- "27017:27017"
environment:
MONGO_INITDB_ROOT_USERNAME: myuni
MONGO_INITDB_ROOT_PASSWORD: myuni
volumes:
- mongo_volume:/data/db
networks:
- api_network

networks:
api_network:

volumes:
mongo_volume:
60 changes: 60 additions & 0 deletions backend/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
from typing import Dict

from fastapi import FastAPI, Request
from fastapi.openapi.utils import get_openapi
from dotenv import load_dotenv
from starlette.middleware.cors import CORSMiddleware

from controllers.forum_controllers import forum_router
from controllers.user_controller import user_router
from controllers.disciplines_controllers import disciplines_router


app = FastAPI()
app.include_router(forum_router)
app.include_router(user_router)
app.include_router(disciplines_router)

app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"]
)


@app.get("/", include_in_schema=False)
def api_root():
return {"message": "MyUni API"}


@app.middleware("http")
async def before_request(request: Request, call_next):
load_dotenv()

response = await call_next(request)
return response


def customize_openapi(openapi_schema: Dict):
pass


def custom_openapi():
if app.openapi_schema:
return app.openapi_schema

openapi_schema = get_openapi(
title="MyUni API",
version="1.0.0",
description="Endpoint documentation to MyUni api.",
routes=app.routes,
)

customize_openapi(openapi_schema)
app.openapi_schema = openapi_schema
return app.openapi_schema


app.openapi = custom_openapi
Empty file added backend/models/__init__.py
Empty file.
44 changes: 44 additions & 0 deletions backend/models/discipline.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
from typing import Dict
from datetime import datetime

from models.mixins.mongodb import MongoDBMixin
from views.discipline_serializer import discipline_serializer


class Discipline:
def __init__(self, code: str, name: str):
self.code = code
self.name = name

now = datetime.utcnow().isoformat()
self.created_at = now
self.updated_at = now

def to_dict(self) -> Dict:
return {
"code": self.code,
"name": self.name,
"created_at": self.created_at,
"updated_at": self.updated_at
}

def is_valid(self) -> bool:
return all([
self.code,
self.name
])


class DisciplineModel(MongoDBMixin):
def register_discipline(self, discipline: Discipline) -> bool:
insert_response = self.discipline_collection.insert_one(discipline.to_dict())
return True if insert_response.acknowledged else False

def discipline_code_exists(self, discipline_code: str) -> bool:
discipline = self.discipline_collection.find_one({"code": discipline_code})
return True if discipline else False

def get_disciplines(self) -> list[Dict]:
disciplines_cursor = self.discipline_collection.find()
disciplines = [discipline_serializer(discipline) for discipline in disciplines_cursor]
return disciplines
Empty file.
32 changes: 32 additions & 0 deletions backend/models/mixins/mongodb.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
from os import environ

from pymongo import MongoClient
from pymongo.collection import Collection


class MongoDBMixin:
def __init__(self):
host = environ.get("MONGO_HOST")
port = environ.get("MONGO_PORT")
user = environ.get("MONGO_USER")
password = environ.get("MONGO_PASSWORD")

if not all([host, port, user, password]):
raise Exception(".env file missing variables")

self._client = MongoClient(
host=host,
port=int(port),
username=user,
password=password
)

self._user_db = self._client.user
self.user_collection: Collection = self._user_db.user

self._discipline_db = self._client.discipline
self.discipline_collection: Collection = self._discipline_db.discipline

@property
def client(self):
return self._client
54 changes: 54 additions & 0 deletions backend/models/user.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
from typing import Dict
from datetime import datetime
from re import match

from models.mixins.mongodb import MongoDBMixin
from utils.hasher import hash_password


class User:
def __init__(self, name: str, password: str, user_type: int, email: str, college: str, disciplines: list[str]):
self.name = name
self.password = password
self.user_type = user_type
self.email = email
self.college = college
self.disciplines = disciplines

now = datetime.utcnow().isoformat()
self.created_at = now
self.updated_at = now

def to_dict(self) -> Dict:
return {
"name": self.name,
"password": hash_password(self.password),
"user_type": self.user_type,
"email": self.email,
"college": self.college,
"disciplines": self.disciplines,
"created_at": self.created_at,
"updated_at": self.updated_at
}

def is_valid(self) -> bool:
return all([
self.name,
len(self.password) >= 8,
match(r"(^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$)", self.email) is not None,
self.college
])


class UserModel(MongoDBMixin):
def user_exists_by_email(self, email: str):
user = self.user_collection.find_one({"email": email}) or {}
return True if user.get("_id") else False

def register_user(self, user: User) -> str:
insert_response = self.user_collection.insert_one(user.to_dict())
return str(insert_response.inserted_id) if insert_response.acknowledged else ""

def retrieve_user_by_login(self, email: str, hashed_password: str) -> str:
user = self.user_collection.find_one({"email": email, "password": hashed_password}) or {}
return str(user.get("_id", ""))
8 changes: 8 additions & 0 deletions backend/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
fastapi==0.73.0
uvicorn==0.17.4
pymongo==4.0.1
python-dotenv==0.19.2
requests==2.27.1
pytest==7.0.1
pytest_cov==3.0.0
python-jose==3.3.0
Empty file added backend/utils/__init__.py
Empty file.
Loading