Skip to content

Commit

Permalink
Merge pull request #44 from toddbirchard/feature/gunicorn-migration
Browse files Browse the repository at this point in the history
Migrate to Gunicorn.
  • Loading branch information
toddbirchard authored Dec 16, 2023
2 parents 0afc8a3 + 0c997a0 commit ebee3dc
Show file tree
Hide file tree
Showing 25 changed files with 159 additions and 682 deletions.
6 changes: 2 additions & 4 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
ENVIRONMENT=production

FLASK_APP=main.py
FLASK_APP=wsgi.py
FLASK_DEBUG=False
SECRET_KEY=yoursecretkey

SQLALCHEMY_DATABASE_URI=mysql+pymysql://myuser:[email protected]:1234/mydatabase

LESS_BIN=/usr/local/bin/lessc
COMPRESSOR_DEBUG=False
ASSETS_DEBUG=False
LESS_RUN_IN_DEBUG=False

12 changes: 7 additions & 5 deletions .github/workflows/pythonapp.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,18 +15,20 @@ jobs:

steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v4
- uses: actions/setup-python@v5
with:
python-version: "3.10"
cache: "pip" # caching pip dependencies

- name: Install dependencies
run: |
python -m pip install --upgrade pip setuptools wheel
pip install -r requirements.txt
python -m pip install --upgrade pip
pip install flake8 pytest
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
- name: Lint with flake8
run: |
python3 -m pip install flake8
# stop the build if there are Python syntax errors or undefined names
flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
# exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -129,3 +129,7 @@ dmypy.json

# Pyre type checker
.pyre/

# IDEs
.vscode/
.idea/
15 changes: 7 additions & 8 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,7 @@ $(VIRTUAL_ENV):

.PHONY: run
run: env
export LESS_BIN=$(shell which lessc) && \
$(LOCAL_PYTHON) -m main
$(LOCAL_PYTHON) -m gunicorn -w 4 wsgi:app

.PHONY: install
install: env
Expand Down Expand Up @@ -78,11 +77,11 @@ lint: env
.PHONY: clean
clean:
find . -name '.coverage' -delete && \
find . -name '*.pyc' -delete \
find . -name '__pycache__' -delete \
find . -name 'poetry.lock' -delete \
find . -name '*.log' -delete \
find . -name '.DS_Store' -delete \
find . -name '*.pyc' -delete && \
find . -name '__pycache__' -delete && \
find . -name 'poetry.lock' -delete && \
find . -name '*.log' -delete && \
find . -name '.DS_Store' -delete && \
find . -wholename '**/*.pyc' -delete && \
find . -wholename '**/*.html' -delete && \
find . -type d -wholename '__pycache__' -exec rm -rf {} + && \
Expand All @@ -91,4 +90,4 @@ clean:
find . -type d -wholename '**/.pytest_cache' -exec rm -rf {} + && \
find . -type d -wholename '**/*.log' -exec rm -rf {} + && \
find . -type d -wholename './.reports/*' -exec rm -rf {} + && \
find . -type d -wholename '**/.webassets-cache' -exec rm -rf {}
find . -type d -wholename '**/.webassets-cache' -exec rm -rf {} +
12 changes: 4 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Flask-Login Tutorial

