aboutsummaryrefslogtreecommitdiff
path: root/webmentions_ssg/__init__.py
diff options
context:
space:
mode:
authorDennis Fink2026-08-19 19:49:55 +0200
committerDennis Fink2026-08-19 19:49:55 +0200
commitd0e245d63e014d268310976fc45429f08d2cbbe9 (patch)
treeef2e96570723190baf02f05c2c4644a19ff1af78 /webmentions_ssg/__init__.py
parentd4a03623544aac0ce911d8471ff4b033bc3255e1 (diff)
downloadwebmentions-ssg-d0e245d63e014d268310976fc45429f08d2cbbe9.tar.gz
webmentions-ssg-d0e245d63e014d268310976fc45429f08d2cbbe9.zip
refactor(core): improve typing and test coverage
Restructure database and user CLI commands, make passwords write-only model properties, and use the UTC datetime constant throughout the application. Add PEP 287-style documentation and expand receiver, scanner, sender, and URL security tests to cover error paths and edge cases.
Diffstat (limited to 'webmentions_ssg/__init__.py')
-rw-r--r--webmentions_ssg/__init__.py97
1 files changed, 58 insertions, 39 deletions
diff --git a/webmentions_ssg/__init__.py b/webmentions_ssg/__init__.py
index b799678..3928195 100644
--- a/webmentions_ssg/__init__.py
+++ b/webmentions_ssg/__init__.py
@@ -11,6 +11,7 @@ import click
import coloredlogs
import sqlalchemy as sa
from flask import Flask
+from flask.cli import AppGroup
from flask_bootstrap import Bootstrap5
from flask_migrate import Migrate, upgrade
from flask_sqlalchemy import SQLAlchemy
@@ -35,14 +36,25 @@ DATABASE = SQLAlchemy(model_class=Base)
MIGRATE = Migrate()
HUEY = Huey()
+USERS_CLI = AppGroup("users", help="Handle users")
+DB_CLI = AppGroup("db", help="Handle database")
+
def create_app(config_class: type[DefaultConfig] | None = None) -> Flask:
- """Creates and configures the Flask application."""
+ """
+ Create and configure the Flask application.
+
+ The application configuration is selected based on the development
+ environment unless an explicit configuration class is provided. Logging,
+ extensions, authentication, Huey, blueprints, and CLI commands are then
+ initialized.
- development = os.environ.get(
- "WEBMENTIONS_SSG_DEV",
- "",
- ).lower() in {
+ :param config_class: Configuration class to use, or ``None`` to select the
+ development or production configuration automatically.
+ :return: Configured Flask application.
+ """
+
+ development = os.environ.get("WEBMENTIONS_SSG_DEV", "").lower() in {
"1",
"true",
"yes",
@@ -53,8 +65,7 @@ def create_app(config_class: type[DefaultConfig] | None = None) -> Flask:
config_class = DevelopmentConfig if development else ProductionConfig
app = Flask(
- __name__,
- instance_path=str(Path("testing").absolute()) if development else None,
+ __name__, instance_path=str(Path("testing").absolute()) if development else None
)
app.config.from_object(config_class)
@@ -84,9 +95,7 @@ def create_app(config_class: type[DefaultConfig] | None = None) -> Flask:
werkzeug_logger.addHandler(log_file_handler)
coloredlogs.install(
- level=app.config["LOG_LEVEL"],
- logger=app.logger,
- fmt=app.config["LOG_FORMAT"],
+ level=app.config["LOG_LEVEL"], logger=app.logger, fmt=app.config["LOG_FORMAT"]
)
coloredlogs.install(
level=app.config["LOG_LEVEL"],
@@ -106,44 +115,54 @@ def create_app(config_class: type[DefaultConfig] | None = None) -> Flask:
@app.context_processor
def inject_version():
- return dict(VERSION=VERSION)
+ """
+ Add the application version to the template context.
+
+ :return: Template context containing the application version.
+ """
+ return {"VERSION": VERSION}
from .views import root_page
app.register_blueprint(root_page)
- @app.cli.command("create-db")
- def create_db() -> None:
- """Create or update the database to the latest migration."""
- upgrade()
+ app.cli.add_command(USERS_CLI)
- @app.cli.command("create-user")
- @click.argument("username")
- @click.password_option(
- confirmation_prompt=True,
- )
- def create_user(
- username: str,
- password: str,
- ) -> None:
- """Create a user."""
+ return app
- from .models import User
- if (
- DATABASE.session.scalar(sa.select(User).where(User.username == username))
- is not None
- ):
- raise click.ClickException(f"User {username!r} already exists.")
+@USERS_CLI.command("create", help="Create or add users")
+@click.option("--username", prompt=True)
+@click.password_option(confirmation_prompt=True)
+def create_user(username: str, password: str) -> None:
+ """
+ Create a new user.
- user = User(
- username=username,
- )
- user.set_password(password)
+ :param username: Username of the user to create.
+ :param password: Plain-text password to hash and store for the user.
+ :raises click.ClickException: If a user with the given username already
+ exists.
+ """
- DATABASE.session.add(user)
- DATABASE.session.commit()
+ from .models import User
- click.echo(f"Created user {username!r}.")
+ if (
+ DATABASE.session.scalar(sa.select(User).where(User.username == username))
+ is not None
+ ):
+ raise click.ClickException(f"User {username!r} already exists.")
- return app
+ user = User(username=username, password=password)
+
+ DATABASE.session.add(user)
+ DATABASE.session.commit()
+
+ click.echo(f"Created user {username!r}.")
+
+
+@DB_CLI.command("create")
+def create_db() -> None:
+ """
+ Upgrade the database to the latest migration revision.
+ """
+ upgrade()