# 04 · Application Core Covers the application factory, configuration, extensions, middleware, CLI commands, context processors, and template layer. This is **Community 4** in the Graphify knowledge graph. --- ## 1. Application Factory — `app/app.py` The entire app is constructed by `create_app(config_name=None)`: ```mermaid flowchart TD A["create_app()"] --> B["Set instance_path"] B --> C["Load config: Dev / Prod / Test"] C --> D["ProxyFix middleware"] D --> E["ScriptNameFix middleware"] E --> F["init extensions: db bcrypt login migrate cache cors"] F --> G["configure_login_manager()"] G --> H["register_blueprints()"] H --> I["register_error_handlers()"] I --> J["register_commands()"] J --> K["register_context_processors()"] K --> L["register_template_filters()"] L --> M["init_portal_sso(app)"] M --> N["db.create_all() (idempotent)"] N --> O["return app"] ``` ### Blueprint registration ```python def register_blueprints(app): from app.blueprints.main import main_bp from app.blueprints.auth import auth_bp from app.blueprints.admin import admin_bp from app.blueprints.players import players_bp from app.blueprints.content import content_bp from app.blueprints.playlist import playlist_bp from app.blueprints.api import api_bp ... ``` > Note: `app.blueprints.content_old` is **not** imported — it is dead/legacy code. --- ## 2. Configuration — `app/config.py` Four classes: `Config` (base) → `DevelopmentConfig`, `ProductionConfig`, `TestingConfig`. | Setting | Value | Notes | |---|---|---| | `MAX_CONTENT_LENGTH` | 2 GB | upload cap | | `UPLOAD_FOLDER` | `app/static/uploads` | media | | `UPLOAD_FOLDERLOGO` | `app/static/resurse` | logos | | `ALLOWED_EXTENSIONS` | png jpg jpeg gif bmp mp4 avi mkv mov webm pdf ppt pptx | | | `PERMANENT_SESSION_LIFETIME` | 30 min | | | `SESSION_COOKIE_SECURE` | False (True in prod) | | | `ITEMS_PER_PAGE` | 20 | | | `SERVER_VERSION` | 2.0.0 | shown in UI footer | | `PLAYER_CODE_DIR` | `/app/data/player` | staged player source | | `PLAYER_REPO_URL` | env `PLAYER_REPO_URL` | Kiwy-Signage repo | | DB (dev) | `instance/dev.db` | | | DB (prod) | `instance/dashboard.db` | | | DB (test) | in-memory | | --- ## 3. Extensions — `app/extensions.py` Centralizes shared singletons: ```python db = SQLAlchemy() bcrypt = Bcrypt() login_manager = LoginManager() # login_view='auth.login' migrate = Migrate() cache = Cache() cors = CORS() ``` CORS is configured in `create_app` for `/api/*`: all origins, GET/POST/OPTIONS/PUT/DELETE, supports credentials. --- ## 4. Middleware ### ProxyFix (Werkzeug) `app.wsgi_app = ProxyFix(app.wsgi_app, x_for=1, x_proto=1, x_host=1, x_port=1)` — trusts one reverse-proxy hop (Caddy / umbrella nginx). ### ScriptNameFix — `app/utils/script_name_fix.py` `ScriptNameFix` WSGI middleware sets `SCRIPT_NAME` from `HTTP_X_SCRIPT_NAME` so `url_for()` generates correct paths when the app is mounted under a sub-path (e.g. `/digiserver`). --- ## 5. Login Manager ```python login_manager.login_view = 'auth.login' login_manager.login_message = 'Please log in to access this page.' @login_manager.user_loader def load_user(user_id): return User.query.get(int(user_id)) ``` --- ## 6. Error Handlers | Code | Template | Behaviour | |---|---|---| | 404 | `errors/404.html` | Not found | | 403 | `errors/403.html` | Forbidden | | 500 | `errors/500.html` | Rolls back DB session | | 413 | `errors/413.html` | Payload too large | | 408 | `errors/408.html` | Request timeout | The `/api` blueprint registers its own **JSON** 404/405/500 handlers. --- ## 7. CLI Commands | Command | Purpose | |---|---| | `flask init-db` | `db.create_all()` | | `flask create-admin --username X` | Create admin user (prompts for password) | | `flask seed-db` | Seed sample data (blocked in production) | --- ## 8. Context Processors - **`inject_config`** — exposes `server_version`, `build_date`, `logo_exists` to all templates. - **`inject_user_theme`** — exposes the authenticated user's `theme` (`light`/`dark`) for the UI toggle. --- ## 9. Template Filters - **`localtime`** — converts naive/UTC datetimes to local time with a configurable `strftime` format (default `%Y-%m-%d %H:%M`). --- ## 10. Template & Static Layout ``` app/templates/ ├── base.html ← main layout (theme, logos, nav) ├── dashboard.html ├── auth/ login.html, register.html, change_password.html ├── admin/ admin.html, user_management.html, leftover_media.html, │ dependencies.html, customize_logos.html, editing_users.html, │ https_config.html, build_player.html ├── content/ content_list.html (legacy), content_list_new.html (modern), │ media_library.html, upload_content.html (legacy), │ upload_media.html, manage_playlist_content.html, edit_content.html ├── players/ players_list.html, add_player.html, edit_player.html, │ manage_player.html, player_page.html, player_fullscreen.html, │ edited_media.html, edited_media_report.html, _deploy_badge.html └── errors/ 403.html, 404.html, 500.html (+413/408) app/static/ ├── icons/ edit, home, info, monitor, moon, playlist, sun, trash, upload, warning (SVG) ├── uploads/ uploaded media + edited_media// (versioned edits) └── (resurse/ logo storage — referenced by config) ``` --- ## 11. Portal SSO — `app/utils/portal_sso.py` `init_portal_sso(app)` registers a `before_request` hook that reads `X-Auth-Username` / `X-Auth-Role` headers (set by the umbrella nginx gateway). If present, it auto-creates and logs in the local `User` (`_get_or_create_user`). This lets the app sit behind an existing corporate SSO portal. --- > Next: [05 · Blueprints & API](05-blueprints-api.md)