Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit cd112bd

Browse files
Refactor/upgrade backend and frontend parts (#2)
* ♻️ Refactor and simplify backend code * ♻️ Refactor frontend state, integrate typesafe-vuex accessors into state files * ♻️ Use new state accessors and standardize layout * 🔒 Upgrade and fix npm security audit * 🔧 Update local re-generation scripts * 🔊 Log startup exceptions to detect errors early * ✏️ Fix password reset token content * 🔥 Remove unneeded Dockerfile directives * 🔥 Remove unnecessary print * 🔥 Remove unnecessary code, upgrade dependencies in backend * ✏️ Fix typos in docstrings and comments * 🏗️ Improve user Depends utilities to simplify and remove code * 🔥 Remove deprecated SQLAlchemy parameter
1 parent 9e0b826 commit cd112bd

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

54 files changed

+492
-371
lines changed

‎dev-fsfp-back.sh

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
#! /usr/bin/env bash
2+
3+
# Run this script from outside the project, to integrate a dev-fsfp project with changes and review modifications
4+
5+
# Exit in case of error
6+
set -e
7+
8+
if [ $(uname -s) = "Linux" ]; then
9+
echo "Remove __pycache__ files"
10+
sudo find ./dev-fsfp/ -type d -name __pycache__ -exec rm -r {} \+
11+
fi
12+
13+
rm -rf ./full-stack-fastapi-postgresql/\{\{cookiecutter.project_slug\}\}/*
14+
15+
rsync -a --exclude=node_modules ./dev-fsfp/* ./full-stack-fastapi-postgresql/\{\{cookiecutter.project_slug\}\}/
16+
17+
rsync -a ./dev-fsfp/{.env,.gitignore,.gitlab-ci.yml} ./full-stack-fastapi-postgresql/\{\{cookiecutter.project_slug\}\}/

‎dev-fsfp.sh

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
#! /usr/bin/env bash
22

3+
# Run this script from outside the project, to generate a dev-fsfp project
4+
35
# Exit in case of error
46
set -e
57

‎{{cookiecutter.project_slug}}/README.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ The changes to those files only affect the local development environment, not th
6969

7070
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.
7171

72-
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.
72+
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.
7373

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

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

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

94-
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:
94+
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:
9595

9696
```bash
97-
bash ./backend-live.sh
97+
bash /start-reload.sh
9898
```
9999

100100
...it will look like:
101101

102102
```bash
103-
root@7f2607af31c3:/app# bash ./backend-live.sh
103+
root@7f2607af31c3:/app# bash /start-reload.sh
104104
```
105105

106106
and then hit enter. That runs the debugging server that auto reloads when it detects code changes.
Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
11
from fastapi import APIRouter
22

3-
from app.api.api_v1.endpoints.token import router as token_router
4-
from app.api.api_v1.endpoints.user import router as user_router
5-
from app.api.api_v1.endpoints.utils import router as utils_router
3+
from app.api.api_v1.endpoints import token, user, utils
64

75
api_router = APIRouter()
8-
api_router.include_router(token_router)
9-
api_router.include_router(user_router)
10-
api_router.include_router(utils_router)
6+
api_router.include_router(token.router)
7+
api_router.include_router(user.router)
8+
api_router.include_router(utils.router)

‎{{cookiecutter.project_slug}}/backend/app/app/api/api_v1/endpoints/token.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,12 @@
44
from fastapi.security import OAuth2PasswordRequestForm
55
from sqlalchemy.orm import Session
66

7+
from app import crud
78
from app.api.utils.db import get_db
89
from app.api.utils.security import get_current_user
910
from app.core import config
1011
from app.core.jwt import create_access_token
1112
from app.core.security import get_password_hash
12-
from app.crud import user as crud_user
1313
from app.db_models.user import User as DBUser
1414
from app.models.msg import Msg
1515
from app.models.token import Token
@@ -30,12 +30,12 @@ def login_access_token(
3030
"""
3131
OAuth2 compatible token login, get an access token for future requests
3232
"""
33-
user = crud_user.authenticate(
33+
user = crud.user.authenticate(
3434
db, email=form_data.username, password=form_data.password
3535
)
3636
if not user:
3737
raise HTTPException(status_code=400, detail="Incorrect email or password")
38-
elif not crud_user.is_active(user):
38+
elif not crud.user.is_active(user):
3939
raise HTTPException(status_code=400, detail="Inactive user")
4040
access_token_expires = timedelta(minutes=config.ACCESS_TOKEN_EXPIRE_MINUTES)
4141
return {
@@ -59,7 +59,7 @@ def recover_password(email: str, db: Session = Depends(get_db)):
5959
"""
6060
Password Recovery
6161
"""
62-
user = crud_user.get_by_email(db, email=email)
62+
user = crud.user.get_by_email(db, email=email)
6363

6464
if not user:
6565
raise HTTPException(
@@ -81,13 +81,13 @@ def reset_password(token: str, new_password: str, db: Session = Depends(get_db))
8181
email = verify_password_reset_token(token)
8282
if not email:
8383
raise HTTPException(status_code=400, detail="Invalid token")
84-
user = crud_user.get_by_email(db, email=email)
84+
user = crud.user.get_by_email(db, email=email)
8585
if not user:
8686
raise HTTPException(
8787
status_code=404,
8888
detail="The user with this username does not exist in the system.",
8989
)
90-
elif not crud_user.is_active(user):
90+
elif not crud.user.is_active(user):
9191
raise HTTPException(status_code=400, detail="Inactive user")
9292
hashed_password = get_password_hash(new_password)
9393
user.hashed_password = hashed_password

‎{{cookiecutter.project_slug}}/backend/app/app/api/api_v1/endpoints/user.py

Lines changed: 19 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,10 @@
55
from pydantic.types import EmailStr
66
from sqlalchemy.orm import Session
77

8+
from app import crud
89
from app.api.utils.db import get_db
9-
from app.api.utils.security import get_current_user
10+
from app.api.utils.security import get_current_active_superuser, get_current_active_user
1011
from app.core import config
11-
from app.crud import user as crud_user
1212
from app.db_models.user import User as DBUser
1313
from app.models.user import User, UserInCreate, UserInDB, UserInUpdate
1414
from app.utils import send_new_account_email
@@ -21,18 +21,12 @@ def read_users(
2121
db: Session = Depends(get_db),
2222
skip: int = 0,
2323
limit: int = 100,
24-
current_user: DBUser = Depends(get_current_user),
24+
current_user: DBUser = Depends(get_current_active_superuser),
2525
):
2626
"""
2727
Retrieve users
2828
"""
29-
if not crud_user.is_active(current_user):
30-
raise HTTPException(status_code=400, detail="Inactive user")
31-
elif not crud_user.is_superuser(current_user):
32-
raise HTTPException(
33-
status_code=400, detail="The user doesn't have enough privileges"
34-
)
35-
users = crud_user.get_multi(db, skip=skip, limit=limit)
29+
users = crud.user.get_multi(db, skip=skip, limit=limit)
3630
return users
3731

3832

@@ -41,24 +35,18 @@ def create_user(
4135
*,
4236
db: Session = Depends(get_db),
4337
user_in: UserInCreate,
44-
current_user: DBUser = Depends(get_current_user),
38+
current_user: DBUser = Depends(get_current_active_superuser),
4539
):
4640
"""
4741
Create new user
4842
"""
49-
if not crud_user.is_active(current_user):
50-
raise HTTPException(status_code=400, detail="Inactive user")
51-
elif not crud_user.is_superuser(current_user):
52-
raise HTTPException(
53-
status_code=400, detail="The user doesn't have enough privileges"
54-
)
55-
user = crud_user.get_by_email(db, email=user_in.email)
43+
user = crud.user.get_by_email(db, email=user_in.email)
5644
if user:
5745
raise HTTPException(
5846
status_code=400,
5947
detail="The user with this username already exists in the system.",
6048
)
61-
user = crud_user.create(db, user_in=user_in)
49+
user = crud.user.create(db, user_in=user_in)
6250
if config.EMAILS_ENABLED and user_in.email:
6351
send_new_account_email(
6452
email_to=user_in.email, username=user_in.email, password=user_in.password
@@ -73,13 +61,11 @@ def update_user_me(
7361
password: str = Body(None),
7462
full_name: str = Body(None),
7563
email: EmailStr = Body(None),
76-
current_user: DBUser = Depends(get_current_user),
64+
current_user: DBUser = Depends(get_current_active_user),
7765
):
7866
"""
7967
Update own user
8068
"""
81-
if not crud_user.is_active(current_user):
82-
raise HTTPException(status_code=400, detail="Inactive user")
8369
current_user_data = jsonable_encoder(current_user)
8470
user_in = UserInUpdate(**current_user_data)
8571
if password is not None:
@@ -88,19 +74,18 @@ def update_user_me(
8874
user_in.full_name = full_name
8975
if email is not None:
9076
user_in.email = email
91-
user = crud_user.update(db, user=current_user, user_in=user_in)
77+
user = crud.user.update(db, user=current_user, user_in=user_in)
9278
return user
9379

9480

9581
@router.get("/users/me", tags=["users"], response_model=User)
9682
def read_user_me(
97-
db: Session = Depends(get_db), current_user: DBUser = Depends(get_current_user)
83+
db: Session = Depends(get_db),
84+
current_user: DBUser = Depends(get_current_active_user),
9885
):
9986
"""
10087
Get current user
10188
"""
102-
if not crud_user.is_active(current_user):
103-
raise HTTPException(status_code=400, detail="Inactive user")
10489
return current_user
10590

10691

@@ -120,32 +105,30 @@ def create_user_open(
120105
status_code=403,
121106
detail="Open user resgistration is forbidden on this server",
122107
)
123-
user = crud_user.get_by_email(db, email=email)
108+
user = crud.user.get_by_email(db, email=email)
124109
if user:
125110
raise HTTPException(
126111
status_code=400,
127112
detail="The user with this username already exists in the system",
128113
)
129114
user_in = UserInCreate(password=password, email=email, full_name=full_name)
130-
user = crud_user.create(db, user_in=user_in)
115+
user = crud.user.create(db, user_in=user_in)
131116
return user
132117

133118

134119
@router.get("/users/{user_id}", tags=["users"], response_model=User)
135120
def read_user_by_id(
136121
user_id: int,
137-
current_user: DBUser = Depends(get_current_user),
122+
current_user: DBUser = Depends(get_current_active_user),
138123
db: Session = Depends(get_db),
139124
):
140125
"""
141126
Get a specific user by username (email)
142127
"""
143-
if not crud_user.is_active(current_user):
144-
raise HTTPException(status_code=400, detail="Inactive user")
145-
user = crud_user.get(db, user_id=user_id)
128+
user = crud.user.get(db, user_id=user_id)
146129
if user == current_user:
147130
return user
148-
if not crud_user.is_superuser(current_user):
131+
if not crud.user.is_superuser(current_user):
149132
raise HTTPException(
150133
status_code=400, detail="The user doesn't have enough privileges"
151134
)
@@ -158,23 +141,17 @@ def update_user(
158141
db: Session = Depends(get_db),
159142
user_id: int,
160143
user_in: UserInUpdate,
161-
current_user: UserInDB = Depends(get_current_user),
144+
current_user: UserInDB = Depends(get_current_active_superuser),
162145
):
163146
"""
164147
Update a user
165148
"""
166-
if not crud_user.is_active(current_user):
167-
raise HTTPException(status_code=400, detail="Inactive user")
168-
elif not crud_user.is_superuser(current_user):
169-
raise HTTPException(
170-
status_code=400, detail="The user doesn't have enough privileges"
171-
)
172-
user = crud_user.get(db, user_id=user_id)
149+
user = crud.user.get(db, user_id=user_id)
173150

174151
if not user:
175152
raise HTTPException(
176153
status_code=404,
177154
detail="The user with this username does not exist in the system",
178155
)
179-
user = crud_user.update(db, user=user, user_in=user_in)
156+
user = crud.user.update(db, user=user, user_in=user_in)
180157
return user
Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
1-
from fastapi import APIRouter, Depends, HTTPException
1+
from fastapi import APIRouter, Depends
22
from pydantic.types import EmailStr
33

4-
from app.api.utils.security import get_current_user
4+
from app.api.utils.security import get_current_active_superuser
55
from app.core.celery_app import celery_app
6-
from app.crud import user as crud_user
76
from app.models.msg import Msg
87
from app.models.user import UserInDB
98
from app.utils import send_test_email
@@ -12,22 +11,22 @@
1211

1312

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

2423

2524
@router.post("/test-email/", tags=["utils"], response_model=Msg, status_code=201)
26-
def test_email(email_to: EmailStr, current_user: UserInDB = Depends(get_current_user)):
25+
def test_email(
26+
email_to: EmailStr, current_user: UserInDB = Depends(get_current_active_superuser)
27+
):
2728
"""
2829
Test emails
2930
"""
30-
if not crud_user.is_superuser(current_user):
31-
raise HTTPException(status_code=400, detail="Not a superuser")
3231
send_test_email(email_to=email_to)
3332
return {"msg": "Test email sent"}

‎{{cookiecutter.project_slug}}/backend/app/app/api/utils/security.py

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,11 @@
55
from sqlalchemy.orm import Session
66
from starlette.status import HTTP_403_FORBIDDEN
77

8+
from app import crud
89
from app.api.utils.db import get_db
910
from app.core import config
1011
from app.core.jwt import ALGORITHM
11-
from app.crud import userascrud_user
12+
from app.db_models.user import User
1213
from app.models.token import TokenPayload
1314

1415
reusable_oauth2 = OAuth2PasswordBearer(tokenUrl="/api/v1/login/access-token")
@@ -24,7 +25,21 @@ def get_current_user(
2425
raise HTTPException(
2526
status_code=HTTP_403_FORBIDDEN, detail="Could not validate credentials"
2627
)
27-
user = crud_user.get(db, user_id=token_data.user_id)
28+
user = crud.user.get(db, user_id=token_data.user_id)
2829
if not user:
2930
raise HTTPException(status_code=404, detail="User not found")
3031
return user
32+
33+
34+
def get_current_active_user(current_user: User = Security(get_current_user)):
35+
if not crud.user.is_active(current_user):
36+
raise HTTPException(status_code=400, detail="Inactive user")
37+
return current_user
38+
39+
40+
def get_current_active_superuser(current_user: User = Security(get_current_user)):
41+
if not crud.user.is_superuser(current_user):
42+
raise HTTPException(
43+
status_code=400, detail="The user doesn't have enough privileges"
44+
)
45+
return current_user

‎{{cookiecutter.project_slug}}/backend/app/app/backend_pre_start.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,12 @@
1818
after=after_log(logger, logging.WARN),
1919
)
2020
def init():
21-
# Try to create session to check if DB is awake
22-
db_session.execute("SELECT 1")
21+
try:
22+
# Try to create session to check if DB is awake
23+
db_session.execute("SELECT 1")
24+
except Exception as e:
25+
logger.error(e)
26+
raise e
2327

2428

2529
def main():

‎{{cookiecutter.project_slug}}/backend/app/app/celeryworker_pre_start.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,12 @@
1818
after=after_log(logger, logging.WARN),
1919
)
2020
def init():
21-
# Try to create session to check if DB is awake
22-
db_session.execute("SELECT 1")
21+
try:
22+
# Try to create session to check if DB is awake
23+
db_session.execute("SELECT 1")
24+
except Exception as e:
25+
logger.error(e)
26+
raise e
2327

2428

2529
def main():

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /