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

Refactor/upgrade backend and frontend parts #2

Merged
merged 13 commits into from
Mar 11, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
17 changes: 17 additions & 0 deletions dev-fsfp-back.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#! /usr/bin/env bash

# Run this script from outside the project, to integrate a dev-fsfp project with changes and review modifications

# Exit in case of error
set -e

if [ $(uname -s) = "Linux" ]; then
echo "Remove __pycache__ files"
sudo find ./dev-fsfp/ -type d -name __pycache__ -exec rm -r {} \+
fi

rm -rf ./full-stack-fastapi-postgresql/\{\{cookiecutter.project_slug\}\}/*

rsync -a --exclude=node_modules ./dev-fsfp/* ./full-stack-fastapi-postgresql/\{\{cookiecutter.project_slug\}\}/

rsync -a ./dev-fsfp/{.env,.gitignore,.gitlab-ci.yml} ./full-stack-fastapi-postgresql/\{\{cookiecutter.project_slug\}\}/
2 changes: 2 additions & 0 deletions dev-fsfp.sh
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
#! /usr/bin/env bash

# Run this script from outside the project, to generate a dev-fsfp project

# Exit in case of error
set -e

Expand Down
8 changes: 4 additions & 4 deletions {{cookiecutter.project_slug}}/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ The changes to those files only affect the local development environment, not th

For example, the directory with the backend code is mounted as a Docker "host volume" (in the file `docker-compose.dev.volumes.yml`), mapping the code you change live to the directory inside the container. That allows you to test your changes right away, without having to build the Docker image again. It should only be done during development, for production, you should build the Docker image with a recent version of the backend code. But during development, it allows you to iterate very fast.

There is also a commented out `command` override (in the file `docker-compose.dev.command.yml`), if you want to enable it, uncomment it. It makes the backend container run a process that does "nothing", but keeps the process running. That allows you to get inside your living container and run commands inside, for example a Python interpreter to test installed dependencies, or start the development server that reloads when it detectes changes.
There is also a commented out `command` override (in the file `docker-compose.dev.command.yml`), if you want to enable it, uncomment it. It makes the backend container run a process that does "nothing", but keeps the process running. That allows you to get inside your living container and run commands inside, for example a Python interpreter to test installed dependencies, or start the development server that reloads when it detects changes.

To get inside the container with a `bash` session you can start the stack with:

Expand All @@ -91,16 +91,16 @@ root@7f2607af31c3:/app#

that means that you are in a `bash` session inside your container, as a `root` user, under the `/app` directory.

There is also a script `backend-live.sh` to run the debug live reloading server. You can run that script from inside the container with:
There is also a script `/start-reload.sh` to run the debug live reloading server. You can run that script from inside the container with:

```bash
bash ./backend-live.sh
bash /start-reload.sh
```

...it will look like:

```bash
root@7f2607af31c3:/app# bash ./backend-live.sh
root@7f2607af31c3:/app# bash /start-reload.sh
```

and then hit enter. That runs the debugging server that auto reloads when it detects code changes.
Expand Down
10 changes: 4 additions & 6 deletions {{cookiecutter.project_slug}}/backend/app/app/api/api_v1/api.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
from fastapi import APIRouter

from app.api.api_v1.endpoints.token import router as token_router
from app.api.api_v1.endpoints.user import router as user_router
from app.api.api_v1.endpoints.utils import router as utils_router
from app.api.api_v1.endpoints import token, user, utils

api_router = APIRouter()
api_router.include_router(token_router)
api_router.include_router(user_router)
api_router.include_router(utils_router)
api_router.include_router(token.router)
api_router.include_router(user.router)
api_router.include_router(utils.router)
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@
from fastapi.security import OAuth2PasswordRequestForm
from sqlalchemy.orm import Session

from app import crud
from app.api.utils.db import get_db
from app.api.utils.security import get_current_user
from app.core import config
from app.core.jwt import create_access_token
from app.core.security import get_password_hash
from app.crud import user as crud_user
from app.db_models.user import User as DBUser
from app.models.msg import Msg
from app.models.token import Token
Expand All @@ -30,12 +30,12 @@ def login_access_token(
"""
OAuth2 compatible token login, get an access token for future requests
"""
user = crud_user.authenticate(
user = crud.user.authenticate(
db, email=form_data.username, password=form_data.password
)
if not user:
raise HTTPException(status_code=400, detail="Incorrect email or password")
elif not crud_user.is_active(user):
elif not crud.user.is_active(user):
raise HTTPException(status_code=400, detail="Inactive user")
access_token_expires = timedelta(minutes=config.ACCESS_TOKEN_EXPIRE_MINUTES)
return {
Expand All @@ -59,7 +59,7 @@ def recover_password(email: str, db: Session = Depends(get_db)):
"""
Password Recovery
"""
user = crud_user.get_by_email(db, email=email)
user = crud.user.get_by_email(db, email=email)

if not user:
raise HTTPException(
Expand All @@ -81,13 +81,13 @@ def reset_password(token: str, new_password: str, db: Session = Depends(get_db))
email = verify_password_reset_token(token)
if not email:
raise HTTPException(status_code=400, detail="Invalid token")
user = crud_user.get_by_email(db, email=email)
user = crud.user.get_by_email(db, email=email)
if not user:
raise HTTPException(
status_code=404,
detail="The user with this username does not exist in the system.",
)
elif not crud_user.is_active(user):
elif not crud.user.is_active(user):
raise HTTPException(status_code=400, detail="Inactive user")
hashed_password = get_password_hash(new_password)
user.hashed_password = hashed_password
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@
from pydantic.types import EmailStr
from sqlalchemy.orm import Session

from app import crud
from app.api.utils.db import get_db
from app.api.utils.security import get_current_user
from app.api.utils.security import get_current_active_superuser, get_current_active_user
from app.core import config
from app.crud import user as crud_user
from app.db_models.user import User as DBUser
from app.models.user import User, UserInCreate, UserInDB, UserInUpdate
from app.utils import send_new_account_email
Expand All @@ -21,18 +21,12 @@ def read_users(
db: Session = Depends(get_db),
skip: int = 0,
limit: int = 100,
current_user: DBUser = Depends(get_current_user),
current_user: DBUser = Depends(get_current_active_superuser),
):
"""
Retrieve users
"""
if not crud_user.is_active(current_user):
raise HTTPException(status_code=400, detail="Inactive user")
elif not crud_user.is_superuser(current_user):
raise HTTPException(
status_code=400, detail="The user doesn't have enough privileges"
)
users = crud_user.get_multi(db, skip=skip, limit=limit)
users = crud.user.get_multi(db, skip=skip, limit=limit)
return users


Expand All @@ -41,24 +35,18 @@ def create_user(
*,
db: Session = Depends(get_db),
user_in: UserInCreate,
current_user: DBUser = Depends(get_current_user),
current_user: DBUser = Depends(get_current_active_superuser),
):
"""
Create new user
"""
if not crud_user.is_active(current_user):
raise HTTPException(status_code=400, detail="Inactive user")
elif not crud_user.is_superuser(current_user):
raise HTTPException(
status_code=400, detail="The user doesn't have enough privileges"
)
user = crud_user.get_by_email(db, email=user_in.email)
user = crud.user.get_by_email(db, email=user_in.email)
if user:
raise HTTPException(
status_code=400,
detail="The user with this username already exists in the system.",
)
user = crud_user.create(db, user_in=user_in)
user = crud.user.create(db, user_in=user_in)
if config.EMAILS_ENABLED and user_in.email:
send_new_account_email(
email_to=user_in.email, username=user_in.email, password=user_in.password
Expand All @@ -73,13 +61,11 @@ def update_user_me(
password: str = Body(None),
full_name: str = Body(None),
email: EmailStr = Body(None),
current_user: DBUser = Depends(get_current_user),
current_user: DBUser = Depends(get_current_active_user),
):
"""
Update own user
"""
if not crud_user.is_active(current_user):
raise HTTPException(status_code=400, detail="Inactive user")
current_user_data = jsonable_encoder(current_user)
user_in = UserInUpdate(**current_user_data)
if password is not None:
Expand All @@ -88,19 +74,18 @@ def update_user_me(
user_in.full_name = full_name
if email is not None:
user_in.email = email
user = crud_user.update(db, user=current_user, user_in=user_in)
user = crud.user.update(db, user=current_user, user_in=user_in)
return user


@router.get("/users/me", tags=["users"], response_model=User)
def read_user_me(
db: Session = Depends(get_db), current_user: DBUser = Depends(get_current_user)
db: Session = Depends(get_db),
current_user: DBUser = Depends(get_current_active_user),
):
"""
Get current user
"""
if not crud_user.is_active(current_user):
raise HTTPException(status_code=400, detail="Inactive user")
return current_user


Expand All @@ -120,32 +105,30 @@ def create_user_open(
status_code=403,
detail="Open user resgistration is forbidden on this server",
)
user = crud_user.get_by_email(db, email=email)
user = crud.user.get_by_email(db, email=email)
if user:
raise HTTPException(
status_code=400,
detail="The user with this username already exists in the system",
)
user_in = UserInCreate(password=password, email=email, full_name=full_name)
user = crud_user.create(db, user_in=user_in)
user = crud.user.create(db, user_in=user_in)
return user


@router.get("/users/{user_id}", tags=["users"], response_model=User)
def read_user_by_id(
user_id: int,
current_user: DBUser = Depends(get_current_user),
current_user: DBUser = Depends(get_current_active_user),
db: Session = Depends(get_db),
):
"""
Get a specific user by username (email)
"""
if not crud_user.is_active(current_user):
raise HTTPException(status_code=400, detail="Inactive user")
user = crud_user.get(db, user_id=user_id)
user = crud.user.get(db, user_id=user_id)
if user == current_user:
return user
if not crud_user.is_superuser(current_user):
if not crud.user.is_superuser(current_user):
raise HTTPException(
status_code=400, detail="The user doesn't have enough privileges"
)
Expand All @@ -158,23 +141,17 @@ def update_user(
db: Session = Depends(get_db),
user_id: int,
user_in: UserInUpdate,
current_user: UserInDB = Depends(get_current_user),
current_user: UserInDB = Depends(get_current_active_superuser),
):
"""
Update a user
"""
if not crud_user.is_active(current_user):
raise HTTPException(status_code=400, detail="Inactive user")
elif not crud_user.is_superuser(current_user):
raise HTTPException(
status_code=400, detail="The user doesn't have enough privileges"
)
user = crud_user.get(db, user_id=user_id)
user = crud.user.get(db, user_id=user_id)

if not user:
raise HTTPException(
status_code=404,
detail="The user with this username does not exist in the system",
)
user = crud_user.update(db, user=user, user_in=user_in)
user = crud.user.update(db, user=user, user_in=user_in)
return user
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
from fastapi import APIRouter, Depends, HTTPException
from fastapi import APIRouter, Depends
from pydantic.types import EmailStr

from app.api.utils.security import get_current_user
from app.api.utils.security import get_current_active_superuser
from app.core.celery_app import celery_app
from app.crud import user as crud_user
from app.models.msg import Msg
from app.models.user import UserInDB
from app.utils import send_test_email
Expand All @@ -12,22 +11,22 @@


@router.post("/test-celery/", tags=["utils"], response_model=Msg, status_code=201)
def test_celery(msg: Msg, current_user: UserInDB = Depends(get_current_user)):
def test_celery(
msg: Msg, current_user: UserInDB = Depends(get_current_active_superuser)
):
"""
Test Celery worker
"""
if not crud_user.is_superuser(current_user):
raise HTTPException(status_code=400, detail="Not a superuser")
celery_app.send_task("app.worker.test_celery", args=[msg.msg])
return {"msg": "Word received"}


@router.post("/test-email/", tags=["utils"], response_model=Msg, status_code=201)
def test_email(email_to: EmailStr, current_user: UserInDB = Depends(get_current_user)):
def test_email(
email_to: EmailStr, current_user: UserInDB = Depends(get_current_active_superuser)
):
"""
Test emails
"""
if not crud_user.is_superuser(current_user):
raise HTTPException(status_code=400, detail="Not a superuser")
send_test_email(email_to=email_to)
return {"msg": "Test email sent"}
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,11 @@
from sqlalchemy.orm import Session
from starlette.status import HTTP_403_FORBIDDEN

from app import crud
from app.api.utils.db import get_db
from app.core import config
from app.core.jwt import ALGORITHM
from app.crud import user as crud_user
from app.db_models.user import User
from app.models.token import TokenPayload

reusable_oauth2 = OAuth2PasswordBearer(tokenUrl="/api/v1/login/access-token")
Expand All @@ -24,7 +25,21 @@ def get_current_user(
raise HTTPException(
status_code=HTTP_403_FORBIDDEN, detail="Could not validate credentials"
)
user = crud_user.get(db, user_id=token_data.user_id)
user = crud.user.get(db, user_id=token_data.user_id)
if not user:
raise HTTPException(status_code=404, detail="User not found")
return user


def get_current_active_user(current_user: User = Security(get_current_user)):
if not crud.user.is_active(current_user):
raise HTTPException(status_code=400, detail="Inactive user")
return current_user


def get_current_active_superuser(current_user: User = Security(get_current_user)):
if not crud.user.is_superuser(current_user):
raise HTTPException(
status_code=400, detail="The user doesn't have enough privileges"
)
return current_user
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,12 @@
after=after_log(logger, logging.WARN),
)
def init():
# Try to create session to check if DB is awake
db_session.execute("SELECT 1")
try:
# Try to create session to check if DB is awake
db_session.execute("SELECT 1")
except Exception as e:
logger.error(e)
raise e


def main():
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,12 @@
after=after_log(logger, logging.WARN),
)
def init():
# Try to create session to check if DB is awake
db_session.execute("SELECT 1")
try:
# Try to create session to check if DB is awake
db_session.execute("SELECT 1")
except Exception as e:
logger.error(e)
raise e


def main():
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from . import user
Loading