diff --git a/.env b/.env new file mode 100644 index 0000000..ee6c579 --- /dev/null +++ b/.env @@ -0,0 +1,8 @@ +FLASK_APP=server.py +FLASK_ENV=development +BABEL_DEFAULT_LOCALE=fr + +BCRYPT_ROUNDS=6 +SECRET_KEY=blabla +SQLALCHEMY_DATABASE_URI=mysql://politikorama:politikorama@localhost/politikorama +API_PER_PAGE=30 diff --git a/app/model/__init__.py b/app/model/__init__.py index e9dab7e..aadf108 100644 --- a/app/model/__init__.py +++ b/app/model/__init__.py @@ -3,6 +3,8 @@ This module imports models to allow alembic and flask to find them. """ +from app.model.user import UserModel + from app.model.country import CountryModel from app.model.representative import RepresentativeModel from app.model.address import AddressModel diff --git a/app/model/user.py b/app/model/user.py new file mode 100644 index 0000000..3f217ae --- /dev/null +++ b/app/model/user.py @@ -0,0 +1,63 @@ +# encoding: utf-8 + +import bcrypt + +from flask import current_app + +from app import admin, db +from app.model.model import Model, View + + +def get_user(user_id): + return UserModel.query.get(user_id) + + +class UserModel(db.Model, Model): + __tablename__ = "user" + id = db.Column(db.Integer, primary_key=True) + login = db.Column(db.String(500), unique=True) + password_hash = db.Column(db.String(128)) + email = db.Column(db.String(500), unique=True) + active = db.Column(db.Boolean) + admin = db.Column(db.Boolean) + + @property + def password(self): + return self.password_hash + + @password.setter + def password(self, password): + self.password_hash = bcrypt.hashpw( + password.encode("utf-8"), + bcrypt.gensalt(rounds=current_app.config["BCRYPT_ROUNDS"]), + ) + + def check_password(self, password): + return bcrypt.checkpw(password.encode("utf-8"), self.password_hash) + + @property + def is_active(self): + return self.active or False + + @property + def is_anonymous(self): + return self.id is None + + @property + def is_authenticated(self): + return self.id is not None + + def get_id(self): + return str(self.id) + + +class AdminView(View): + column_default_sort = "login" + column_exclude_list = ["password_hash", ] + + def on_model_change(self, form, model, is_created): + if len(form.password_hash.data) < 128: + model.password = form.password_hash.data + + +admin.add_view(AdminView(UserModel, db.session, name="User")) diff --git a/config.py b/config.py deleted file mode 100644 index 2408709..0000000 --- a/config.py +++ /dev/null @@ -1,37 +0,0 @@ -# encoding: utf-8 -""" -Minimal configuration able to run but maybe not as you want it. -""" - -import os - -DEBUG = False -HOST = "0.0.0.0" -PORT = 5000 -SECRET_KEY = "No secret key" - -JINJA_ENV = { - "TRIM_BLOCKS": True, - "LSTRIP_BLOCKS": True, -} - -# defining base directory -BASE_DIR = os.path.abspath(os.path.dirname(__file__)) - -# defining database URI -# MySQL example -# SQLALCHEMY_DATABASE_URI = "mysql://username:password@server/db" -# SQLite example -# SQLALCHEMY_DATABASE_URI = "sqlite:///" + os.path.join(BASE_DIR, "db.sqlite3") -SQLALCHEMY_TRACK_MODIFICATIONS = False - -# defining Babel settings -BABEL_DEFAULT_LOCALE = "fr" - -# Languages available -AVAILABLE_LANGUAGES = { - "en": "English", - "fr": "Français", -} - -API_PER_PAGE = 30 diff --git a/pyproject.toml b/pyproject.toml deleted file mode 100644 index c05b905..0000000 --- a/pyproject.toml +++ /dev/null @@ -1,5 +0,0 @@ -[build-system] -requires = ["setuptools", "wheel"] - -[tool.black] -exclude = "migrations/" diff --git a/requirements-dev.txt b/requirements-dev.txt deleted file mode 100644 index 5424efa..0000000 --- a/requirements-dev.txt +++ /dev/null @@ -1,3 +0,0 @@ -black -pytest -pytest-black diff --git a/requirements.txt b/requirements.txt index d2a5f05..55731a9 100644 --- a/requirements.txt +++ b/requirements.txt @@ -7,7 +7,7 @@ flask_migrate flask_restful flask_sqlalchemy flask_wtf +python-dotenv python-slugify requests mysqlclient - diff --git a/server.py b/server.py index c285874..14dc4ec 100644 --- a/server.py +++ b/server.py @@ -13,38 +13,34 @@ from app.routes import apis, routes from command import commands -app = flask.Flask(__name__, template_folder="app/view") -app.config.from_object("config") -try: - app.config.from_object(f"config-{app.config['ENV']}") -except Exception as e: - print(e) +application = flask.Flask(__name__, template_folder="app/view") +application.config.from_object("settings") -if "JINJA_ENV" in app.config: - app.jinja_env.trim_blocks = app.config["JINJA_ENV"]["TRIM_BLOCKS"] - app.jinja_env.lstrip_blocks = app.config["JINJA_ENV"]["LSTRIP_BLOCKS"] +if "JINJA_ENV" in application.config: + application.jinja_env.trim_blocks = application.config["JINJA_ENV"]["TRIM_BLOCKS"] + application.jinja_env.lstrip_blocks = application.config["JINJA_ENV"]["LSTRIP_BLOCKS"] # Loading routes for route in routes: if len(route) < 3: - app.add_url_rule(route[0], route[1].__name__, route[1], methods=["GET"]) + application.add_url_rule(route[0], route[1].__name__, route[1], methods=["GET"]) else: - app.add_url_rule(route[0], route[1].__name__, route[1], methods=route[2]) + application.add_url_rule(route[0], route[1].__name__, route[1], methods=route[2]) # Loading API routes for route in apis: api.add_resource(route[1], route[0]) # Initialisation of extensions -admin.init_app(app) -api.init_app(app) -babel.init_app(app) -db.init_app(app) -login_manager.init_app(app) -migrate.init_app(app, db) +admin.init_app(application) +api.init_app(application) +babel.init_app(application) +db.init_app(application) +login_manager.init_app(application) +migrate.init_app(application, db) # Manage commands for command in commands: - app.cli.add_command(command) + application.cli.add_command(command) # Manage locale diff --git a/settings.py b/settings.py new file mode 100644 index 0000000..050bcc5 --- /dev/null +++ b/settings.py @@ -0,0 +1,21 @@ +# encoding: utf-8 + +import os + + +BASE_DIR = os.path.abspath(os.path.dirname(__file__)) +DEBUG = os.environ.get("DEBUG", False) +SECRET_KEY = os.environ.get("DEBUG", "Choose a secret key") +JINJA_ENV = { + "TRIM_BLOCKS": True, + "LSTRIP_BLOCKS": True, +} +SQLALCHEMY_DATABASE_URI = os.environ.get("SQLALCHEMY_DATABASE_URI", "sqlite:///" + os.path.join(BASE_DIR, "db.sqlite3")) +SQLALCHEMY_TRACK_MODIFICATIONS = False +BCRYPT_ROUNDS = os.environ.get("BCRYPT_ROUNDS", 15) +BABEL_DEFAULT_LOCALE = os.environ.get("BABEL_DEFAULT_LOCALE", "en") +AVAILABLE_LANGUAGES = { + "en": "English", + "fr": "Français", +} +API_PER_PAGE = os.environ.get("API_PER_PAGE", 10)