Add owner-only logging and Brickset API call tracking

LIM configured no logging at all, so the instruction fetcher had no trail to follow. Adds a rotating file handler on the root logger plus two owner-only admin pages.

- Admin > Logs: filter by level, source and text; tracebacks kept with their error; auto-refresh, raw download, rotated-file browsing and rotate-now

- Admin > Logs > Brickset API calls: every outbound call recorded with method, params, caller, duration, result count and error, plus daily quota accounting (only getSets counts) and a comparison against getKeyUsageStats

- New owner concept (services/ownership.py): a single account, distinct from is_admin, since these pages expose other admins activity. Defaults to the first admin; flask owner show/set to change

- Services now log through module loggers; retention setting and pruning for call records

- New CLI: flask owner show/set, flask logs path/prune

- New table brickset_api_calls, created automatically at boot; LOG_* env vars, /data/logs in Docker
This commit is contained in:
jessikitty committed 2026-09-27 10:42:49 +10:00
1 parent 89f65e2d74
commit f7c20de5ac
27 files changed
+1914 -35

No files matched your search

+16
View File
@@ -19,6 +19,10 @@ def create_app(config_name='default'):
# Load configuration
app.config.from_object(config[config_name])
# Logging first, so anything below can report problems.
from app.services.logging_setup import configure_logging
configure_logging(app)
# Initialize extensions
db.init_app(app)
@@ -48,6 +52,7 @@ def create_app(config_name='default'):
from app.routes.moc import bp as moc_bp
from app.routes.backup import backup_bp
from app.routes.instruction_fetch import instruction_fetch_bp
from app.routes.logs import logs_bp
app.register_blueprint(auth_bp)
app.register_blueprint(main_bp)
@@ -58,6 +63,7 @@ def create_app(config_name='default'):
app.register_blueprint(moc_bp)
app.register_blueprint(backup_bp)
app.register_blueprint(instruction_fetch_bp)
app.register_blueprint(logs_bp)
# Import models to ensure they're registered with SQLAlchemy
from app.models.user import User
@@ -67,6 +73,7 @@ def create_app(config_name='default'):
from app.models.set_share import SetShare
from app.models.site_setting import SiteSetting
from app.models.instruction_fetch import InstructionFetchJob
from app.models.api_call import BricksetApiCall
# Add any columns missing from an older database (additive only).
if app.config.get('AUTO_SCHEMA_UPGRADE', True):
@@ -103,6 +110,15 @@ def create_app(config_name='default'):
from app.cli import register_cli
register_cli(app)
# Templates need to know whether to show owner-only links.
@app.context_processor
def inject_ownership():
from app.services.ownership import is_owner
try:
return {'current_user_is_owner': is_owner()}
except Exception:
return {'current_user_is_owner': False}
# Background ticker for the automatic instruction fetcher. It does
# nothing at all until an admin enables it on the fetcher page.
from app.services import fetch_worker