diff --git a/.env.example b/.env.example
new file mode 100644
index 0000000..0eaafd4
--- /dev/null
+++ b/.env.example
@@ -0,0 +1,118 @@
+# Quizzical Beats - Environment Variables Example
+# Copy this file to .env and replace with your actual values
+# SECURITY NOTE: Never commit .env file to version control!
+
+# ============================================================================
+# CRITICAL SECURITY SETTINGS - MUST BE SET
+# ============================================================================
+
+# Secret key for Flask session management (REQUIRED)
+# Generate with: python -c 'import secrets; print(secrets.token_hex(32))'
+SECRET_KEY=
+
+# Automation token for API endpoints (REQUIRED)
+# Generate with: python -c 'import secrets; print(secrets.token_urlsafe(32))'
+AUTOMATION_TOKEN=
+
+# ============================================================================
+# DEBUG SETTINGS
+# ============================================================================
+# WARNING: Set DEBUG=False in production environments!
+DEBUG=False
+DEBUG2=False
+
+# ============================================================================
+# DATABASE CONFIGURATION
+# ============================================================================
+SQLALCHEMY_DATABASE_URI=sqlite:///data/song_data.db
+SQLALCHEMY_TRACK_MODIFICATIONS=False
+
+# ============================================================================
+# HTTPS CONFIGURATION
+# ============================================================================
+# Set to True when running behind a reverse proxy with HTTPS (e.g., Traefik, nginx)
+USE_HTTPS=False
+# PREFERRED_URL_SCHEME=https
+
+# ============================================================================
+# SPOTIFY API CONFIGURATION (REQUIRED)
+# ============================================================================
+# Get credentials from: https://developer.spotify.com/dashboard/applications
+SPOTIFY_CLIENT_ID=
+SPOTIFY_CLIENT_SECRET=
+SPOTIFY_REDIRECT_URI=http://localhost:5000/auth/spotify/callback
+
+# ============================================================================
+# MUSIC METADATA APIs
+# ============================================================================
+# Last.fm API for genre enrichment (REQUIRED)
+# Get from: https://www.last.fm/api/account/create
+LASTFM_API_KEY=
+
+# ============================================================================
+# DEEZER API CONFIGURATION (OPTIONAL)
+# ============================================================================
+# Get credentials from: https://developers.deezer.com/myapps
+DEEZER_APP_ID=
+DEEZER_APP_SECRET=
+DEEZER_REDIRECT_URI=http://localhost:5000/deezer-callback
+
+# ============================================================================
+# OAUTH PROVIDERS (OPTIONAL)
+# ============================================================================
+
+# Google OAuth
+GOOGLE_CLIENT_ID=
+GOOGLE_CLIENT_SECRET=
+
+# Authentik OAuth (self-hosted SSO)
+AUTHENTIK_CLIENT_ID=
+AUTHENTIK_CLIENT_SECRET=
+AUTHENTIK_METADATA_URL=
+
+# Dropbox OAuth (for cloud export)
+DROPBOX_APP_KEY=
+DROPBOX_APP_SECRET=
+DROPBOX_REDIRECT_URI=http://localhost:5000/users/dropbox/callback
+
+# ============================================================================
+# AI SERVICES (OPTIONAL)
+# ============================================================================
+# OpenAI API for quiz generation features
+OPENAI_API_KEY=
+OPENAI_URL=https://api.openai.com/v1
+OPENAI_MODEL=gpt-4o-mini
+OPENAI_SEARCH_MODEL=gpt-4o-mini-search-preview
+
+# ElevenLabs API for voice generation
+ELEVENLABS_API_KEY=
+
+# Translation services (optional)
+DEEPL_API_KEY=
+MEANINGCLOUD_API_KEY=
+
+# ACRCloud for audio fingerprinting (optional)
+ACRCLOUD_TOKEN=
+
+# ============================================================================
+# EMAIL CONFIGURATION (OPTIONAL)
+# ============================================================================
+MAIL_HOST=smtp.example.com
+MAIL_PORT=587
+MAIL_USE_TLS=True
+MAIL_USE_SSL=False
+MAIL_USERNAME=
+MAIL_PASSWORD=
+MAIL_SENDER=quizzical-beats@example.com
+MAIL_RECIPIENT=admin@example.com
+
+# ============================================================================
+# ADVANCED CONFIGURATION (OPTIONAL)
+# ============================================================================
+# Static OAuth URLs for production (when behind complex reverse proxies)
+# STATIC_OAUTH_URLS=False
+# OAUTH_SPOTIFY_AUTH_URL=
+# OAUTH_SPOTIFY_LINK_URL=
+# OAUTH_GOOGLE_URL=
+# OAUTH_AUTHENTIK_URL=
+# OAUTH_DROPBOX_URL=
diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml
new file mode 100644
index 0000000..346cd29
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/bug_report.yml
@@ -0,0 +1,139 @@
+name: Bug Report
+description: Report a bug or unexpected behavior
+title: "[Bug]: "
+labels: ["bug", "needs-triage"]
+body:
+ - type: markdown
+ attributes:
+ value: |
+ Thanks for taking the time to report a bug! Please fill out the form below to help us investigate.
+
+ - type: textarea
+ id: description
+ attributes:
+ label: Bug Description
+ description: A clear and concise description of what the bug is.
+ placeholder: What happened?
+ validations:
+ required: true
+
+ - type: textarea
+ id: steps
+ attributes:
+ label: Steps to Reproduce
+ description: Steps to reproduce the behavior
+ placeholder: |
+ 1. Go to '...'
+ 2. Click on '...'
+ 3. Scroll down to '...'
+ 4. See error
+ validations:
+ required: true
+
+ - type: textarea
+ id: expected
+ attributes:
+ label: Expected Behavior
+ description: What did you expect to happen?
+ placeholder: I expected...
+ validations:
+ required: true
+
+ - type: textarea
+ id: actual
+ attributes:
+ label: Actual Behavior
+ description: What actually happened?
+ placeholder: Instead...
+ validations:
+ required: true
+
+ - type: dropdown
+ id: version
+ attributes:
+ label: Version
+ description: What version of Quizzical Beats are you running?
+ options:
+ - Latest (main branch)
+ - v1.9 (Security Hardening)
+ - v1.8 (Documentation Dynamo)
+ - v1.7 (Dropbox Dispatch)
+ - v1.6 (Bulletproof Backups)
+ - Other (please specify in additional context)
+ validations:
+ required: true
+
+ - type: dropdown
+ id: deployment
+ attributes:
+ label: Deployment Method
+ description: How are you running Quizzical Beats?
+ options:
+ - Docker Compose (recommended)
+ - Docker (custom)
+ - Manual installation (pip)
+ - Other (please specify)
+ validations:
+ required: true
+
+ - type: textarea
+ id: environment
+ attributes:
+ label: Environment Details
+ description: |
+ Provide relevant environment information:
+ - OS (e.g., Ubuntu 22.04, macOS 13, Windows 11)
+ - Python version (e.g., 3.11)
+ - Browser (if web UI issue)
+ - Database (SQLite, PostgreSQL, MySQL)
+ placeholder: |
+ - OS: Ubuntu 22.04
+ - Python: 3.11.5
+ - Browser: Chrome 120
+ - Database: SQLite
+ validations:
+ required: false
+
+ - type: textarea
+ id: logs
+ attributes:
+ label: Relevant Logs
+ description: |
+ Please copy and paste any relevant log output. This will be automatically formatted into code.
+ **Security Note**: Remove any API keys, tokens, or personal information!
+ render: shell
+ placeholder: |
+ Paste logs here...
+ validations:
+ required: false
+
+ - type: textarea
+ id: screenshots
+ attributes:
+ label: Screenshots
+ description: If applicable, add screenshots to help explain your problem.
+ placeholder: Drag and drop images here or paste URLs
+ validations:
+ required: false
+
+ - type: textarea
+ id: context
+ attributes:
+ label: Additional Context
+ description: Add any other context about the problem here.
+ placeholder: Any additional information that might be helpful...
+ validations:
+ required: false
+
+ - type: checkboxes
+ id: checklist
+ attributes:
+ label: Pre-submission Checklist
+ description: Please confirm the following before submitting
+ options:
+ - label: I have searched existing issues to ensure this is not a duplicate
+ required: true
+ - label: I have removed any sensitive information (API keys, passwords, etc.) from logs and screenshots
+ required: true
+ - label: I am using a supported version of Quizzical Beats
+ required: true
diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml
new file mode 100644
index 0000000..b6aa9a8
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/feature_request.yml
@@ -0,0 +1,114 @@
+name: Feature Request
+description: Suggest a new feature or enhancement
+title: "[Feature]: "
+labels: ["enhancement", "needs-triage"]
+body:
+ - type: markdown
+ attributes:
+ value: |
+ Thanks for suggesting a feature! Please provide as much detail as possible.
+
+ - type: textarea
+ id: problem
+ attributes:
+ label: Problem Statement
+ description: Is your feature request related to a problem? Please describe.
+ placeholder: I'm always frustrated when...
+ validations:
+ required: true
+
+ - type: textarea
+ id: solution
+ attributes:
+ label: Proposed Solution
+ description: Describe the solution you'd like to see
+ placeholder: I would like...
+ validations:
+ required: true
+
+ - type: textarea
+ id: alternatives
+ attributes:
+ label: Alternatives Considered
+ description: Describe any alternative solutions or features you've considered
+ placeholder: I also thought about...
+ validations:
+ required: false
+
+ - type: dropdown
+ id: priority
+ attributes:
+ label: Priority
+ description: How important is this feature to you?
+ options:
+ - Critical (blocking my use)
+ - High (would significantly improve experience)
+ - Medium (nice to have)
+ - Low (minor enhancement)
+ validations:
+ required: true
+
+ - type: dropdown
+ id: category
+ attributes:
+ label: Feature Category
+ description: Which area does this feature relate to?
+ options:
+ - Import/Export
+ - Round Generation
+ - User Interface
+ - Authentication
+ - API Integration
+ - Performance
+ - Security
+ - Documentation
+ - Other
+ validations:
+ required: true
+
+ - type: textarea
+ id: use-case
+ attributes:
+ label: Use Case
+ description: Describe how you would use this feature
+ placeholder: I would use this feature to...
+ validations:
+ required: false
+
+ - type: textarea
+ id: mockups
+ attributes:
+ label: Mockups or Examples
+ description: If applicable, add mockups, wireframes, or links to similar features in other apps
+ placeholder: Drag and drop images here or paste URLs
+ validations:
+ required: false
+
+ - type: textarea
+ id: context
+ attributes:
+ label: Additional Context
+ description: Add any other context about the feature request here
+ placeholder: Any additional information...
+ validations:
+ required: false
+
+ - type: checkboxes
+ id: contribution
+ attributes:
+ label: Contribution
+ description: Would you be willing to contribute to this feature?
+ options:
+ - label: I would like to implement this feature myself
+ - label: I can help test this feature
+ - label: I can help write documentation for this feature
+
+ - type: checkboxes
+ id: checklist
+ attributes:
+ label: Pre-submission Checklist
+ options:
+ - label: I have searched existing issues and feature requests
+ required: true
+ - label: I have checked the roadmap to see if this is already planned
+ required: true
diff --git a/.github/ISSUE_TEMPLATE/security.yml b/.github/ISSUE_TEMPLATE/security.yml
new file mode 100644
index 0000000..c5a274f
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/security.yml
@@ -0,0 +1,59 @@
+name: Security Vulnerability
+description: Report a security vulnerability (private disclosure)
+title: "[Security]: "
+labels: ["security"]
+body:
+ - type: markdown
+ attributes:
+ value: |
+ ## ⚠️ SECURITY NOTICE
+
+ **DO NOT** report security vulnerabilities in public issues!
+
+ Please report security issues privately via email to:
+ **christian@kaufdeinquiz.com**
+
+ See [SECURITY.md](https://github.com/christianlouis/QuizzicalBeats/blob/main/SECURITY.md) for our full security policy.
+
+ ---
+
+ This issue template is for non-critical security improvements or discussions only.
+
+ - type: textarea
+ id: description
+ attributes:
+ label: Security Concern Description
+ description: Describe the security improvement or concern (not a vulnerability)
+ placeholder: I noticed that...
+ validations:
+ required: true
+
+ - type: dropdown
+ id: severity
+ attributes:
+ label: Severity
+ description: How severe is this concern?
+ options:
+ - Low (security improvement suggestion)
+ - Medium (potential security issue)
+ - High (security vulnerability - REPORT VIA EMAIL!)
+ validations:
+ required: true
+
+ - type: textarea
+ id: impact
+ attributes:
+ label: Potential Impact
+ description: What could happen if this is not addressed?
+ placeholder: This could lead to...
+ validations:
+ required: false
+
+ - type: textarea
+ id: recommendation
+ attributes:
+ label: Recommendation
+ description: What steps should be taken to address this?
+ placeholder: I recommend...
+ validations:
+ required: false
diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md
index 7d10ffc..eae07fb 100644
--- a/.github/PULL_REQUEST_TEMPLATE.md
+++ b/.github/PULL_REQUEST_TEMPLATE.md
@@ -1,32 +1,152 @@
-# Pull Request Template
+# Pull Request
## Description
-Provide a brief description of the changes made in this pull request. Include the motivation and context for the change.
-
-Fixes # (issue)
+
## Type of Change
+
+
- [ ] Bug fix (non-breaking change which fixes an issue)
- [ ] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
- [ ] Documentation update
+- [ ] Performance improvement
+- [ ] Code refactoring
+- [ ] Security fix
+- [ ] Dependency update
-## Checklist
+## Related Issues
-- [ ] My code follows the style guidelines of this project
-- [ ] I have performed a self-review of my own code
-- [ ] I have commented my code, particularly in hard-to-understand areas
-- [ ] I have made corresponding changes to the documentation
-- [ ] I have added tests that prove my fix is effective or that my feature works
-- [ ] New and existing unit tests pass locally with my changes
-- [ ] Any dependent changes have been merged and published in downstream modules
+
+
+Fixes #
+Relates to #
+
+## Changes Made
+
+
+
+-
+-
+-
+
+## Testing
+
+
+
+### Test Environment
+
+- OS:
+- Python Version:
+- Database:
+- Browser (if applicable):
+
+### Tests Performed
+
+- [ ] Unit tests pass
+- [ ] Integration tests pass
+- [ ] Manual testing completed
+- [ ] Browser compatibility tested (if UI changes)
+
+### Test Coverage
+
+
+
+- [ ] Added new tests for new functionality
+- [ ] Updated existing tests
+- [ ] No tests needed (documentation/config only)
+
+## Security Checklist
+
+
+
+- [ ] No sensitive data (API keys, passwords, tokens) included in code
+- [ ] All user inputs are validated and sanitized
+- [ ] No SQL injection vulnerabilities
+- [ ] No XSS vulnerabilities
+- [ ] Dependencies checked for known vulnerabilities
+- [ ] Security implications documented (if applicable)
+
+## Documentation
+
+
+
+- [ ] Code comments added/updated
+- [ ] README.md updated (if needed)
+- [ ] Documentation updated (if needed)
+- [ ] CHANGELOG.md updated
+- [ ] API documentation updated (if applicable)
## Screenshots (if applicable)
-Add screenshots to help explain your changes if applicable.
+
-## Additional Context
+### Before
-Add any other context or information about the pull request here.
\ No newline at end of file
+
+### After
+
+
+## Deployment Notes
+
+
+
+- [ ] Database migrations required
+- [ ] Environment variables added/changed
+- [ ] Configuration changes needed
+- [ ] Dependencies updated (requirements.txt)
+- [ ] No deployment steps required
+
+### Migration Steps
+
+
+
+1.
+2.
+
+## Performance Impact
+
+
+
+- [ ] No performance impact
+- [ ] Performance improved
+- [ ] Performance impact (explain below)
+
+## Breaking Changes
+
+
+
+
+
+## Checklist
+
+
+
+- [ ] My code follows the project's coding standards (PEP 8)
+- [ ] I have performed a self-review of my code
+- [ ] I have commented my code, particularly in hard-to-understand areas
+- [ ] I have made corresponding changes to the documentation
+- [ ] My changes generate no new warnings
+- [ ] I have added tests that prove my fix is effective or that my feature works
+- [ ] New and existing unit tests pass locally with my changes
+- [ ] Any dependent changes have been merged and published
+- [ ] I have checked my code for security vulnerabilities
+
+## Additional Notes
+
+
+
+
+---
+
+## For Reviewers
+
+### Review Focus Areas
+
+- [ ] Security implications
+- [ ] Performance impact
+- [ ] Code quality and maintainability
+- [ ] Test coverage
+- [ ] Documentation completeness
\ No newline at end of file
diff --git a/AGENTS.md b/AGENTS.md
index 8839c9d..7f95210 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -1,22 +1,421 @@
-# AGENTS Instructions for Quizzical Beats
+# AI Agent Instructions for Quizzical Beats
-These instructions apply to the entire repository.
+This document provides guidelines for AI coding agents working on the Quizzical Beats repository.
-## Development Guidelines
-- Follow **PEP 8** with a maximum line length of **100 characters**.
-- Use **4 spaces** for indentation. Tabs are not allowed.
-- Provide docstrings for all functions and classes using Google's style guide.
-- Keep Flask routes organized using blueprints and prefer class-based views for complex endpoints.
+## Project Overview
+
+**Quizzical Beats** is a Flask-based web application for creating music quiz rounds for pub quizzes. It integrates with multiple music APIs (Spotify, Deezer, Last.fm) and provides PDF/MP3 export capabilities.
+
+## Technology Stack
+
+- **Backend**: Python 3.11+, Flask 3.x
+- **Database**: SQLAlchemy ORM (SQLite dev, PostgreSQL/MySQL production)
+- **Frontend**: Jinja2 templates, vanilla JavaScript
+- **APIs**: Spotify, Deezer, Last.fm, OpenAI, Dropbox
+- **Authentication**: Flask-Login, Authlib (OAuth)
+- **Deployment**: Docker, Docker Compose
+
+## Repository Structure
+
+```
+musicround/ # Main application package
+├── __init__.py # Application factory
+├── config.py # Configuration management
+├── models.py # Database models (SQLAlchemy)
+├── helpers/ # Utility modules
+├── routes/ # Flask blueprints (auth, api, core, generate, etc.)
+├── static/ # CSS, JavaScript, images
+└── templates/ # Jinja2 HTML templates
+
+tests/ # Test suite
+docs/ # MkDocs documentation
+migrations/ # Database migration scripts
+```
+
+## Code Style Guidelines
+
+### Python Style
+- Follow **PEP 8** with maximum line length of **100 characters**
+- Use **4 spaces** for indentation (no tabs)
+- Provide docstrings for all functions and classes (Google style)
+- Use type hints where beneficial
+
+```python
+def process_playlist(playlist_id: str, user_id: int) -> dict:
+ """Process a Spotify playlist and import songs.
+
+ Args:
+ playlist_id: The Spotify playlist ID
+ user_id: The user's database ID
+
+ Returns:
+ Dictionary containing import results with keys:
+ - success: Boolean indicating success
+ - songs_imported: Number of songs imported
+ - errors: List of error messages (if any)
+ """
+ pass
+```
+
+### Flask Best Practices
+- Organize routes using blueprints
+- Prefer class-based views for complex endpoints
+- Use Flask-WTF for form handling
+- Always validate and sanitize user inputs
+- Use SQLAlchemy ORM (never raw SQL without parameterization)
+
+### Security Requirements
+- **NEVER** commit API keys, secrets, or passwords
+- **ALWAYS** use environment variables for sensitive data
+- Validate all user inputs
+- Use parameterized queries (SQLAlchemy ORM does this)
+- Escape all template outputs (Jinja2 auto-escaping)
+- Check [SECURITY.md](SECURITY.md) before making security-related changes
+
+## Development Workflow
+
+### Before Making Changes
+
+1. **Understand the codebase**:
+ - Read related code in `musicround/routes/` and `musicround/helpers/`
+ - Check existing tests in `tests/`
+ - Review documentation in `docs/`
+
+2. **Check existing issues and roadmap**:
+ - Review [TODO.md](TODO.md) for planned features
+ - Check [ROADMAP.md](ROADMAP.md) for strategic direction
+ - Search GitHub issues for related discussions
+
+3. **Set up development environment**:
+ ```bash
+ python -m venv venv
+ source venv/bin/activate # On Windows: venv\Scripts\activate
+ pip install -r requirements.txt
+ ```
+
+### Making Changes
+
+1. **Create minimal, focused changes**:
+ - Make the smallest change that solves the problem
+ - Don't refactor unrelated code
+ - Don't fix unrelated bugs or style issues
+
+2. **Write tests**:
+ - Add tests for new functionality in `tests/`
+ - Update existing tests if behavior changes
+ - Run tests: `pytest tests/ -v`
+
+3. **Update documentation**:
+ - Update docstrings for modified functions
+ - Update `docs/` if user-facing changes
+ - Update `README.md` if installation/setup changes
+
+### Testing
+
+```bash
+# Run all tests
+pytest tests/ -v
+
+# Run specific test file
+pytest tests/test_metadata.py -v
+
+# Run with coverage
+pytest --cov=musicround tests/
+```
+
+### Linting and Code Quality
+
+```bash
+# Format code (if black is installed)
+black musicround/ --line-length 100
+
+# Check code style
+flake8 musicround/ --max-line-length=100
+
+# Type checking (if mypy is installed)
+mypy musicround/
+```
+
+## Common Tasks
+
+### Adding a New Route
+
+```python
+# In musicround/routes/new_feature.py
+from flask import Blueprint, render_template, request
+from flask_login import login_required, current_user
+
+new_feature_bp = Blueprint('new_feature', __name__)
+
+@new_feature_bp.route('/new-feature')
+@login_required
+def index():
+ """Display the new feature page."""
+ return render_template('new_feature/index.html')
+```
+
+Then register in `musicround/__init__.py`:
+```python
+from musicround.routes.new_feature import new_feature_bp
+app.register_blueprint(new_feature_bp)
+```
+
+### Adding a Database Model
+
+```python
+# In musicround/models.py
+class NewModel(db.Model):
+ """Description of what this model represents."""
+
+ __tablename__ = 'new_model'
+
+ id = db.Column(db.Integer, primary_key=True)
+ name = db.Column(db.String(100), nullable=False)
+ created_at = db.Column(db.DateTime, default=datetime.utcnow)
+
+ def __repr__(self):
+ return f''
+```
+
+Then create a migration:
+```bash
+flask db migrate -m "Add NewModel"
+flask db upgrade
+```
+
+### Adding a Configuration Variable
+
+```python
+# In musicround/config.py
+class Config:
+ NEW_SETTING = os.getenv("NEW_SETTING", "default_value")
+```
+
+Add to `.env.example`:
+```env
+# Description of what this does
+NEW_SETTING=default_value
+```
+
+## Database Migrations
+
+- Migration files are in `migrations/`
+- Use `run_migration.py` to run migrations
+- Always test migrations on a backup database first
+- Document schema changes in migration message
+
+```bash
+# Create a new migration
+python run_migration.py
+
+# Or manually
+flask db migrate -m "Description of change"
+flask db upgrade
+```
+
+## API Integration Guidelines
+
+### Spotify API
+- Token management in `musicround/helpers/spotify_helper.py`
+- Use existing client manager: `SpotifyClientManager`
+- Handle rate limits gracefully (retry with backoff)
+- Always refresh expired tokens
+
+### OAuth Integration
+- OAuth routes in `musicround/routes/auth.py`
+- Store tokens encrypted in database (User model)
+- Implement token refresh before expiration
+- Follow existing patterns for new OAuth providers
+
+## Error Handling
+
+```python
+# Use Flask error handlers
+from musicround.errors import APIError
+
+@app.errorhandler(APIError)
+def handle_api_error(error):
+ return render_template('error.html', error=error), error.status_code
+
+# In your code
+if not valid:
+ raise APIError("Invalid input", status_code=400)
+```
+
+## Logging
+
+```python
+import logging
+
+logger = logging.getLogger(__name__)
+
+# Use appropriate log levels
+logger.debug("Detailed debugging information")
+logger.info("Informational messages")
+logger.warning("Warning messages")
+logger.error("Error messages")
+logger.critical("Critical errors")
+```
## Commit Messages
-- Write clear, concise commit messages in English.
-- Reference related issues when applicable.
-## Testing
-- Run `pytest` before committing changes. If tests fail due to missing
- dependencies or external services, mention this in the PR.
+Follow conventional commit format:
-## Pull Request
-- Summarize major changes and reference relevant documentation updates.
-- Include test results in the PR description.
+```
+():
+
+
+
+
+
+
+
+
+
+
+
**Quizzical Beats** (formerly MusicRound) is a Flask-based web application for building engaging music quiz rounds for pub quizzes. Leveraging the Spotify and Deezer APIs, it allows you to generate rounds based on the least-used genres, decades, or completely random criteria, making your quizzes dynamic and entertaining.
---
@@ -141,6 +148,14 @@ Comprehensive documentation is available at [quizzicalbeats.readthedocs.io](http
- [API Reference](https://quizzicalbeats.readthedocs.io/developer-guide/api-reference.html)
- [FAQ](https://quizzicalbeats.readthedocs.io/faq.html)
+### Additional Documentation
+
+- [SECURITY.md](SECURITY.md) - Security policy, best practices, and vulnerability reporting
+- [ROADMAP.md](ROADMAP.md) - Project roadmap, milestones, and future plans
+- [AGENTS.md](AGENTS.md) - Guidelines for AI coding agents and developers
+- [CONTRIBUTING.md](CONTRIBUTING.md) - Contribution guidelines
+- [TODO.md](TODO.md) - Detailed task list and completed milestones
+
---
## Project Structure
diff --git a/ROADMAP.md b/ROADMAP.md
new file mode 100644
index 0000000..159047f
--- /dev/null
+++ b/ROADMAP.md
@@ -0,0 +1,608 @@
+# Quizzical Beats - Project Roadmap
+
+**Version**: 2026.Q1
+**Last Updated**: February 2026
+
+## Vision Statement
+
+Quizzical Beats aims to be the premier platform for creating, managing, and delivering engaging music quiz experiences. Our roadmap focuses on reliability, scalability, user experience, and AI-powered innovation.
+
+## Current Status
+
+**Latest Release**: v1.9 - "Documentation Dynamo"
+**Active Development**: v2.0 - Scaling & Performance
+**Repository Health**: ✅ Production-Ready
+
+---
+
+## 🎯 Strategic Priorities (2026)
+
+### Q1 2026: Foundation & Security
+**Focus**: Security hardening, stability, and production readiness
+
+### Q2 2026: Scale & Performance
+**Focus**: Concurrent operations, infrastructure, queue systems
+
+### Q3 2026: Intelligence & Automation
+**Focus**: AI features, scraping, advanced search
+
+### Q4 2026: Collaboration & Cloud
+**Focus**: Multi-user features, cloud storage, sharing
+
+---
+
+## 📋 Release Schedule
+
+### ✅ Completed Releases
+
+#### v1.0 - "Spotify Integration Fix"
+*Released: Q4 2024*
+- ✅ Spotify playlist import with pagination
+- ✅ API rate limit handling
+- ✅ Refactored Spotify client
+- ✅ Comprehensive logging
+
+#### v1.1 - "Authentication Foundation"
+*Released: Q4 2024*
+- ✅ User database schema
+- ✅ Local authentication system
+- ✅ User management interfaces
+- ✅ Role-based access control
+- ✅ Secure password handling
+
+#### v1.2 - "Spotify OAuth Integration"
+*Released: Q1 2025*
+- ✅ User-specific Spotify tokens
+- ✅ OAuth login option
+- ✅ Service account fallback
+- ✅ Playlist-user linking
+
+#### v1.3 - "Enhanced User Experience"
+*Released: Q1 2025*
+- ✅ Custom intro/outro/replay MP3s
+- ✅ User-specific email settings
+- ✅ User preferences system
+
+#### v1.4 - "Multi-Provider OAuth"
+*Released: Q2 2025*
+- ✅ Google OAuth integration
+- ✅ Authentik OAuth integration
+- ✅ Unified authentication experience
+
+#### v1.5 - "Advanced Features"
+*Released: Q2 2025*
+- ✅ Comprehensive logging
+- ✅ System monitoring
+
+#### v1.6 - "Bulletproof Backups"
+*Released: Q3 2025*
+- ✅ Full system backup/restore
+- ✅ Scheduled backups (Ofelia)
+- ✅ Admin backup management UI
+- ✅ Backup versioning
+- ✅ Retention policies
+- ✅ CLI backup tools
+
+#### v1.7 - "Dropbox Dispatch"
+*Released: Q4 2025*
+- ✅ Dropbox OAuth per-user
+- ✅ Round export to Dropbox
+- ✅ ZIP and PDF export
+- ✅ Token refresh handling
+
+#### v1.8 - "Documentation Dynamo"
+*Released: Q4 2025*
+- ✅ User guide with screenshots
+- ✅ FAQ section
+- ✅ API documentation
+- ✅ Architecture documentation
+- ✅ Deployment guides
+- ✅ MkDocs portal
+- ✅ ReadTheDocs integration
+
+#### v1.9 - "Security Hardening"
+*Released: Q1 2026*
+- ✅ Upgraded authlib to 1.6.5+ (CVE fixes)
+- ✅ Required secure SECRET_KEY
+- ✅ Required secure AUTOMATION_TOKEN
+- ✅ Comprehensive SECURITY.md
+- ✅ .env.example template
+- ✅ Security documentation
+
+---
+
+### 🚀 Upcoming Releases
+
+#### v2.0 - "Import Infrastructure" *(Q1 2026 - HIGH PRIORITY)*
+**Status**: 🔴 Not Started
+**Priority**: Critical
+**Effort**: 2-3 weeks
+
+**Goals**: Background processing for large imports
+
+**Features**:
+- [ ] Import queue system (Celery/RQ)
+- [ ] Background worker processes
+- [ ] Concurrent import job support
+- [ ] Priority queue handling
+- [ ] Job retry logic
+- [ ] Dead letter queue for failures
+
+**Success Metrics**:
+- Import 1000+ song playlists without timeout
+- Support 5+ concurrent imports
+- 99% job completion rate
+
+**Dependencies**:
+- Redis or RabbitMQ
+- Worker orchestration (Docker Compose)
+
+---
+
+#### v2.1 - "Progress Pulse" *(Q1 2026 - HIGH PRIORITY)*
+**Status**: 🔴 Not Started
+**Priority**: High
+**Effort**: 1-2 weeks
+
+**Goals**: Real-time import status tracking
+
+**Features**:
+- [ ] WebSocket/SSE progress updates
+- [ ] Progress bars for active imports
+- [ ] Detailed error reporting
+- [ ] Recovery options for failed imports
+- [ ] Email notifications on completion
+- [ ] Import history dashboard
+
+**Success Metrics**:
+- Real-time progress updates (<1s latency)
+- Clear error messages for 90%+ failures
+- User satisfaction with transparency
+
+**Dependencies**:
+- v2.0 (Import Infrastructure)
+- Flask-SocketIO or SSE
+
+---
+
+#### v2.2 - "Server Stability" *(Q1 2026 - HIGH PRIORITY)*
+**Status**: 🔴 Not Started
+**Priority**: Critical
+**Effort**: 1 week
+
+**Goals**: Production-grade web server
+
+**Features**:
+- [ ] Replace Flask dev server with Gunicorn
+- [ ] Configure worker processes (4-8 workers)
+- [ ] Graceful shutdown/restart
+- [ ] Nginx reverse proxy configuration
+- [ ] SSL termination
+- [ ] Static file optimization
+- [ ] Response compression
+- [ ] Security headers
+
+**Success Metrics**:
+- Handle 100+ concurrent users
+- <500ms median response time
+- 99.9% uptime
+- Zero downtime deployments
+
+**Dependencies**:
+- Docker configuration updates
+- nginx configuration
+
+---
+
+#### v2.3 - "Database Durability" *(Q2 2026 - HIGH PRIORITY)*
+**Status**: 🔴 Not Started
+**Priority**: High
+**Effort**: 1-2 weeks
+
+**Goals**: Production database configuration
+
+**Features**:
+- [ ] Connection pooling (SQLAlchemy pool)
+- [ ] Database query optimization
+- [ ] Index creation for common queries
+- [ ] Concurrent write handling
+- [ ] Database performance monitoring
+- [ ] Read replica support (optional)
+- [ ] Zero-downtime migrations
+
+**Success Metrics**:
+- Support 50+ concurrent connections
+- <100ms query execution (95th percentile)
+- No deadlocks or transaction conflicts
+
+**Dependencies**:
+- PostgreSQL or MySQL recommended
+- Monitoring tools (Prometheus/Grafana)
+
+---
+
+#### v2.4 - "Search Supercharge" *(Q2 2026 - MEDIUM PRIORITY)*
+**Status**: 🔴 Not Started
+**Priority**: Medium
+**Effort**: 2 weeks
+
+**Goals**: Advanced search capabilities
+
+**Features**:
+- [ ] Full-text search (PostgreSQL FTS or Elasticsearch)
+- [ ] Relevance scoring improvements
+- [ ] Advanced filters (year, genre, artist, BPM)
+- [ ] Search result caching
+- [ ] Faceted search
+- [ ] Search suggestions/autocomplete
+- [ ] Search analytics
+
+**Success Metrics**:
+- Search results <200ms
+- 80%+ user satisfaction with relevance
+- Support 10,000+ song database efficiently
+
+**Dependencies**:
+- v2.3 (Database Durability)
+- Optional: Elasticsearch
+
+---
+
+#### v2.5 - "Performance Pulse" *(Q2 2026 - MEDIUM PRIORITY)*
+**Status**: 🔴 Not Started
+**Priority**: Medium
+**Effort**: 1-2 weeks
+
+**Goals**: Application performance optimization
+
+**Features**:
+- [ ] Database index optimization
+- [ ] Lazy loading for lists
+- [ ] Pagination for all large datasets
+- [ ] MP3 preview streaming
+- [ ] Redis caching layer
+- [ ] Query result caching
+- [ ] CDN for static assets
+- [ ] Load testing suite
+
+**Success Metrics**:
+- Page load <1s (90th percentile)
+- Support 10,000+ songs in library
+- <100MB memory per worker
+
+**Dependencies**:
+- v2.2 (Server Stability)
+- Redis for caching
+
+---
+
+#### v2.6 - "Textual Transport" *(Q2 2026 - MEDIUM PRIORITY)*
+**Status**: 🔴 Not Started
+**Priority**: Medium
+**Effort**: 2 weeks
+
+**Goals**: Text-based playlist import
+
+**Features**:
+- [ ] Plain text playlist parsing
+- [ ] CSV format support
+- [ ] Artist/song detection algorithms
+- [ ] Confidence scoring for matches
+- [ ] Manual review interface
+- [ ] Bulk import workflow
+- [ ] Format templates
+
+**Success Metrics**:
+- 90%+ accurate matching for clean input
+- Support 500+ songs per import
+- Clear review workflow for low-confidence matches
+
+---
+
+#### v3.0 - "Rhythm Roundsmith" *(Q3 2026 - HIGH PRIORITY)*
+**Status**: 🔴 Not Started
+**Priority**: High
+**Effort**: 3-4 weeks
+
+**Goals**: AI-powered quiz generation
+
+**Features**:
+- [ ] AI quiz round generation
+- [ ] Multiple quiz formats (MCQ, clips, open-ended)
+- [ ] Theme-based generation
+- [ ] Metadata-driven questions
+- [ ] User review/edit interface
+- [ ] Prompt optimization
+- [ ] AI provider abstraction (OpenAI, Anthropic, local)
+- [ ] Cost tracking
+
+**Success Metrics**:
+- Generate engaging rounds in <30s
+- 80%+ user satisfaction with AI questions
+- <$0.10 cost per round generation
+
+**Dependencies**:
+- OpenAI API or compatible
+- Prompt engineering
+
+---
+
+#### v3.1 - "Curated Collector" *(Q3 2026 - MEDIUM PRIORITY)*
+**Status**: 🔴 Not Started
+**Priority**: Medium
+**Effort**: 2-3 weeks
+
+**Goals**: Web scraping for playlists
+
+**Features**:
+- [ ] Spotify web scraper
+- [ ] HTML/JSON extraction
+- [ ] Rate limiting and rotation
+- [ ] User-agent rotation
+- [ ] Proxy support
+- [ ] Scraper detection avoidance
+- [ ] Compliance with ToS
+
+**Success Metrics**:
+- Successfully scrape 95%+ playlists
+- Avoid detection/blocking
+- Extract complete metadata
+
+**Legal Note**: ⚠️ Scraping must comply with platform ToS
+
+---
+
+#### v3.2 - "Scraper Symphony" *(Q3 2026 - LOW PRIORITY)*
+**Status**: 🔴 Not Started
+**Priority**: Low
+**Effort**: 3 weeks
+
+**Goals**: External music chart data
+
+**Features**:
+- [ ] Billboard chart scraper
+- [ ] Official Charts scraper
+- [ ] 2-3 additional sources
+- [ ] Data normalization
+- [ ] Spotify record linking
+- [ ] Admin review interface
+- [ ] Scheduled scraper runs
+- [ ] Error logging
+
+**Success Metrics**:
+- Weekly chart updates
+- 95%+ successful Spotify matching
+- Comprehensive historical data
+
+---
+
+#### v3.3 - "Alert Amplifier" *(Q3 2026 - MEDIUM PRIORITY)*
+**Status**: 🔴 Not Started
+**Priority**: Medium
+**Effort**: 1-2 weeks
+
+**Goals**: Comprehensive notification system
+
+**Features**:
+- [ ] Email verification
+- [ ] Round completion notifications
+- [ ] OAuth token expiration warnings
+- [ ] Admin usage summaries
+- [ ] Push notifications (browser/Telegram)
+- [ ] Notification preferences
+- [ ] Digest emails
+
+**Success Metrics**:
+- <5s notification delivery
+- 90%+ email deliverability
+- User-controlled notification settings
+
+---
+
+#### v4.0 - "Storage Sanctuary" *(Q4 2026 - HIGH PRIORITY)*
+**Status**: 🔴 Not Started
+**Priority**: High
+**Effort**: 3-4 weeks
+
+**Goals**: Cloud storage integration
+
+**Features**:
+- [ ] Storage backend abstraction
+- [ ] AWS S3 support
+- [ ] S3-compatible storage (MinIO, Wasabi)
+- [ ] Dropbox storage backend
+- [ ] Unified management UI
+- [ ] Cloud backup storage
+- [ ] MP3 cloud storage
+- [ ] Differential uploads
+- [ ] Background synchronization
+
+**Success Metrics**:
+- Support 100GB+ storage
+- <$10/month storage costs
+- Automatic failover between providers
+
+**Dependencies**:
+- boto3 (AWS SDK)
+- Storage abstraction layer
+
+---
+
+#### v4.1 - "Collaboration Core" *(Q4 2026 - MEDIUM PRIORITY)*
+**Status**: 🔴 Not Started
+**Priority**: Medium
+**Effort**: 3 weeks
+
+**Goals**: Multi-user collaboration
+
+**Features**:
+- [ ] Shared round editing
+- [ ] Collaboration roles (view/comment/edit)
+- [ ] User invitations via email/username
+- [ ] Presence indicators
+- [ ] Revision history
+- [ ] Public sharing links
+- [ ] Access audit logs
+- [ ] Comment threads
+
+**Success Metrics**:
+- Real-time collaboration (<2s sync)
+- Support 10+ simultaneous editors
+- Full audit trail for compliance
+
+---
+
+#### v4.2 - "Profile Personalizer" *(Q4 2026 - LOW PRIORITY)*
+**Status**: 🟡 Partially Complete
+**Priority**: Low
+**Effort**: 1 week
+
+**Features**:
+- [x] Custom user MP3 fallbacks
+- [x] Persistent user settings
+- [ ] Default round format preferences
+- [ ] Personal tag system
+- [ ] Tag filtering and sorting
+- [ ] Dark mode toggle
+- [ ] UI customization
+
+---
+
+#### v4.3 - "Deployment Dynamo" *(Q4 2026 - HIGH PRIORITY)*
+**Status**: 🔴 Not Started
+**Priority**: High
+**Effort**: 2 weeks
+
+**Goals**: CI/CD and automation
+
+**Features**:
+- [ ] GitHub Actions CI/CD pipeline
+- [ ] Automated testing
+- [ ] Automated builds
+- [ ] Nightly backup jobs
+- [ ] Sentry error tracking
+- [ ] Auto-updater script
+- [ ] Health check endpoint (/healthz)
+- [ ] Uptime monitoring integration
+
+**Success Metrics**:
+- <10min build and deploy time
+- Automated security scanning
+- Zero-downtime deployments
+
+---
+
+## 🔮 Future Considerations (2027+)
+
+### Ideas Under Evaluation
+
+#### "Blind Test" Mode
+- Hide title/artist metadata during play
+- Reveal answers on command
+- Scoring system
+
+#### Team Scoreboard
+- Live projection mode
+- Real-time score tracking
+- Leaderboard display
+
+#### Public Round Library
+- Community-shared rounds
+- Clone and customize
+- Rating and reviews
+- Trending rounds
+
+#### REST API
+- Third-party integrations
+- Trivia bot support
+- Mobile app backend
+- Webhook support
+
+#### Audio Fingerprinting
+- Validate user uploads
+- Detect duplicates
+- Copyright compliance
+
+#### Round Analytics
+- Usage frequency
+- Popularity metrics
+- User ratings
+- A/B testing
+
+#### Video Tutorials
+- Complex workflow guides
+- YouTube integration
+- Interactive help
+
+#### Keyboard Shortcuts
+- Power user features
+- Accessibility improvements
+- Documentation
+
+---
+
+## 📊 Metrics & KPIs
+
+### User Metrics
+- Monthly Active Users (MAU)
+- Round Creation Rate
+- Export Success Rate
+- User Retention (30/60/90 day)
+
+### Performance Metrics
+- Page Load Time (p50, p95, p99)
+- API Response Time
+- Error Rate (<0.1% target)
+- Uptime (99.9% target)
+
+### Technical Metrics
+- Test Coverage (>80% target)
+- Code Quality Score
+- Security Vulnerabilities (0 high/critical)
+- Dependency Freshness
+
+---
+
+## 🎯 Success Criteria
+
+### By Q2 2026
+- [ ] 100+ active users
+- [ ] 99.5% uptime
+- [ ] <1s median page load
+- [ ] Support 10,000+ songs per user
+- [ ] All critical security issues resolved
+
+### By Q4 2026
+- [ ] 500+ active users
+- [ ] 99.9% uptime
+- [ ] AI-generated rounds feature
+- [ ] Cloud storage integration
+- [ ] Collaboration features
+- [ ] Full CI/CD pipeline
+
+---
+
+## 📞 Feedback & Contributions
+
+We welcome community feedback on this roadmap!
+
+- **GitHub Discussions**: Share ideas and vote on features
+- **GitHub Issues**: Report bugs and request features
+- **Email**: christian@kaufdeinquiz.com
+
+See [CONTRIBUTING.md](CONTRIBUTING.md) for contribution guidelines.
+
+---
+
+## 📝 Changelog
+
+| Date | Change |
+|------|--------|
+| 2026-02 | Added v1.9 Security Hardening release |
+| 2026-02 | Initial comprehensive roadmap created |
+| 2025-12 | v1.8 Documentation Dynamo completed |
+| 2025-10 | v1.7 Dropbox Dispatch completed |
+
+---
+
+*This roadmap is subject to change based on user feedback, technical constraints, and strategic priorities.*
diff --git a/SECURITY.md b/SECURITY.md
new file mode 100644
index 0000000..d45bd5a
--- /dev/null
+++ b/SECURITY.md
@@ -0,0 +1,323 @@
+# Security Policy
+
+## Supported Versions
+
+We take security seriously and actively maintain the latest version of Quizzical Beats.
+
+| Version | Supported |
+| ------- | ------------------ |
+| Latest | :white_check_mark: |
+| < Latest| :x: |
+
+## Reporting a Vulnerability
+
+If you discover a security vulnerability in Quizzical Beats, please report it responsibly:
+
+1. **DO NOT** open a public GitHub issue
+2. Email security details to: christian@kaufdeinquiz.com
+3. Include:
+ - Description of the vulnerability
+ - Steps to reproduce
+ - Potential impact
+ - Suggested fix (if available)
+
+We will respond within 48 hours and work with you to understand and address the issue.
+
+## Security Best Practices
+
+### Deployment Security
+
+#### 1. Environment Variables
+
+**CRITICAL**: Never use default values in production!
+
+```bash
+# Generate secure secrets
+python -c 'import secrets; print(secrets.token_hex(32))' # For SECRET_KEY
+python -c 'import secrets; print(secrets.token_urlsafe(32))' # For AUTOMATION_TOKEN
+```
+
+**Required secure variables:**
+- `SECRET_KEY`: Flask session encryption (32+ bytes hex)
+- `AUTOMATION_TOKEN`: API authentication (32+ bytes URL-safe)
+
+#### 2. HTTPS Configuration
+
+**REQUIRED for production:**
+
+```env
+USE_HTTPS=True
+PREFERRED_URL_SCHEME=https
+DEBUG=False
+```
+
+Use a reverse proxy (nginx, Traefik, Caddy) for SSL termination.
+
+#### 3. Database Security
+
+- Use PostgreSQL or MySQL in production (not SQLite)
+- Enable database encryption at rest
+- Use strong database passwords
+- Restrict database network access
+- Regular backups with encryption
+
+#### 4. OAuth Configuration
+
+**Redirect URI Security:**
+- Use HTTPS redirect URIs in production
+- Never use wildcards in redirect URIs
+- Validate all OAuth state parameters
+
+**Token Storage:**
+- OAuth tokens are encrypted in the database
+- Use secure session storage (Redis recommended)
+- Set appropriate token expiration times
+
+#### 5. API Keys Protection
+
+**Storage:**
+- Store API keys in `.env` file only
+- Never commit `.env` to version control
+- Use secret management services (e.g., AWS Secrets Manager, HashiCorp Vault)
+
+**Rotation:**
+- Rotate Spotify/Deezer API credentials regularly
+- Update OAuth client secrets periodically
+- Monitor API key usage for anomalies
+
+### Application Security
+
+#### 1. Authentication
+
+- Use strong passwords (12+ characters, mixed case, numbers, symbols)
+- Enable multi-factor authentication via OAuth providers
+- Implement account lockout after failed login attempts
+- Use secure password hashing (werkzeug PBKDF2-SHA256)
+
+#### 2. Session Management
+
+- Sessions expire after inactivity
+- Use secure, httponly cookies
+- CSRF protection enabled (Flask-WTF)
+- Session data encrypted with SECRET_KEY
+
+#### 3. Input Validation
+
+- All user inputs are validated
+- SQL injection protected via SQLAlchemy ORM
+- XSS protection via template auto-escaping
+- File upload validation (type, size limits)
+
+#### 4. Rate Limiting
+
+**Recommendations:**
+```python
+# Add to production deployment
+- Login endpoints: 5 attempts per minute
+- API endpoints: 100 requests per minute
+- File uploads: 10 per hour
+```
+
+#### 5. Dependency Management
+
+**Current Known Issues:**
+- ~~authlib < 1.6.5~~ (FIXED: upgraded to 1.6.5+)
+
+**Maintenance:**
+```bash
+# Check for vulnerabilities
+pip install safety
+safety check
+
+# Update dependencies
+pip list --outdated
+pip install --upgrade
+```
+
+### Infrastructure Security
+
+#### 1. Docker Security
+
+**Best practices:**
+```dockerfile
+# Use non-root user
+USER musicround
+
+# Minimize attack surface
+FROM python:3.11-slim
+
+# Security updates
+RUN apt-get update && apt-get upgrade -y
+```
+
+#### 2. Network Security
+
+- Use firewall rules (only expose ports 80, 443)
+- Implement DDoS protection (Cloudflare, AWS Shield)
+- Use VPN for administrative access
+- Enable audit logging
+
+#### 3. File System Security
+
+```bash
+# Secure file permissions
+chmod 600 .env
+chmod 700 data/
+chmod 755 musicround/
+
+# Restrict write access
+chown -R musicround:musicround /app
+```
+
+#### 4. Backup Security
+
+- Encrypt backups at rest and in transit
+- Store backups in separate location/region
+- Test backup restoration regularly
+- Implement retention policies (30-90 days)
+
+### Monitoring and Logging
+
+#### 1. Security Logging
+
+**Log these events:**
+- Failed login attempts
+- Password changes
+- OAuth token creation/refresh
+- API key usage
+- Admin actions
+- File uploads/downloads
+
+#### 2. Alerting
+
+**Configure alerts for:**
+- Multiple failed logins
+- Unusual API traffic patterns
+- Database errors
+- Backup failures
+- Certificate expiration
+
+#### 3. Audit Trail
+
+```python
+# Enable comprehensive logging
+import logging
+logging.basicConfig(
+ level=logging.INFO,
+ format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
+)
+```
+
+## Security Checklist for Production
+
+- [ ] Generated secure SECRET_KEY (32+ bytes)
+- [ ] Generated secure AUTOMATION_TOKEN (32+ bytes)
+- [ ] Set DEBUG=False
+- [ ] Enabled HTTPS (USE_HTTPS=True)
+- [ ] Using production database (PostgreSQL/MySQL)
+- [ ] Database credentials are strong and unique
+- [ ] All OAuth redirect URIs use HTTPS
+- [ ] API keys rotated from defaults
+- [ ] Reverse proxy configured (nginx/Traefik)
+- [ ] Firewall rules enabled
+- [ ] SSL certificate valid and auto-renewing
+- [ ] Automated backups configured
+- [ ] Backup encryption enabled
+- [ ] Security monitoring enabled
+- [ ] Logs reviewed regularly
+- [ ] Dependencies up to date
+- [ ] File permissions restricted
+- [ ] Running as non-root user
+- [ ] Rate limiting implemented
+- [ ] CSRF protection enabled
+
+## Security Updates
+
+We recommend:
+1. Subscribe to security advisories for Python, Flask, and dependencies
+2. Review [GitHub Security Advisories](https://github.com/christianlouis/QuizzicalBeats/security/advisories)
+3. Monitor the [CHANGELOG](https://quizzicalbeats.readthedocs.io/changelog.html) for security updates
+4. Join our security mailing list (coming soon)
+
+## Compliance
+
+### Data Protection
+
+- User data stored securely with encryption
+- OAuth tokens encrypted at rest
+- Personal information minimization
+- Data retention policies implemented
+
+### GDPR Considerations
+
+- User data export available
+- Account deletion supported
+- Privacy policy available
+- Cookie consent implemented
+
+## Security Tools
+
+### Recommended Tools
+
+```bash
+# Static analysis
+pip install bandit
+bandit -r musicround/
+
+# Dependency scanning
+pip install safety
+safety check
+
+# Secret detection
+git-secrets --scan
+
+# Container scanning
+docker scan quizzicalbeats:latest
+```
+
+### CI/CD Security
+
+**GitHub Actions recommended checks:**
+- Dependency vulnerability scanning
+- Static code analysis (CodeQL)
+- Secret scanning
+- Container image scanning
+- License compliance
+
+## Known Security Considerations
+
+### Current Limitations
+
+1. **SQLite in Development**: Not suitable for concurrent production use
+2. **File System Storage**: MP3 files stored locally (consider S3 for scale)
+3. **Session Storage**: In-memory sessions don't scale (use Redis)
+4. **Rate Limiting**: Not implemented (add nginx/Cloudflare)
+
+### Future Improvements
+
+- [ ] Add two-factor authentication (TOTP)
+- [ ] Implement rate limiting middleware
+- [ ] Add security headers middleware
+- [ ] Content Security Policy (CSP)
+- [ ] Subresource Integrity (SRI)
+- [ ] Add honeypot fields to forms
+- [ ] Implement IP reputation checking
+- [ ] Add user session management dashboard
+
+## Resources
+
+- [OWASP Top 10](https://owasp.org/www-project-top-ten/)
+- [Flask Security Guide](https://flask.palletsprojects.com/en/latest/security/)
+- [Python Security Best Practices](https://python.readthedocs.io/en/latest/library/security_warnings.html)
+- [Docker Security Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Docker_Security_Cheat_Sheet.html)
+
+## Contact
+
+For security concerns, contact:
+- Email: christian@kaufdeinquiz.com
+- Maintainer: Christian Krakau-Louis
+- Response Time: Within 48 hours
+
+---
+
+*Last updated: February 2026*
diff --git a/musicround/config.py b/musicround/config.py
index 52113e2..4ff612e 100644
--- a/musicround/config.py
+++ b/musicround/config.py
@@ -17,7 +17,9 @@ class Config:
# Debug settings
DEBUG = os.getenv("DEBUG", "True") == "True"
DEBUG2 = os.getenv("DEBUG2", "False") == "True"
- SECRET_KEY = os.getenv('SECRET_KEY', 'dev-key-please-change')
+ SECRET_KEY = os.getenv('SECRET_KEY')
+ if not SECRET_KEY:
+ raise ValueError("SECRET_KEY environment variable must be set. Generate a secure key with: python -c 'import secrets; print(secrets.token_hex(32))'")
# API Keys
OPENAI_API_KEY = os.getenv("OPENAI_API_KEY")
@@ -69,7 +71,9 @@ class Config:
MAIL_RECIPIENT = os.getenv("MAIL_RECIPIENT", "admin@example.com")
# Automation settings
- AUTOMATION_TOKEN = os.getenv("AUTOMATION_TOKEN", "change-this-token-in-production") # Reverse proxy settings
+ AUTOMATION_TOKEN = os.getenv("AUTOMATION_TOKEN")
+ if not AUTOMATION_TOKEN:
+ raise ValueError("AUTOMATION_TOKEN environment variable must be set. Generate a secure token with: python -c 'import secrets; print(secrets.token_urlsafe(32))'") # Reverse proxy settings
USE_HTTPS = os.getenv("USE_HTTPS", "False") == "True" # Force HTTPS URL generation
PREFERRED_URL_SCHEME = os.getenv("PREFERRED_URL_SCHEME", 'https' if USE_HTTPS else 'http')
diff --git a/requirements.txt b/requirements.txt
index 8056f6b..176b92f 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -15,6 +15,11 @@ flask-admin
Flask-Login
Flask-Mail
Flask-Session
-authlib
+authlib>=1.6.5
Flask-Caching
-psutil
\ No newline at end of file
+psutil
+
+# Testing dependencies
+pytest>=7.4.0
+pytest-cov>=4.1.0
+pytest-flask>=1.2.0
\ No newline at end of file
diff --git a/tests/conftest.py b/tests/conftest.py
new file mode 100644
index 0000000..c12831e
--- /dev/null
+++ b/tests/conftest.py
@@ -0,0 +1,91 @@
+"""Pytest configuration and fixtures for Quizzical Beats tests."""
+import os
+import sys
+import pytest
+from unittest.mock import MagicMock
+
+# Add project root to path
+sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
+
+
+@pytest.fixture
+def app():
+ """Create a test Flask application instance."""
+ from musicround import create_app, db
+
+ # Create app in testing mode
+ test_config = {
+ 'TESTING': True,
+ 'SQLALCHEMY_DATABASE_URI': 'sqlite:///:memory:',
+ 'SQLALCHEMY_TRACK_MODIFICATIONS': False,
+ 'SECRET_KEY': 'test-secret-key-for-testing-only',
+ 'AUTOMATION_TOKEN': 'test-automation-token-for-testing',
+ 'WTF_CSRF_ENABLED': False, # Disable CSRF for testing
+ }
+
+ app = create_app()
+ app.config.update(test_config)
+
+ with app.app_context():
+ db.create_all()
+ yield app
+ db.session.remove()
+ db.drop_all()
+
+
+@pytest.fixture
+def client(app):
+ """Create a test client for the app."""
+ return app.test_client()
+
+
+@pytest.fixture
+def runner(app):
+ """Create a test CLI runner."""
+ return app.test_cli_runner()
+
+
+@pytest.fixture
+def mock_app():
+ """Create a mock Flask app for unit testing."""
+ app = MagicMock()
+ app.logger = MagicMock()
+ app.config = {
+ 'SECRET_KEY': 'test-key',
+ 'AUTOMATION_TOKEN': 'test-token',
+ }
+ return app
+
+
+@pytest.fixture
+def mock_spotify_client():
+ """Create a mock Spotify client."""
+ client = MagicMock()
+ client.search.return_value = {
+ 'tracks': {
+ 'items': []
+ }
+ }
+ return client
+
+
+@pytest.fixture
+def sample_user_data():
+ """Sample user data for testing."""
+ return {
+ 'username': 'testuser',
+ 'email': 'test@example.com',
+ 'password': 'SecurePassword123!',
+ }
+
+
+@pytest.fixture
+def sample_song_data():
+ """Sample song data for testing."""
+ return {
+ 'title': 'Highway to Hell',
+ 'artist_name': 'AC/DC',
+ 'year': '1979',
+ 'genre': 'Hard Rock',
+ 'isrc': 'AUAP07900028',
+ }
diff --git a/tests/test_security.py b/tests/test_security.py
new file mode 100644
index 0000000..642b181
--- /dev/null
+++ b/tests/test_security.py
@@ -0,0 +1,240 @@
+"""Security tests for Quizzical Beats."""
+import pytest
+import os
+import re
+
+
+class TestSecurityConfiguration:
+ """Test security configuration and settings."""
+
+ def test_secret_key_required(self):
+ """Test that SECRET_KEY must be set."""
+ # Remove SECRET_KEY from environment
+ secret_key = os.environ.get('SECRET_KEY')
+ if secret_key:
+ del os.environ['SECRET_KEY']
+
+ # Import should fail if SECRET_KEY not set
+ with pytest.raises(ValueError, match="SECRET_KEY environment variable must be set"):
+ from musicround.config import Config
+ _ = Config.SECRET_KEY
+
+ # Restore environment
+ if secret_key:
+ os.environ['SECRET_KEY'] = secret_key
+
+ def test_automation_token_required(self):
+ """Test that AUTOMATION_TOKEN must be set."""
+ # Remove AUTOMATION_TOKEN from environment
+ token = os.environ.get('AUTOMATION_TOKEN')
+ if token:
+ del os.environ['AUTOMATION_TOKEN']
+
+ # Import should fail if AUTOMATION_TOKEN not set
+ with pytest.raises(ValueError, match="AUTOMATION_TOKEN environment variable must be set"):
+ from musicround.config import Config
+ _ = Config.AUTOMATION_TOKEN
+
+ # Restore environment
+ if token:
+ os.environ['AUTOMATION_TOKEN'] = token
+
+ def test_no_credentials_in_code(self):
+ """Test that no credentials are hardcoded in Python files."""
+ project_root = os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
+ musicround_dir = os.path.join(project_root, 'musicround')
+
+ # Patterns that indicate potential credentials
+ dangerous_patterns = [
+ r'password\s*=\s*["\'][^"\']{8,}["\']', # password = "something"
+ r'token\s*=\s*["\'][A-Za-z0-9]{20,}["\']', # token = "longstring"
+ r'api[_-]?key\s*=\s*["\'][A-Za-z0-9]{20,}["\']', # api_key = "key"
+ r'secret\s*=\s*["\'][A-Za-z0-9]{20,}["\']', # secret = "value"
+ ]
+
+ issues = []
+
+ for root, dirs, files in os.walk(musicround_dir):
+ # Skip __pycache__ and test files
+ dirs[:] = [d for d in dirs if d != '__pycache__']
+
+ for file in files:
+ if not file.endswith('.py'):
+ continue
+
+ filepath = os.path.join(root, file)
+ with open(filepath, 'r', encoding='utf-8', errors='ignore') as f:
+ content = f.read()
+
+ for pattern in dangerous_patterns:
+ matches = re.finditer(pattern, content, re.IGNORECASE)
+ for match in matches:
+ # Exclude common test/example patterns
+ matched_text = match.group(0)
+ if any(safe in matched_text.lower() for safe in [
+ 'test', 'example', 'placeholder', 'changeme',
+ 'your-', 'secret-key-here', 'getenvi'
+ ]):
+ continue
+
+ issues.append({
+ 'file': filepath,
+ 'line': content[:match.start()].count('\n') + 1,
+ 'match': matched_text
+ })
+
+ if issues:
+ message = "Found potential hardcoded credentials:\n"
+ for issue in issues[:5]: # Show first 5
+ message += f" {issue['file']}:{issue['line']}: {issue['match']}\n"
+ pytest.fail(message)
+
+ def test_env_example_exists(self):
+ """Test that .env.example file exists."""
+ project_root = os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
+ env_example = os.path.join(project_root, '.env.example')
+ assert os.path.exists(env_example), ".env.example file should exist"
+
+ def test_security_md_exists(self):
+ """Test that SECURITY.md file exists."""
+ project_root = os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
+ security_md = os.path.join(project_root, 'SECURITY.md')
+ assert os.path.exists(security_md), "SECURITY.md file should exist"
+
+
+class TestDependencySecurity:
+ """Test dependency security."""
+
+ def test_authlib_version(self):
+ """Test that authlib is at least version 1.6.5."""
+ requirements_path = os.path.join(
+ os.path.dirname(__file__), '..', 'requirements.txt'
+ )
+
+ with open(requirements_path, 'r') as f:
+ content = f.read()
+
+ # Check for authlib with version constraint
+ assert 'authlib>=1.6.5' in content, \
+ "authlib should be pinned to >= 1.6.5 to fix known vulnerabilities"
+
+
+class TestInputValidation:
+ """Test that user input is properly validated."""
+
+ def test_sql_injection_prevention(self):
+ """Test that SQLAlchemy ORM is used (not raw SQL)."""
+ project_root = os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
+ musicround_dir = os.path.join(project_root, 'musicround')
+
+ # Look for dangerous SQL patterns
+ dangerous_patterns = [
+ r'\.execute\(["\'].*%s.*["\']', # .execute("... %s ...")
+ r'\.execute\(["\'].*\+.*["\']', # .execute("... " + var)
+ r'\.execute\(f["\']', # .execute(f"...")
+ ]
+
+ issues = []
+
+ for root, dirs, files in os.walk(musicround_dir):
+ dirs[:] = [d for d in dirs if d != '__pycache__']
+
+ for file in files:
+ if not file.endswith('.py'):
+ continue
+
+ filepath = os.path.join(root, file)
+ with open(filepath, 'r', encoding='utf-8', errors='ignore') as f:
+ content = f.read()
+
+ for pattern in dangerous_patterns:
+ matches = re.finditer(pattern, content)
+ for match in matches:
+ issues.append({
+ 'file': filepath,
+ 'line': content[:match.start()].count('\n') + 1,
+ 'match': match.group(0)
+ })
+
+ # migrations are allowed to use raw SQL
+ issues = [i for i in issues if '/migrations/' not in i['file']]
+
+ if issues:
+ message = "Found potential SQL injection risks:\n"
+ for issue in issues[:5]:
+ message += f" {issue['file']}:{issue['line']}: {issue['match']}\n"
+ pytest.fail(message)
+
+
+class TestSecureDefaults:
+ """Test that secure defaults are in place."""
+
+ def test_debug_disabled_by_default(self):
+ """Test that DEBUG is disabled by default in config."""
+ # Check .env.example
+ env_example_path = os.path.join(os.path.dirname(__file__), '..', '.env.example')
+ with open(env_example_path, 'r') as f:
+ content = f.read()
+
+ # Should have DEBUG=False
+ assert 'DEBUG=False' in content, ".env.example should have DEBUG=False"
+
+ def test_https_recommended(self):
+ """Test that HTTPS is documented as recommended."""
+ security_md_path = os.path.join(os.path.dirname(__file__), '..', 'SECURITY.md')
+ with open(security_md_path, 'r') as f:
+ content = f.read()
+
+ assert 'HTTPS' in content or 'https' in content, \
+ "SECURITY.md should mention HTTPS"
+ assert 'USE_HTTPS' in content, \
+ "SECURITY.md should document USE_HTTPS setting"
+
+
+class TestSecretManagement:
+ """Test secret management practices."""
+
+ def test_gitignore_includes_env(self):
+ """Test that .env is in .gitignore."""
+ gitignore_path = os.path.join(os.path.dirname(__file__), '..', '.gitignore')
+ with open(gitignore_path, 'r') as f:
+ content = f.read()
+
+ assert '.env' in content, ".env should be in .gitignore"
+
+ def test_no_env_files_committed(self):
+ """Test that actual .env files are not in git."""
+ project_root = os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
+
+ # .env.example is OK, but .env should not exist in repo
+ # (it will exist locally but should be gitignored)
+ # We're checking if demo files have placeholder values
+
+ demo_files = ['.env.demo', '.env.oauth', '.env.oauth.production']
+ for demo_file in demo_files:
+ demo_path = os.path.join(project_root, demo_file)
+ if os.path.exists(demo_path):
+ with open(demo_path, 'r') as f:
+ content = f.read()
+
+ # Check that these don't contain real-looking credentials
+ # Real API keys are usually 32+ characters of alphanumeric
+ patterns = [
+ r'[A-Za-z0-9]{40,}', # Very long alphanumeric strings
+ ]
+
+ for pattern in patterns:
+ matches = re.findall(pattern, content)
+ for match in matches:
+ # Skip if it's obviously a placeholder
+ if any(x in match.lower() for x in [
+ 'your-', 'example', 'changeme', 'placeholder'
+ ]):
+ continue
+
+ # This might be a real credential
+ if len(match) > 40:
+ pytest.fail(
+ f"Found potential real credential in {demo_file}: "
+ f"{match[:20]}... (redacted)"
+ )