diff --git a/.env.example b/.env.example index 262ec21..6502dae 100644 --- a/.env.example +++ b/.env.example @@ -1,11 +1,11 @@ APP_SECRET_KEY=change-me-in-production -ADMIN_PASSWORD_HASH= +ADMIN_PASSWORD=change-me SMTP_HOST=mailroot8.namespro.ca SMTP_PORT=465 SMTP_USER=ai@hallsworth.ca SMTP_PASS= SMTP_FROM=ai@hallsworth.ca -ADMIN_ALERT_EMAIL=ai@hallsworth.ca +ADMIN_ALERT_EMAIL= PUBLIC_BASE_URL=http://127.0.0.1:5000 BOOTH_NAME=Trollgorithm Theme Songs DATABASE=/app/data/booth.db diff --git a/README.md b/README.md index 2eb4ab7..38a83fa 100644 --- a/README.md +++ b/README.md @@ -19,17 +19,11 @@ cd /home/jess/workspace/theme-song-booth python3 -m venv .venv .venv/bin/pip install -r requirements.txt cp .env.example .env -# Edit .env and set APP_SECRET_KEY, ADMIN_PASSWORD_HASH, SMTP_PASS, PUBLIC_BASE_URL +# Edit .env and set APP_SECRET_KEY, ADMIN_PASSWORD, SMTP_PASS, PUBLIC_BASE_URL .venv/bin/python init_db.py .venv/bin/python -m flask --app app run --host=0.0.0.0 ``` -Generate an admin password hash with: - -```bash -.venv/bin/python -c "from werkzeug.security import generate_password_hash; print(generate_password_hash('yourpassword'))" -``` - ## Deployment with Portainer The repo includes a `docker-compose.yml` that builds directly from GitLab, so Portainer can pull and deploy it as a stack. @@ -42,10 +36,10 @@ The repo includes a `docker-compose.yml` that builds directly from GitLab, so Po - Compose path: `docker-compose.yml` 4. Add environment variables in Portainer (or upload a `.env` file): - `APP_SECRET_KEY` - - `ADMIN_PASSWORD_HASH` + - `ADMIN_PASSWORD` - `SMTP_PASS` - `PUBLIC_BASE_URL` (your HTTPS domain) - - `ADMIN_ALERT_EMAIL` + - `BOOTH_NAME` (optional) 5. Deploy the stack. 6. Open a console into the `booth` container and run once: ```bash diff --git a/app.py b/app.py index 962b328..0998c1d 100644 --- a/app.py +++ b/app.py @@ -5,7 +5,6 @@ from email.message import EmailMessage from pathlib import Path from flask import Flask, request, render_template, redirect, url_for, flash, session, send_from_directory, abort, current_app -from werkzeug.security import check_password_hash, generate_password_hash from werkzeug.utils import secure_filename from config import Config @@ -33,6 +32,9 @@ def require_admin(): if not is_admin(): return redirect(url_for('admin_login')) +def admin_password_ok(pw): + return pw and pw == current_app.config['ADMIN_PASSWORD'] + def allowed_file(filename): return '.' in filename and filename.rsplit('.', 1)[1].lower() in current_app.config['ALLOWED_EXTENSIONS'] @@ -120,17 +122,9 @@ def approve(token): update_request(req['id'], customer_approved=choice, status='awaiting_payment', approval_notified_at=now_utc()) - # alert operator - alert_to = current_app.config['ADMIN_ALERT_EMAIL'] or current_app.config['SMTP_FROM'] - if alert_to: - admin_link = f"{current_app.config['PUBLIC_BASE_URL']}/admin/request/{req['id']}" - version_label = {'a': 'A', 'b': 'B', 'both': 'Both'}[choice] - body = f"{req['name']} ({req['email']}) approved: Version {version_label}.\n\nRequest #{req['id']}\nPayment is now due.\n\nOpen admin: {admin_link}" - try: - send_email(alert_to, f"{req['name']} approved their theme song", body) - except Exception as e: - flash(f'Approval saved, but operator alert failed: {e}', 'warning') - return redirect(url_for('play', token=token)) + # alert operator (disabled — admin dashboard is the queue) + # alert_to = current_app.config['ADMIN_ALERT_EMAIL'] or current_app.config['SMTP_FROM'] + # if alert_to: ... flash('Thanks! Please return to the booth to finalize payment.', 'success') return redirect(url_for('play', token=token)) @@ -143,17 +137,9 @@ def revise(token): note = request.form.get('revision_note', '').strip() update_request(req['id'], revision_note=note, status='songs_uploaded') - alert_to = current_app.config['ADMIN_ALERT_EMAIL'] or current_app.config['SMTP_FROM'] - if alert_to and note: - admin_link = f"{current_app.config['PUBLIC_BASE_URL']}/admin/request/{req['id']}" - body = f"{req['name']} ({req['email']}) requested changes for request #{req['id']}.\n\nNote:\n{note}\n\nOpen admin: {admin_link}" - try: - send_email(alert_to, f"{req['name']} requested changes", body) - except Exception as e: - flash(f'Revision saved, but operator alert failed: {e}', 'warning') - return redirect(url_for('play', token=token)) - - flash('Your feedback has been sent. We will regenerate and update you.', 'success') + # Revision feedback is stored in the DB and surfaced on the admin dashboard. + # No operator email is sent — the dashboard is the single queue. + flash('Your feedback has been saved. We will regenerate and update you.', 'success') return redirect(url_for('play', token=token)) @app.route('/audio//.mp3') @@ -176,14 +162,10 @@ def admin_login(): if is_admin(): return redirect(url_for('admin_dashboard')) if request.method == 'POST': - pw_hash = current_app.config['ADMIN_PASSWORD_HASH'] - if not pw_hash: - flash('Admin password is not configured.', 'error') - elif check_password_hash(pw_hash, request.form.get('password', '')): + if admin_password_ok(request.form.get('password', '')): session['admin'] = True return redirect(url_for('admin_dashboard')) - else: - flash('Invalid password.', 'error') + flash('Invalid password.', 'error') return render_template('admin/login.html') @app.route('/admin/logout') diff --git a/config.py b/config.py index efedc68..b9cd4cc 100644 --- a/config.py +++ b/config.py @@ -15,7 +15,7 @@ class Config: SMTP_PASS = os.environ.get('SMTP_PASS', '') SMTP_FROM = os.environ.get('SMTP_FROM', 'ai@hallsworth.ca') - ADMIN_PASSWORD_HASH = os.environ.get('ADMIN_PASSWORD_HASH', '') + ADMIN_PASSWORD = os.environ.get('ADMIN_PASSWORD', '') ADMIN_ALERT_EMAIL = os.environ.get('ADMIN_ALERT_EMAIL', '') PUBLIC_BASE_URL = os.environ.get('PUBLIC_BASE_URL', 'http://127.0.0.1:5000')