Update app templates and routes
This commit is contained in:
@@ -16,6 +16,9 @@ RUN pip install --no-cache-dir -r requirements.txt \
|
|||||||
|
|
||||||
COPY . .
|
COPY . .
|
||||||
|
|
||||||
|
# Ensure entrypoint is executable
|
||||||
|
RUN chmod +x docker/entrypoint.sh
|
||||||
|
|
||||||
# Create runtime dirs (also mountable as volumes)
|
# Create runtime dirs (also mountable as volumes)
|
||||||
RUN mkdir -p instance app/static/uploads
|
RUN mkdir -p instance app/static/uploads
|
||||||
|
|
||||||
@@ -27,4 +30,4 @@ ENV FLASK_ENV=production \
|
|||||||
GUNICORN_BIND=0.0.0.0:8000
|
GUNICORN_BIND=0.0.0.0:8000
|
||||||
|
|
||||||
# Run via WSGI entrypoint
|
# Run via WSGI entrypoint
|
||||||
CMD ["sh", "-c", "gunicorn -w ${GUNICORN_WORKERS} -b ${GUNICORN_BIND} wsgi:app"]
|
CMD ["sh", "-c", "./docker/entrypoint.sh"]
|
||||||
|
|||||||
15
README.md
15
README.md
@@ -71,10 +71,23 @@ environment variables via your process manager / secrets.
|
|||||||
|
|
||||||
This repo includes a `docker-compose.yml` for a one-command startup.
|
This repo includes a `docker-compose.yml` for a one-command startup.
|
||||||
|
|
||||||
|
On first run, the container will ensure the SQLite schema exists.
|
||||||
|
If you provide `ADMIN_PASS`, it will also create/update the initial admin user.
|
||||||
|
|
||||||
```powershell
|
```powershell
|
||||||
docker compose up --build
|
docker compose up --build
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Create an admin on startup (recommended):
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
$env:ADMIN_EMAIL="you@example.com"
|
||||||
|
$env:ADMIN_PASS="YourStrongPassword"
|
||||||
|
docker compose up --build
|
||||||
|
```
|
||||||
|
|
||||||
|
Or put these in a `.env` file used by Compose.
|
||||||
|
|
||||||
Run in the background:
|
Run in the background:
|
||||||
|
|
||||||
```powershell
|
```powershell
|
||||||
@@ -217,5 +230,7 @@ If the reset email is not received:
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ from werkzeug.exceptions import RequestEntityTooLarge
|
|||||||
|
|
||||||
from .extensions import db, login_manager
|
from .extensions import db, login_manager
|
||||||
from .models import AppSettings, User
|
from .models import AppSettings, User
|
||||||
from .cli import init_db_command
|
from .cli import ensure_db_command, init_db_command
|
||||||
|
|
||||||
|
|
||||||
def create_app():
|
def create_app():
|
||||||
@@ -85,6 +85,7 @@ def create_app():
|
|||||||
return db.session.get(User, int(user_id))
|
return db.session.get(User, int(user_id))
|
||||||
|
|
||||||
# CLI
|
# CLI
|
||||||
|
app.cli.add_command(ensure_db_command)
|
||||||
app.cli.add_command(init_db_command)
|
app.cli.add_command(init_db_command)
|
||||||
|
|
||||||
# Blueprints
|
# Blueprints
|
||||||
|
|||||||
43
app/cli.py
43
app/cli.py
@@ -5,21 +5,11 @@ from .extensions import db
|
|||||||
from .models import AppSettings, User
|
from .models import AppSettings, User
|
||||||
|
|
||||||
|
|
||||||
@click.command("init-db")
|
def _ensure_schema_and_settings() -> None:
|
||||||
@click.option(
|
"""Create tables + run lightweight SQLite migrations + ensure settings row exists."""
|
||||||
"--admin-email",
|
|
||||||
required=False,
|
|
||||||
default="beheer@alphen.cloud",
|
|
||||||
show_default=True,
|
|
||||||
help="Email for the initial admin",
|
|
||||||
)
|
|
||||||
@click.option("--admin-pass", required=True, help="Password for the initial admin")
|
|
||||||
@with_appcontext
|
|
||||||
def init_db_command(admin_email: str, admin_pass: str):
|
|
||||||
"""Create tables and ensure an admin account exists."""
|
|
||||||
db.create_all()
|
db.create_all()
|
||||||
|
|
||||||
# Lightweight migration for older SQLite DBs: ensure User.email column exists.
|
# Lightweight migration for older SQLite DBs: ensure columns exist.
|
||||||
# This avoids requiring Alembic for this small project.
|
# This avoids requiring Alembic for this small project.
|
||||||
try:
|
try:
|
||||||
cols = [r[1] for r in db.session.execute(db.text("PRAGMA table_info(user)")).fetchall()]
|
cols = [r[1] for r in db.session.execute(db.text("PRAGMA table_info(user)")).fetchall()]
|
||||||
@@ -50,6 +40,33 @@ def init_db_command(admin_email: str, admin_pass: str):
|
|||||||
db.session.add(AppSettings(id=1))
|
db.session.add(AppSettings(id=1))
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
|
||||||
|
|
||||||
|
@click.command("ensure-db")
|
||||||
|
@with_appcontext
|
||||||
|
def ensure_db_command():
|
||||||
|
"""Create tables / apply lightweight migrations.
|
||||||
|
|
||||||
|
This is useful for container startup where you want schema readiness,
|
||||||
|
without requiring admin credentials.
|
||||||
|
"""
|
||||||
|
_ensure_schema_and_settings()
|
||||||
|
click.echo("Database ready.")
|
||||||
|
|
||||||
|
|
||||||
|
@click.command("init-db")
|
||||||
|
@click.option(
|
||||||
|
"--admin-email",
|
||||||
|
required=False,
|
||||||
|
default="beheer@alphen.cloud",
|
||||||
|
show_default=True,
|
||||||
|
help="Email for the initial admin",
|
||||||
|
)
|
||||||
|
@click.option("--admin-pass", required=True, help="Password for the initial admin")
|
||||||
|
@with_appcontext
|
||||||
|
def init_db_command(admin_email: str, admin_pass: str):
|
||||||
|
"""Create tables and ensure an admin account exists."""
|
||||||
|
_ensure_schema_and_settings()
|
||||||
|
|
||||||
admin_email = (admin_email or "").strip().lower()
|
admin_email = (admin_email or "").strip().lower()
|
||||||
if not admin_email:
|
if not admin_email:
|
||||||
raise click.UsageError("--admin-email is required")
|
raise click.UsageError("--admin-email is required")
|
||||||
|
|||||||
@@ -7,9 +7,16 @@ services:
|
|||||||
environment:
|
environment:
|
||||||
# Override in a .env file or your shell; this default is only for convenience.
|
# Override in a .env file or your shell; this default is only for convenience.
|
||||||
SECRET_KEY: "change-me"
|
SECRET_KEY: "change-me"
|
||||||
|
# Optional bootstrap (only runs if ADMIN_PASS is set)
|
||||||
|
ADMIN_EMAIL: ${ADMIN_EMAIL:-admin@admin.admin}
|
||||||
|
ADMIN_PASS: ${ADMIN_PASS:-}
|
||||||
# Optional overrides (the Dockerfile already defaults these)
|
# Optional overrides (the Dockerfile already defaults these)
|
||||||
GUNICORN_WORKERS: "2"
|
GUNICORN_WORKERS: "2"
|
||||||
GUNICORN_BIND: "0.0.0.0:8000"
|
GUNICORN_BIND: "0.0.0.0:8000"
|
||||||
|
# Entrypoint (from Dockerfile) runs:
|
||||||
|
# - `flask ensure-db`
|
||||||
|
# - optional `flask init-db` when ADMIN_PASS is set
|
||||||
|
# - gunicorn
|
||||||
volumes:
|
volumes:
|
||||||
# Persist SQLite DB and uploads on the host
|
# Persist SQLite DB and uploads on the host
|
||||||
- ./instance:/app/instance
|
- ./instance:/app/instance
|
||||||
|
|||||||
12
docker/entrypoint.sh
Normal file
12
docker/entrypoint.sh
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
set -eu
|
||||||
|
|
||||||
|
# Ensure DB schema exists
|
||||||
|
flask --app app ensure-db
|
||||||
|
|
||||||
|
# Optional: create/update initial admin account
|
||||||
|
if [ -n "${ADMIN_PASS:-}" ]; then
|
||||||
|
flask --app app init-db --admin-email "${ADMIN_EMAIL:-admin@admin.admin}" --admin-pass "${ADMIN_PASS}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
exec gunicorn -w "${GUNICORN_WORKERS:-2}" -b "${GUNICORN_BIND:-0.0.0.0:8000}" wsgi:app
|
||||||
Reference in New Issue
Block a user