Skip to content

Commit 40397a1

Browse files
authored
Merge pull request #19 from flavienbwk/develop
1.2.0 : Refactored Flask architecture for right Python import
2 parents 6ac13d2 + fc0357a commit 40397a1

29 files changed

+88
-108
lines changed

.env.example

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
FLASK_API_VERSION="1.1"
1+
FLASK_API_VERSION="1.2.0"
22
FLASK_SERVER_NAME="My API Project"
33
FLASK_SERVER_DESCRIPTION="Dockerized Flask API boilerplate using an LDAP and token-based authentication"
44
FLASK_SECRET_KEY="Some secret key"

README.md

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Dockerized ReactJS, Flask, LDAP boilerplate
1+
# Dockerized ReactJS, Flask & LDAP boilerplate
22

33
<p align="center">
44
<a href="https://travis-ci.com/flavienbwk/reactjs-flask-ldap-boilerplate" target="_blank">
@@ -8,18 +8,16 @@
88
</p>
99
<p align="center">ReactJS + Flask + Docker (+ K8S)<br/>boilerplate using a token-based LDAP authentication</p>
1010

11-
> :smiley: Suggestions and feedbacks are [highly appreciated](https://github.com/flavienbwk/reactjs-flask-ldap-boilerplate/issues/new)
12-
1311
## Features
1412

1513
- Docker architecture
1614
- LDAP authentication
1715
- Token-based API authentication
18-
- Automatic [token renewal](./api/app/service/auth_service.py#L45) with [a Flask middleware](./api/app/service/auth_service.py#L32)
16+
- Automatic [token renewal](./api/app/src/service/auth_service.py#L43) with [a Flask middleware](./api/app/src/service/auth_service.py#L30)
1917
- Swagger documentation
2018
- Flask-Migrate
2119
- Flask-SQLAlchemy (PostgreSQL was chosen)
22-
- [Logging and logs rotation](./api/app/utils/Logger.py#L11)
20+
- [Logging and logs rotation](./api/app/src/utils/Logger.py#L12)
2321
- [Choose](./app/app/src/App.js#L65) between sidebar and navbar (or use both !)
2422
- Responsive design
2523
- [Production](./prod.docker-compose.yml) and [development](./docker-compose.yml) builds
@@ -77,7 +75,7 @@ This section will explain how to properly run this project and set-up the LDAP s
7775
7876
4. Run the API
7977
80-
The database will be automatically set-up thanks to Flask Migrate and any future modification brought to [models](./api/app/model) will be automatically applied when the API is **restarted**.
78+
The database will be automatically set-up thanks to Flask Migrate and any future modification brought to [models](./api/app/src/model) will be automatically applied when the API is **restarted**.
8179
8280
You might wait some time before the database get updated after starting the API :
8381
@@ -142,7 +140,7 @@ I've used [Scaleway Kapsule](https://www.scaleway.com/en/kubernetes-kapsule) to
142140

143141
1. Building production images (optional)
144142

145-
Images are tagged `flavienb/reactjs-flask-ldap-boilerplate-{api,web,nginx}:latest` by default. Edit it in `prod.docker-compose.yml` before building.
143+
Images are tagged `flavienb/reactjs-flask-ldap-boilerplate-{api,app,nginx}:latest` by default. Edit it in `prod.docker-compose.yml` before building.
146144

147145
:information_source: You might be interested in pushing your images in a private registry (e.g: [Scaleway's Container Registry service](https://www.scaleway.com/en/container-registry/)).
148146
@@ -177,3 +175,5 @@ I've used [Scaleway Kapsule](https://www.scaleway.com/en/kubernetes-kapsule) to
177175
4. Configure the first user
178176
179177
**Create** your first user by accessing phpLDAPAdmin at [endpoint defined](./k8s/ingress.yaml#L35) and [following the LDAP user creation guide](./CREATE_LDAP_USER.md).
178+
179+
> :smiley: Suggestions and feedbacks are [highly appreciated](https://github.com/flavienbwk/reactjs-flask-ldap-boilerplate/issues/new)

api/app/.gitignore

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1 @@
11
__pycache__/
2-
3-
medias/*
4-
!medias/.gitkeep
5-
6-
certs/*
7-
!certs/.gitkeep

api/app/manager.py renamed to api/app/main.py

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,9 @@
1-
import os
2-
import json
3-
41
from flask_migrate import Migrate, MigrateCommand
52
from flask_script import Manager
63

7-
from routes import blueprint
8-
from app import create_app, database
9-
10-
from utils.ApiResponse import ApiResponse
4+
from src.routes import blueprint
5+
from src.app import create_app, database
6+
from src.utils.ApiResponse import ApiResponse
117

128
# Initialization
139

api/app/src/__init__.py

Whitespace-only changes.

api/app/app.py renamed to api/app/src/app.py

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,17 @@
11
import os
2-
2+
from time import sleep
33
from flask import Flask
44

5-
from utils.Database import Database
6-
from utils.ApiResponse import ApiResponse
5+
from .utils.Logger import Logger
6+
from .utils.Database import Database
7+
from .utils.ApiResponse import ApiResponse
8+
9+
from .config import config_by_name
710

8-
from config import config_by_name
911

1012
FLASK_LEVEL = os.environ.get("FLASK_LEVEL", "dev")
1113

14+
logger = Logger()
1215
database = Database()
1316

1417
def page_not_found(e):
@@ -22,5 +25,11 @@ def create_app():
2225
app.register_error_handler(404, page_not_found)
2326
app.config.from_object(config_by_name[FLASK_LEVEL])
2427
os.environ["FLASK_ENV"] = config_by_name[FLASK_LEVEL].FLASK_ENV
28+
29+
# Waiting for database to be available
30+
while database.isDatabaseAvailable(app) is False:
31+
logger.warning("Database unreachable. Waiting for 3 seconds to be up...")
32+
sleep(3.0)
33+
logger.info("Database is up and running ✓")
2534
database.initDatabase(app)
2635
return app
File renamed without changes.

api/app/src/controller/__init__.py

Whitespace-only changes.

api/app/controller/auth_controller.py renamed to api/app/src/controller/auth_controller.py

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,9 @@
1-
import time
2-
import datetime
3-
41
from flask import request, escape
52
from flask_restplus import Resource, Namespace, fields
63

7-
import sys
8-
sys.path.append("..")
9-
10-
from utils.ApiResponse import ApiResponse
11-
from utils.Logger import Logger
12-
13-
from model.User import User
14-
from model.Token import Token
4+
from ..utils.Logger import Logger
155

16-
from service.auth_service import AuthService, requires_authentication
6+
from ..service.auth_service import AuthService, requires_authentication
177

188
logger = Logger()
199

api/app/controller/home_controller.py renamed to api/app/src/controller/home_controller.py

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,6 @@
1-
from flask import request, escape
2-
from flask_restplus import Resource, Namespace, fields
1+
from flask_restplus import Resource, Namespace
32

4-
import sys
5-
sys.path.append("..")
6-
7-
from utils.ApiResponse import ApiResponse
3+
from ..utils.ApiResponse import ApiResponse
84

95
api = Namespace('Home', description='Basic health operations')
106

api/app/controller/user_controller.py renamed to api/app/src/controller/user_controller.py

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,9 @@
11
from flask import request, escape
22
from flask_restplus import Resource, Namespace, fields
33

4-
import sys
5-
sys.path.append("..")
4+
from ..service.user_service import UserService
5+
from ..service.auth_service import requires_authentication
66

7-
from service.token_service import TokenService
8-
from service.user_service import UserService
9-
from service.auth_service import requires_authentication
10-
11-
from utils.ApiResponse import ApiResponse
127

138
api = Namespace('User', description='User-related operations')
149

api/app/model/Token.py renamed to api/app/src/model/Token.py

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,7 @@
1-
import datetime
1+
from ..model.User import User
22

3-
from sqlalchemy import func
3+
from ..app import database
44

5-
import sys
6-
sys.path.append("..")
7-
8-
from model.User import User
9-
10-
from app import database
115

126
db = database.getDatabase()
137

api/app/model/User.py renamed to api/app/src/model/User.py

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,7 @@
1-
import datetime
2-
31
from sqlalchemy import func
42

5-
import sys
6-
sys.path.append("..")
3+
from ..app import database
74

8-
from app import database
95

106
db = database.getDatabase()
117

api/app/src/model/__init__.py

Whitespace-only changes.

api/app/routes.py renamed to api/app/src/routes.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,17 @@
33
from flask_restplus import Api
44
from flask import Blueprint
55

6-
from controller.home_controller import api as home_ns
7-
from controller.auth_controller import api as auth_ns
8-
from controller.user_controller import api as user_ns
6+
from .controller.home_controller import api as home_ns
7+
from .controller.auth_controller import api as auth_ns
8+
from .controller.user_controller import api as user_ns
9+
910

1011
blueprint = Blueprint('api', __name__)
1112
api = Api(
1213
blueprint,
1314
title=os.environ.get("FLASK_SERVER_NAME"),
1415
description=os.environ.get("FLASK_SERVER_DESCRIPTION"),
15-
version='1.0'
16+
version=os.environ.get("FLASK_API_VERSION")
1617
)
1718

1819
# Routes

api/app/src/service/__init__.py

Whitespace-only changes.

api/app/service/auth_service.py renamed to api/app/src/service/auth_service.py

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,16 @@
66
from flask import request, escape
77
from functools import wraps
88

9-
import sys
10-
sys.path.append("..")
9+
from ..service.token_service import TokenService
10+
from ..service.user_service import UserService
1111

12-
from service.token_service import TokenService
13-
from service.user_service import UserService
12+
from ..utils.Logger import Logger
13+
from ..utils.ApiResponse import ApiResponse
14+
from ..utils.hash import sha256, hash_id
1415

15-
from utils.Logger import Logger
16-
from utils.ApiResponse import ApiResponse
17-
from utils.hash import sha256, hash_id
16+
from ..model.User import User
17+
from ..model.Token import Token
1818

19-
from model.User import User
20-
from model.Token import Token
2119

2220
LDAP_SCHEME = os.environ.get("LDAP_SCHEME")
2321
LDAP_HOST = os.environ.get("LDAP_HOST")

api/app/service/token_service.py renamed to api/app/src/service/token_service.py

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,18 @@
1-
import os
21
import time
32
from uuid import uuid4
43
from random import randint
54

65
from flask import request
76

8-
import sys
9-
sys.path.append("..")
7+
from ..app import database
108

11-
from app import database
9+
from ..utils.ApiResponse import ApiResponse
10+
from ..utils.Logger import Logger
11+
from ..utils.hash import sha256, hash_id
1212

13-
from utils.ApiResponse import ApiResponse
14-
from utils.Logger import Logger
15-
from utils.hash import sha256, hash_id
13+
from ..model.User import User
14+
from ..model.Token import Token
1615

17-
from model.User import User
18-
from model.Token import Token
1916

2017
TOKEN_EXPIRATION_TIME = 60 * 60 * 6
2118

api/app/service/user_service.py renamed to api/app/src/service/user_service.py

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,14 @@
44
import datetime
55
from validate_email import validate_email
66

7-
import sys
8-
sys.path.append("..")
7+
from ..app import database
98

10-
from app import database
9+
from ..utils.Logger import Logger
10+
from ..utils.ApiResponse import ApiResponse
1111

12-
from utils.Logger import Logger
13-
from utils.ApiResponse import ApiResponse
12+
from ..model.User import User
13+
from ..model.Token import Token
1414

15-
from model.User import User
16-
from model.Token import Token
1715

1816
LDAP_SCHEME = os.environ.get("LDAP_SCHEME")
1917
LDAP_HOST = os.environ.get("LDAP_HOST")
File renamed without changes.

api/app/utils/Database.py renamed to api/app/src/utils/Database.py

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11

2+
import psycopg2
23
from flask_sqlalchemy import SQLAlchemy
34

4-
import sys
5-
sys.path.append("..")
5+
from ..utils.Logger import Logger
66

7-
from utils.Logger import Logger
87

98
logger = Logger()
109

@@ -16,6 +15,20 @@ def __init__(self) -> None:
1615
def getDatabase(self):
1716
return self.database
1817

18+
def isDatabaseAvailable(self, app):
19+
try:
20+
conn = psycopg2.connect(
21+
"host='{}' dbname='{}' user='{}' password='{}'".format(
22+
app.config["POSTGRES_HOST"],
23+
app.config["POSTGRES_DB"],
24+
app.config["POSTGRES_USER"],
25+
app.config["POSTGRES_PASSWORD"]
26+
))
27+
conn.close()
28+
return True
29+
except psycopg2.OperationalError:
30+
return False
31+
1932
def initDatabase(self, app):
2033
self.database.init_app(app)
2134

api/app/utils/Logger.py renamed to api/app/src/utils/Logger.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
class Logger():
99

1010
def __init__(self) -> bool:
11+
logging.getLogger().addHandler(logging.StreamHandler())
1112
handler = logging.handlers.WatchedFileHandler("/logs/api.{}.log".format(PROJECT_NAME))
1213
formatter = logging.Formatter(logging.BASIC_FORMAT)
1314
handler.setFormatter(formatter)
@@ -29,4 +30,4 @@ def error(self, message):
2930
self.root.error(message)
3031

3132
def critical(self, message):
32-
self.root.critical(message)
33+
self.root.critical(message)

api/app/src/utils/__init__.py

Whitespace-only changes.

api/app/utils/hash.py renamed to api/app/src/utils/hash.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import hashlib
33
from hashids import Hashids
44

5+
56
FLASK_SECRET_KEY = os.environ.get("FLASK_SECRET_KEY")
67

78
def hash_id(seed: int):

api/entrypoint.sh

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
#!/bin/sh
22
# These commands are here because they must be run at runtime
33

4-
python /app/manager.py db init
4+
cd /app
55

6-
python /app/manager.py db migrate --message 'initial database migration'
7-
python /app/manager.py db upgrade
8-
9-
python /app/manager.py run
6+
python -m main db init
7+
python -m main db migrate --message 'Initial database migration'
8+
python -m main db upgrade
9+
python -m main run

api/prod.entrypoint.sh

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
#!/bin/sh
22
# These commands are here because they must be run at runtime
33

4-
python /app/manager.py db init
4+
cd /app
55

6-
python /app/manager.py db migrate --message 'initial database migration'
7-
python /app/manager.py db upgrade
6+
python -m main db init
7+
python -m main db migrate --message 'Initial database migration'
8+
python -m main db upgrade
89

9-
gunicorn manager:app -b 0.0.0.0:5000
10+
gunicorn main:app -b 0.0.0.0:5000

api/requirements.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
psycopg2-binary==2.8.4
1+
psycopg2==2.8.4
22
Werkzeug==0.16.1
33
flask-restplus==0.13.0
44
Flask-SQLAlchemy==2.4.1

app/app/package-lock.json

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)