46602f1933
Remove dead code identified in docs/SANITIZATION-REVIEW.md:
- app/blueprints/content_old.py and app/blueprints/playlist.py
- app/models/group.py, app/utils/nginx_config_reader.py
- orphaned templates (content_list, edit_content, upload_content,
player_page) and the related group/Template references
Result: 6 blueprints, 82 routes, no dead modules or orphan templates.
Add files that deploy.sh and docker-entrypoint.sh already require but
which were never tracked:
- https_manager.py (referenced by deploy.sh, migrate_network.sh,
docker-entrypoint.sh)
- Caddyfile.example (seeded by deploy.sh; its absence aborts deploy)
Relocate generated Graphify artifacts from graphify-out/ to
docs/graphify-out/ (110 files, no content change) and archive the
superseded docs under docs/.
Ignore hygiene:
- ignore ad-hoc .env backups (.env.bak*) — they contain live secrets
- keep the pre-sanitization snapshots (docs/legacy code/,
docs/old_code_documentation/) on disk but out of the repo
Fix .env.example: drop a duplicated config block, genericize the
hardcoded host IP, and document HOSTNAME_INTERNAL.
180 lines
5.9 KiB
Markdown
180 lines
5.9 KiB
Markdown
# 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: `content_old.py` (legacy content routes) was **deleted** during the code
|
|
> sanitization pass — see [SANITIZATION-REVIEW.md](SANITIZATION-REVIEW.md). The
|
|
> legacy `playlist.py` blueprint remains (redirect-only).
|
|
|
|
---
|
|
|
|
## 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_new.html, media_library.html,
|
|
│ upload_media.html, manage_playlist_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/<content_id>/ (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)
|