Compare commits

...
6 Commits
Author SHA1 Message Date
finnandClaude Opus 5.5 340006ff37 Disable debug default login
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 08:39:28 +00:00
finnandClaude Opus 5.5 86fcdfde27 Devcontainer: enable bash-completion and set UTF-8 locale
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 08:39:28 +00:00
finnandClaude Opus 5.5 e0b5e05cce Use port 8757 for the dev server
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 08:39:28 +00:00
finnandClaude Opus 5.5 9f25457b24 Remove old docker-compose based dev setup
The devcontainer replaces Dockerfile.dev, dc.dev.yml and the related
start/entrypoint scripts.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 08:39:28 +00:00
finnandClaude Opus 5.5 9895017385 Restructure scripts into dev/ and prod/ for the devcontainer
- scripts/prod: entrypoint, db-setup and schema.sql shipped in the image
- scripts/dev: seed/clear db, psql and cleanup, run natively in the
  devcontainer instead of via docker-compose
- move setup.cfg to the repo root so linters pick it up
- Makefile targets run directly in the devcontainer

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 08:39:28 +00:00
finnandClaude Opus 5.5 7c2b9fb3f4 Refactor logging and drop deprecated FLASK_ENV
Move logging setup into max/log.py. The file handler is only used outside
debug mode and its directory is configurable via LOG_DIR. FLASK_ENV is
deprecated in Flask; debug mode is now controlled via FLASK_DEBUG.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 08:21:07 +00:00
23 changed files with 82 additions and 137 deletions

No files matched your search