![Python](https://img.shields.io/badge/Python-v^3.10-blue.svg?logo=python&longCache=true&logoColor=white&colorB=5e81ac&style=flat-square&colorA=4c566a)
![Python](https://img.shields.io/badge/Python-v3.10-blue.svg?logo=python&longCache=true&logoColor=white&colorB=5e81ac&style=flat-square&colorA=4c566a)
![Flask](https://img.shields.io/badge/Flask-v3.0.0-blue.svg?longCache=true&logo=flask&style=flat-square&logoColor=white&colorB=5e81ac&colorA=4c566a)
![Flask-Login](https://img.shields.io/badge/Flask--Login-v0.6.3-blue.svg?longCache=true&logo=flask&style=flat-square&logoColor=white&colorB=5e81ac&colorA=4c566a)
![Flask-Assets](https://img.shields.io/badge/Flask--Assets-v2.1.0-blue.svg?longCache=true&logo=flask&style=flat-square&logoColor=white&colorB=5e81ac&colorA=4c566a)
Expand All @@ -22,23 +22,19 @@ Add user authentication to your Flask app.

Get set up locally in two steps:

### I. Environment Variables
### Environment Variables

Replace the values in **.env.example** with your values and rename this file to **.env**:

* `FLASK_APP`: Entry point of your application (should be `main.py`).
* `FLASK_DEBUG`: Whether to enable "DEBUG" logging (either `True` or `False`).
* `ENVIRONMENT`: The environment to run your app in (either `development` or `production`).
* `FLASK_DEBUG`: Whether to enable "DEBUG" logging (either `True` or `False`).
* `SECRET_KEY`: Randomly generated string of characters used to encrypt your app's data.
* `SQLALCHEMY_DATABASE_URI`: Connection URI of a SQL database.
* `LESS_BIN`: Path to your local LESS installation via `which lessc` (optional for static assets).
* `ASSETS_DEBUG`: Debug asset creation and bundling in `development` (optional).
* `LESS_RUN_IN_DEBUG`: Debug LESS while in `development` (optional).
* `COMPRESSOR_DEBUG`: Debug asset compression while in `development` (optional).

*Remember never to commit secrets saved in .env files to Github.*

### II. Installation
### Installation

Get up and running with `make deploy`:

Expand Down
10 changes: 6 additions & 4 deletions config.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@ class Config:

# General Config
ENVIRONMENT = environ.get("ENVIRONMENT")
FLASK_APP = environ.get("FLASK_APP")

# Flask Config
FLASK_APP = "wsgi.py"
FLASK_DEBUG = environ.get("FLASK_DEBUG")
SECRET_KEY = environ.get("SECRET_KEY")

Expand All @@ -23,10 +25,10 @@ class Config:

# Flask-Assets
LESS_BIN = environ.get("LESS_BIN")
ASSETS_DEBUG = environ.get("ASSETS_DEBUG")
LESS_RUN_IN_DEBUG = environ.get("LESS_RUN_IN_DEBUG")
ASSETS_DEBUG = False
LESS_RUN_IN_DEBUG = False

# Static Assets
STATIC_FOLDER = "static"
TEMPLATES_FOLDER = "templates"
COMPRESSOR_DEBUG = environ.get("COMPRESSOR_DEBUG")
COMPRESSOR_DEBUG = False
6 changes: 3 additions & 3 deletions flask_login_tutorial/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@


def create_app():
"""Construct the core app object."""
"""Construct application object."""
app = Flask(__name__, instance_relative_config=False)
app.config.from_object("config.Config")

Expand All @@ -21,8 +21,8 @@ def create_app():
from .assets import compile_static_assets

# Register Blueprints
app.register_blueprint(routes.main_bp)
app.register_blueprint(auth.auth_bp)
app.register_blueprint(routes.main_blueprint)
app.register_blueprint(auth.auth_blueprint)

# Create Database Models
db.create_all()
Expand Down
28 changes: 18 additions & 10 deletions flask_login_tutorial/auth.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
"""Routes for user authentication."""
from typing import Optional

from flask import Blueprint, flash, redirect, render_template, request, url_for
from flask_login import current_user, login_user

Expand All @@ -7,10 +9,10 @@
from .models import User, db

# Blueprint Configuration
auth_bp = Blueprint("auth_bp", __name__, template_folder="templates", static_folder="static")
auth_blueprint = Blueprint("auth_blueprint", __name__, template_folder="templates", static_folder="static")


@auth_bp.route("/signup", methods=["GET", "POST"])
@auth_blueprint.route("/signup", methods=["GET", "POST"])
def signup():
"""
User sign-up page.
Expand All @@ -27,7 +29,7 @@ def signup():
db.session.add(user)
db.session.commit() # Create new user
login_user(user) # Log in as newly created user
return redirect(url_for("main_bp.dashboard"))
return redirect(url_for("main_blueprint.dashboard"))
flash("A user already exists with that email address.")
return render_template(
"signup.jinja2",
Expand All @@ -38,7 +40,7 @@ def signup():
)


@auth_bp.route("/login", methods=["GET", "POST"])
@auth_blueprint.route("/login", methods=["GET", "POST"])
def login():
"""
Log-in page for registered users.
Expand All @@ -48,17 +50,17 @@ def login():
"""
# Bypass if user is logged in
if current_user.is_authenticated:
return redirect(url_for("main_bp.dashboard"))
return redirect(url_for("main_blueprint.dashboard"))
form = LoginForm()
# Validate login attempt
if form.validate_on_submit():
user = User.query.filter_by(email=form.email.data).first()
if user and user.check_password(password=form.password.data):
login_user(user)
next_page = request.args.get("next")
return redirect(next_page or url_for("main_bp.dashboard"))
return redirect(next_page or url_for("main_blueprint.dashboard"))
flash("Invalid username/password combination")
return redirect(url_for("auth_bp.login"))
return redirect(url_for("auth_blueprint.login"))
return render_template(
"login.jinja2",
form=form,
Expand All @@ -69,8 +71,14 @@ def login():


@login_manager.user_loader
def load_user(user_id):
"""Check if user is logged-in upon page load."""
def load_user(user_id: int) -> Optional[User]:
"""
Check if user is logged-in upon page load.
:param int user_id: User ID from session cookie.
:returns: bool
"""
if user_id is not None:
return User.query.get(user_id)
return None
Expand All @@ -80,4 +88,4 @@ def load_user(user_id):
def unauthorized():
"""Redirect unauthorized users to Login page."""
flash("You must be logged in to view that page.")
return redirect(url_for("auth_bp.login"))
return redirect(url_for("auth_blueprint.login"))
2 changes: 1 addition & 1 deletion flask_login_tutorial/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ class User(UserMixin, db.Model):

def set_password(self, password):
"""Create hashed password."""
self.password = generate_password_hash(password, method="sha256")
self.password = generate_password_hash(password)

def check_password(self, password):
"""Check hashed password."""
Expand Down
8 changes: 4 additions & 4 deletions flask_login_tutorial/routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@
from flask_login import current_user, login_required, logout_user

# Blueprint Configuration
main_bp = Blueprint("main_bp", __name__, template_folder="templates", static_folder="static")
main_blueprint = Blueprint("main_blueprint", __name__, template_folder="templates", static_folder="static")


@main_bp.route("/", methods=["GET"])
@main_blueprint.route("/", methods=["GET"])
@login_required
def dashboard():
"""Logged-in User Dashboard."""
Expand All @@ -19,9 +19,9 @@ def dashboard():
)


@main_bp.route("/logout")
@main_blueprint.route("/logout")
@login_required
def logout():
"""User log-out logic."""
logout_user()
return redirect(url_for("auth_bp.login"))
return redirect(url_for("auth_blueprint.login"))
2 changes: 1 addition & 1 deletion flask_login_tutorial/static/dist/css/account.css

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Binary file removed flask_login_tutorial/static/dist/img/favicon.png
Binary file not shown.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file removed flask_login_tutorial/static/dist/img/logo.png
Binary file not shown.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit ebee3dc

Please sign in to comment.