diff --git a/README.md b/README.md index 39a02cb..22a4eef 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,8 @@ A Flask web app for a convention booth where visitors request a custom AI-genera ### Customer-facing -- **Request form** (`/request`) — visitors enter name, email, hobbies, notable facts, preferred style/genre, vocal gender preference, and extra requests. A branded banner image is shown. The email field is validated to reduce delivery problems. +- **Request form** (`/request`) — visitors enter name, email, hobbies, notable facts, preferred style/genre, vocal gender preference, and extra requests. A branded banner image is shown. The email field is validated to reduce delivery problems. If the operator marks the booth as closed, this page shows a closed banner and message instead. +- **Closed page** (`/request`) — when the booth is marked closed from `/admin/settings`, visitors see the closed banner and a friendly "goblin engineers are on a break" message. - **Confirmation page** (`/thanks/`) — shows the request number after submission. - **FAQ page** (`/faq`) — answers common customer questions. - **Private player page** (`/play/`) — customer receives an email with a unique link. They can stream Version A and Version B, pick one (or both), or request a limited number of revisions. @@ -59,6 +60,7 @@ pending → prompt_ready → songs_uploaded → awaiting_payment → paid → de | `config.py` | Environment-variable based configuration; defines defaults for DB, uploads, SMTP, and secrets. | | `models.py` | SQLite schema and CRUD helpers. | | `init_db.py` | Standalone script to create the database tables. | +| `templates/closed.html` | Message shown on `/request` when the booth is marked closed. | | `templates/request.html` | Customer request form. | | `templates/thanks.html` | Post-submission confirmation. | | `templates/faq.html` | Customer FAQ page. | @@ -69,6 +71,7 @@ pending → prompt_ready → songs_uploaded → awaiting_payment → paid → de | `templates/admin/settings.html` | Maintenance, settings, backup/restore, and reset page. | | `static/Trollgorithm_booth.jpg` | Banner image on the request page. | | `static/DM-Logo_email.png` | Inline Dionysis Media logo attached to emails. | +| `static/Booth_closed.png` | Banner shown when the booth is marked closed. | | `Dockerfile` | Production container image. | | `docker-compose.yml` | Portainer stack definition. | | `requirements.txt` | Python dependencies. | @@ -131,7 +134,8 @@ After each push to GitLab, go to Portainer → **Stacks** → `theme-song-booth` ## Important notes - **No `.env` file in production.** `docker-compose.yml` passes variables directly from Portainer. This avoids Portainer's `env_file not found` error. -- **Runtime settings persist.** SMTP config, revision limit, auto-refresh interval, and MP3 metadata defaults are stored encrypted (where sensitive) in `booth_settings.json` inside the persistent uploads volume. They survive redeploys. +- **Runtime settings persist.** SMTP config, revision limit, auto-refresh interval, booth open/closed state, and MP3 metadata defaults are stored in `booth_settings.json` inside the persistent uploads volume. They survive redeploys. +- **Booth open/closed switch.** Operators can flip the booth status from `/admin/settings`. When closed, `/request` shows a closed banner and message instead of the form. - **Payments are manual.** The app records a Square payment reference but does not integrate with Square's API. Use a Square Terminal/Reader at the booth. - **Operator queue is the dashboard.** No operator email alerts are sent; approvals and revision notes appear as status changes in `/admin`. - **MP3 metadata.** Uploaded files are tagged with title (from the saved prompt), plus configured artist/album/year/comment values. diff --git a/REVIEW.md b/REVIEW.md index 21f542d..504bf0c 100644 --- a/REVIEW.md +++ b/REVIEW.md @@ -35,6 +35,7 @@ Flask app that lets convention attendees request custom AI-generated theme songs | `templates/admin/dashboard.html` | Queue table + filters + auto-refresh + topbar Reset System button. | | `templates/admin/settings.html` | SMTP config, MP3 metadata defaults, DB backup/restore, health check, reset. | | `templates/faq.html` | Customer FAQ page. | +| `templates/closed.html` | Message shown on `/request` when the booth is marked closed. | | `docker-compose.yml` | No `env_file`; variables come from Portainer. | ## Status meanings @@ -80,7 +81,8 @@ Most can be overridden at runtime from `/admin/settings` and stored in `booth_se - The dashboard uses `basename()` as a function, not a Jinja filter. - Reset System deletes DB rows **and** all files under `UPLOAD_FOLDER`, then resets `sqlite_sequence`. - Runtime settings are stored in the persistent uploads volume (`booth_settings.json`). -- Container cannot read host paths; all static assets used at runtime (logo, banner, favicons) must be in the repo or a mounted volume. +- The `booth_open` setting controls whether `/request` shows the form or the closed banner. +- Container cannot read host paths; all static assets used at runtime (logo, banner, favicons, closed banner) must be in the repo or a mounted volume. ## How to redeploy diff --git a/app.py b/app.py index 5377977..94be26b 100644 --- a/app.py +++ b/app.py @@ -308,6 +308,12 @@ def build_signature_images(): return [(str(logo_path), 'dm-logo')] +def get_booth_open(): + """Return True if the booth is currently marked as open in runtime settings.""" + cfg = load_booth_settings() + return cfg.get('booth_open', True) + + # --------------------------------------------------------------------------- # Public customer routes # --------------------------------------------------------------------------- @@ -323,11 +329,14 @@ def index(): def request_form(): """ Public request form. - GET -> shows the form with the banner image. + GET -> shows the form with the banner image, or a closed message if the booth is closed. POST -> validates the email, creates a database record, sends a confirmation email, and redirects to the thanks page. Rate limited to 5 submissions per minute per IP. """ + if not get_booth_open(): + return render_template('closed.html') + if request.method == 'POST': form_data = { 'name': request.form.get('name', '').strip(), @@ -694,9 +703,9 @@ def admin_settings(): """ Settings / maintenance page for operators. GET -> show database health, statistics, disk usage, runtime settings forms, - SMTP/email config, MP3 metadata defaults, backup/restore, and reset. + booth open/closed switch, SMTP/email config, MP3 metadata defaults, backup/restore, and reset. POST -> handle one of: fix_db, reset_system, save_max_revisions, save_metadata, - save_email_config, send_test_email, save_refresh, download_db, restore_db. + save_email_config, send_test_email, save_refresh, save_booth_open, download_db, restore_db. """ redir = require_admin() if redir: @@ -717,6 +726,7 @@ def admin_settings(): runtime_settings = load_booth_settings() current_max_revisions = runtime_settings.get('max_revisions', current_app.config.get('MAX_REVISIONS', 2)) current_refresh_seconds = runtime_settings.get('refresh_seconds', 10) + booth_open = runtime_settings.get('booth_open', True) # Effective email config to show in the form (non-sensitive only; password left blank). email_form = { @@ -868,6 +878,15 @@ def admin_settings(): flash(f'Dashboard auto-refresh set to {val} seconds.', 'success') return redirect(url_for('admin_settings')) + elif action == 'save_booth_open': + # Toggle whether the public request form is accepting submissions. + cfg = load_booth_settings() + cfg['booth_open'] = request.form.get('booth_open', '1') == '1' + save_booth_settings(cfg) + state = 'open' if cfg['booth_open'] else 'closed' + flash(f'Booth is now {state}.', 'success') + return redirect(url_for('admin_settings')) + elif action == 'download_db': # Send the SQLite database file as a download. if db_path.exists(): @@ -915,6 +934,7 @@ def admin_settings(): upload_path=str(upload_root), current_max_revisions=current_max_revisions, current_refresh_seconds=current_refresh_seconds, + booth_open=booth_open, email_form=email_form, metadata=runtime_settings, ) diff --git a/static/Booth_closed.png b/static/Booth_closed.png new file mode 100755 index 0000000..f35c229 Binary files /dev/null and b/static/Booth_closed.png differ diff --git a/templates/admin/settings.html b/templates/admin/settings.html index 864cbcb..b4edceb 100644 --- a/templates/admin/settings.html +++ b/templates/admin/settings.html @@ -171,6 +171,21 @@
+ +
+

Booth Status

+

When closed, the public request page shows a "booth closed" message instead of the form.

+
+ + + + +
+
+

Database Health

diff --git a/templates/closed.html b/templates/closed.html new file mode 100644 index 0000000..e9e43ac --- /dev/null +++ b/templates/closed.html @@ -0,0 +1,49 @@ + + + + + + Trollgorithm Theme Song Booth — Closed + + + +
+ +
+

Trollgorithm and the goblin engineers are on a break.

+

Please come back in a while and they should be back to work!

+
+
+ +