+8 -4
View File
@@ -1,4 +1,4 @@
# Keep the Python version in sync with Dockerfile.dev
# Keep the Python version in sync with the production Dockerfile
FROM python:3.15-slim-trixie
ARG USERNAME=dev
@@ -7,17 +7,21 @@ ARG USER_GID=$USER_UID
# git + openssh-client: git over the host's forwarded SSH agent
# gcc, libc-dev, libpq-dev: build psycopg2 from requirements.txt
# postgresql-client, wait-for-it: scripts/db-setup.sh, scripts/psql.sh
# postgresql-client, wait-for-it: database scripts
# curl, ca-certificates: Claude Code installer
# vim: personal preferences
# vim, bash-completion: personal preferences
RUN apt-get -y update && \
apt-get install --no-install-recommends -y \
git openssh-client make curl ca-certificates sudo \
gcc libc-dev libpq-dev \
postgresql-client wait-for-it \
vim && \
vim bash-completion && \
apt-get clean && rm -rf /var/lib/apt/lists/*
# Load bash-completion in every interactive shell (e.g. `make <TAB>`). Not in
# ~/.bashrc, since the home dir is a volume (see devcontainer.json).
RUN echo '. /usr/share/bash-completion/bash_completion' >> /etc/bash.bashrc
RUN groupadd --gid $USER_GID $USERNAME && \
useradd --uid $USER_UID --gid $USER_GID -m -s /bin/bash $USERNAME && \
echo "$USERNAME ALL=(root) NOPASSWD:ALL" > /etc/sudoers.d/$USERNAME && \
-2
View File
@@ -9,8 +9,6 @@ services:
- home:/home/dev
environment:
- PYTHONPATH=/workspaces/max
- FLASK_APP=max.app
- FLASK_ENV=development
- PGSQL_HOST=postgresql
- PGSQL_PORT=5432
- PGSQL_USER=max
+3 -2
View File
@@ -13,10 +13,11 @@
// Bind mounts are resolved by the host's daemon, so they need host paths:
// use ${LOCAL_WORKSPACE_FOLDER} instead of paths inside this container.
"remoteEnv": {
"LOCAL_WORKSPACE_FOLDER": "${localWorkspaceFolder}"
"LOCAL_WORKSPACE_FOLDER": "${localWorkspaceFolder}",
"LANG": "C.UTF-8"
},
"postCreateCommand": "pip install -U pip && pip install -U -r requirements.txt -r requirements-dev.txt",
"forwardPorts": [
8000
8757
]
}
+1
View File
@@ -1 +1,2 @@
*.pyc
log/
+1 -3
View File
@@ -12,11 +12,9 @@ RUN mkdir log
ENV PYTHONPATH=/app/
ENV FLASK_APP=max.app
ENV FLASK_ENV=production
COPY max/ ./max
COPY scripts/entrypoint.sh .
COPY scripts/db-setup.sh .
COPY scripts/prod/ .
ENTRYPOINT ["./entrypoint.sh"]
CMD ["gunicorn", "-w", "1", "-b", "0.0.0.0:8000", "max.app:app"]
-21
View File
@@ -1,21 +0,0 @@
FROM python:3.15-slim-trixie
RUN apt -y update && apt -y upgrade && \
apt install --no-install-recommends -y wait-for-it lsb-release wget gnupg2 gcc libpq-dev libc-dev vim postgresql-client && apt clean all
WORKDIR /app
COPY requirements.txt .
COPY requirements-dev.txt .
RUN pip install -U -r requirements.txt -r requirements-dev.txt
RUN mkdir log
ENV PYTHONPATH=/app/
ENV FLASK_APP=max.app
ENV FLASK_ENV=development
COPY scripts/* ./
ENTRYPOINT ["./entrypoint-dev.sh"]
CMD ["flask", "run", "--host", "0.0.0.0", "--port", "8000"]
+12 -27
View File
@@ -1,36 +1,21 @@
# DEVELOPMENT SERVER
# DEV
seed-dev: clear-dev
FLASK_APP=max.app FLASK_DEBUG=1 scripts/dev/seed-db.sh
seed-dev:
scripts/entrypoint-dev.sh
clear-dev:
scripts/dev/clear-db.sh
build-dev:
docker build -t max:dev -f Dockerfile.dev .
run-dev:
FLASK_APP=max.app FLASK_DEBUG=1 flask run --host 0.0.0.0 --port 8757
run-dev: | build-dev
# bash -c "trap 'docker-compose -f dc.dev.yml down' EXIT; MAX_COMMAND=\"flask run --host 0.0.0.0 --port 8000\" docker-compose -f dc.dev.yml up --build"
MAX_COMMAND="flask run --host 0.0.0.0 --port 8000" docker-compose -f dc.dev.yml up
run-dev-as-prod:
mkdir -p log
FLASK_APP=max.app LOG_DIR=log gunicorn -w 1 -b 0.0.0.0:8757 max.app:app
run-bash: | build-dev
MAX_COMMAND="sleep infinity" docker-compose -f dc.dev.yml up -d
docker-compose -f dc.dev.yml exec max "./entrypoint-dev.sh"
docker-compose -f dc.dev.yml exec max chown -R $$(id -u $${USER}):$$(id -g $${USER}) /app
docker-compose -f dc.dev.yml exec -u $$(id -u $${USER}):$$(id -g $${USER}) max bash || true
docker-compose -f dc.dev.yml down
run-bash-root: | build-dev
MAX_COMMAND="sleep infinity" docker-compose -f dc.dev.yml up -d
docker-compose -f dc.dev.yml exec max "./entrypoint-dev.sh"
docker-compose -f dc.dev.yml exec max bash || true
docker-compose -f dc.dev.yml down
stop-dev:
docker-compose -f dc.dev.yml down
run-cleanup: | build-dev
docker run -ti -v `pwd`/max:/app/max --entrypoint="" max-dev ./cleanup.sh
run-cleanup:
scripts/dev/cleanup.sh
# PROD
build-prod:
docker build -t max:$(shell cat VERSION) .
+2 -2
View File
@@ -10,7 +10,7 @@
<script>
function test() {
$.post("http://localhost:8000/user/1/toggle-enabled", null, function () {
$.post("http://localhost:8757/user/1/toggle-enabled", null, function () {
console.log("success", arguments);
});
}
@@ -18,7 +18,7 @@
<button onclick="test()">Change</button>
<form action="http://localhost:8000/user/1/toggle-enabled" method="post">
<form action="http://localhost:8757/user/1/toggle-enabled" method="post">
<button type="submit">Submit</button>
</form>
</body>
-2
View File
@@ -1,2 +0,0 @@
#!/bin/bash
docker-compose -f dc.dev.yml $@
-27
View File
@@ -1,27 +0,0 @@
services:
max:
image: max:dev
volumes:
- ./max:/app/max
depends_on:
- postgresql
command: $MAX_COMMAND
ports:
- 8000:8000/tcp
environment:
- PGSQL_HOST=postgresql
- PGSQL_USER=max
- PGSQL_PASSWORD=max
- PGSQL_NAME=max
networks:
- postgresql
postgresql:
image: postgres:18.6
environment:
- POSTGRES_USER=max
- POSTGRES_PASSWORD=max
- POSTGRES_DB=max
networks:
- postgresql
networks:
postgresql:
+1 -37
View File
@@ -1,8 +1,5 @@
import base64
import logging
import os
import sys
from logging.config import dictConfig
from flask import Flask
from werkzeug.routing import Rule as WerkzeugRule
@@ -11,6 +8,7 @@ from .auth import AuthMiddleware, init as init_auth
from .cli import register_cli
from .csrf import CsrfMiddleware
from .db import database
from .log import init_logging
from .middlewares import (
ExceptionMiddleware,
I18NMiddleware,
@@ -27,40 +25,6 @@ class Rule(WerkzeugRule):
super().__init__(*args, **kwargs) # do not set methods
def init_logging(app):
dictConfig(
{
"version": 1,
"formatters": {
"default": {"format": "[%(asctime)s] [%(levelname)s]: %(message)s"}
},
"handlers": {
"wsgi": {
"class": "logging.StreamHandler",
"stream": "ext://flask.logging.wsgi_errors_stream",
"formatter": "default",
},
"stdout": {
"class": "logging.StreamHandler",
"stream": sys.stdout,
"formatter": "default",
},
"file": {
"class": "logging.handlers.RotatingFileHandler",
"filename": "/app/log/max.log",
"formatter": "default",
"maxBytes": 1024 * 1024,
"backupCount": 5,
},
},
"root": {"level": "INFO", "handlers": ["stdout", "file"]},
"gunicorn": {"level": "INFO", "handlers": ["stdout", "file"]},
}
)
logger = logging.getLogger(app.name)
logger.setLevel(logging.DEBUG if app.debug else logging.INFO)
def main():
app = Flask(__name__)
init_logging(app)
+1 -1
View File
@@ -10,7 +10,7 @@ from max.views import redirect
from .auth import logout
DEBUG_DEFAULT_LOGIN = True
DEBUG_DEFAULT_LOGIN = False
class AuthMiddleware(BaseMiddleware):
+38
View File
@@ -0,0 +1,38 @@
import logging
import os
import sys
from logging.config import dictConfig
def init_logging(app):
handlers = {
"stdout": {
"class": "logging.StreamHandler",
"stream": sys.stdout,
"formatter": "default",
},
}
active_handlers = ["stdout"]
if not app.debug:
handlers["file"] = {
"class": "logging.handlers.RotatingFileHandler",
"filename": os.path.join(os.environ.get("LOG_DIR", "/app/log"), "max.log"),
"formatter": "default",
"maxBytes": 1024 * 1024,
"backupCount": 5,
}
active_handlers.append("file")
dictConfig(
{
"version": 1,
"formatters": {
"default": {"format": "[%(asctime)s] [%(levelname)s]: %(message)s"}
},
"handlers": handlers,
"root": {"level": "INFO", "handlers": active_handlers},
"gunicorn": {"level": "INFO", "handlers": active_handlers},
}
)
logger = logging.getLogger(app.name)
logger.setLevel(logging.DEBUG if app.debug else logging.INFO)
@@ -1,4 +1,7 @@
#!/bin/bash
set -e
printf "Black:\n"
black max/
printf "\nIsort:\n"
+6
View File
@@ -0,0 +1,6 @@
#!/bin/bash
set -e
PGPASSWORD="$PGSQL_PASSWORD" psql -v ON_ERROR_STOP=1 -h "$PGSQL_HOST" -p "$PGSQL_PORT" -U "$PGSQL_USER" -d "$PGSQL_NAME" \
-c "DROP SCHEMA public CASCADE; CREATE SCHEMA public;"
+3
View File
@@ -1,2 +1,5 @@
#!/bin/bash
set -e
PGPASSWORD="$PGSQL_PASSWORD" psql -h "$PGSQL_HOST" -p "$PGSQL_PORT" -U "$PGSQL_USER" -d "$PGSQL_NAME"
@@ -1,11 +1,10 @@
#!/bin/bash
set -e
cd "$(dirname "$0")"
source "$(dirname "$0")/db-setup.sh"
source ../prod/db-setup.sh
flask add-domain finn.st
flask add-domain stutzenste.in
flask reset-admin admin@finn.st admin
exec "$@"
@@ -15,6 +15,6 @@ wait-for-it --timeout=15 "$PGSQL_HOST:$PGSQL_PORT"
# Create schema in postgresql
export PGPASSWORD="$PGSQL_PASSWORD"
psql -1 -h "$PGSQL_HOST" -p "$PGSQL_PORT" -U "$PGSQL_USER" -d "$PGSQL_NAME" -f max/db/schema.sql
psql -1 -v ON_ERROR_STOP=1 -h "$PGSQL_HOST" -p "$PGSQL_PORT" -U "$PGSQL_USER" -d "$PGSQL_NAME" -f "$(dirname "${BASH_SOURCE[0]}")/schema.sql"
echo "end db setup"
File renamed without changes.
File renamed without changes.
-3
View File
@@ -1,3 +0,0 @@
#!/bin/bash
export FLASK_ENV=production
gunicorn -w 1 -b 0.0.0.0:8000 max.app:app
-2
View File
@@ -1,2 +0,0 @@
#!/bin/bash
flask run --host 0.0.0.0 --port 8000
View File
File renamed without changes.