Compare commits

..

3 Commits

Author SHA1 Message Date
pacnpal
02c7cbd1cd Implement code changes to enhance functionality and improve performance 2025-08-23 18:42:09 -04:00
pacnpal
d504d41de2 feat: complete monorepo structure with frontend and shared resources
- Add complete backend/ directory with full Django application
- Add frontend/ directory with Vite + TypeScript setup ready for Next.js
- Add comprehensive shared/ directory with:
  - Complete documentation and memory-bank archives
  - Media files and avatars (letters, park/ride images)
  - Deployment scripts and automation tools
  - Shared types and utilities
- Add architecture/ directory with migration guides
- Configure pnpm workspace for monorepo development
- Update .gitignore to exclude .django_tailwind_cli/ build artifacts
- Preserve all historical documentation in shared/docs/memory-bank/
- Set up proper structure for full-stack development with shared resources
2025-08-23 18:40:07 -04:00
pacnpal
b0e0678590 feat: major project restructure - move Django to backend dir and fix critical imports
- Restructure project: moved Django backend to backend/ directory
- Add frontend/ directory for future Next.js application
- Add shared/ directory for common resources
- Fix critical Django import errors:
  - Add missing sys.path modification for apps directory
  - Fix undefined CATEGORY_CHOICES imports in rides module
  - Fix media migration undefined references
  - Remove unused imports and f-strings without placeholders
- Install missing django-environ dependency
- Django server now runs without ModuleNotFoundError
- Update .gitignore and README for new structure
- Add pnpm workspace configuration for monorepo setup
2025-08-23 18:37:55 -04:00
1048 changed files with 14469 additions and 63914 deletions

443
.gitignore vendored
View File

@@ -1,198 +1,8 @@
/.vscode
/dev.sh
/flake.nix
venv
/venv
./venv
venv/sour
.DS_Store
.DS_Store
.DS_Store
accounts/__pycache__/
__pycache__
thrillwiki/__pycache__
reviews/__pycache__
parks/__pycache__
media/__pycache__
email_service/__pycache__
core/__pycache__
companies/__pycache__
accounts/__pycache__
venv
accounts/__pycache__
thrillwiki/__pycache__/settings.cpython-311.pyc
accounts/migrations/__pycache__/__init__.cpython-311.pyc
accounts/migrations/__pycache__/0001_initial.cpython-311.pyc
companies/migrations/__pycache__
moderation/__pycache__
rides/__pycache__
ssh_tools.jsonc
thrillwiki/__pycache__/settings.cpython-312.pyc
parks/__pycache__/views.cpython-312.pyc
.venv/lib/python3.12/site-packages
thrillwiki/__pycache__/urls.cpython-312.pyc
thrillwiki/__pycache__/views.cpython-312.pyc
.pytest_cache.github
static/css/tailwind.css
static/css/tailwind.css
.venv
location/__pycache__
analytics/__pycache__
designers/__pycache__
history_tracking/__pycache__
media/migrations/__pycache__/0001_initial.cpython-312.pyc
accounts/__pycache__/__init__.cpython-312.pyc
accounts/__pycache__/adapters.cpython-312.pyc
accounts/__pycache__/admin.cpython-312.pyc
accounts/__pycache__/apps.cpython-312.pyc
accounts/__pycache__/models.cpython-312.pyc
accounts/__pycache__/signals.cpython-312.pyc
accounts/__pycache__/urls.cpython-312.pyc
accounts/__pycache__/views.cpython-312.pyc
accounts/migrations/__pycache__/__init__.cpython-312.pyc
accounts/migrations/__pycache__/0001_initial.cpython-312.pyc
companies/__pycache__/__init__.cpython-312.pyc
companies/__pycache__/admin.cpython-312.pyc
companies/__pycache__/apps.cpython-312.pyc
companies/__pycache__/models.cpython-312.pyc
companies/__pycache__/signals.cpython-312.pyc
companies/__pycache__/urls.cpython-312.pyc
companies/__pycache__/views.cpython-312.pyc
companies/migrations/__pycache__/__init__.cpython-312.pyc
companies/migrations/__pycache__/0001_initial.cpython-312.pyc
core/__pycache__/__init__.cpython-312.pyc
core/__pycache__/admin.cpython-312.pyc
core/__pycache__/apps.cpython-312.pyc
core/__pycache__/models.cpython-312.pyc
core/__pycache__/views.cpython-312.pyc
core/migrations/__pycache__/__init__.cpython-312.pyc
core/migrations/__pycache__/0001_initial.cpython-312.pyc
email_service/__pycache__/__init__.cpython-312.pyc
email_service/__pycache__/admin.cpython-312.pyc
email_service/__pycache__/apps.cpython-312.pyc
email_service/__pycache__/models.cpython-312.pyc
email_service/__pycache__/services.cpython-312.pyc
email_service/migrations/__pycache__/__init__.cpython-312.pyc
email_service/migrations/__pycache__/0001_initial.cpython-312.pyc
media/__pycache__/__init__.cpython-312.pyc
media/__pycache__/admin.cpython-312.pyc
media/__pycache__/apps.cpython-312.pyc
media/__pycache__/models.cpython-312.pyc
media/migrations/__pycache__/__init__.cpython-312.pyc
media/migrations/__pycache__/0001_initial.cpython-312.pyc
parks/__pycache__/__init__.cpython-312.pyc
parks/__pycache__/admin.cpython-312.pyc
parks/__pycache__/apps.cpython-312.pyc
parks/__pycache__/models.cpython-312.pyc
parks/__pycache__/signals.cpython-312.pyc
parks/__pycache__/urls.cpython-312.pyc
parks/__pycache__/views.cpython-312.pyc
parks/migrations/__pycache__/__init__.cpython-312.pyc
parks/migrations/__pycache__/0001_initial.cpython-312.pyc
reviews/__pycache__/__init__.cpython-312.pyc
reviews/__pycache__/admin.cpython-312.pyc
reviews/__pycache__/apps.cpython-312.pyc
reviews/__pycache__/models.cpython-312.pyc
reviews/__pycache__/signals.cpython-312.pyc
reviews/__pycache__/urls.cpython-312.pyc
reviews/__pycache__/views.cpython-312.pyc
reviews/migrations/__pycache__/__init__.cpython-312.pyc
reviews/migrations/__pycache__/0001_initial.cpython-312.pyc
rides/__pycache__/__init__.cpython-312.pyc
rides/__pycache__/admin.cpython-312.pyc
rides/__pycache__/apps.cpython-312.pyc
rides/__pycache__/models.cpython-312.pyc
rides/__pycache__/signals.cpython-312.pyc
rides/__pycache__/urls.cpython-312.pyc
rides/__pycache__/views.cpython-312.pyc
rides/migrations/__pycache__/__init__.cpython-312.pyc
rides/migrations/__pycache__/0001_initial.cpython-312.pyc
thrillwiki/__pycache__/__init__.cpython-312.pyc
thrillwiki/__pycache__/settings.cpython-312.pyc
thrillwiki/__pycache__/urls.cpython-312.pyc
thrillwiki/__pycache__/views.cpython-312.pyc
thrillwiki/__pycache__/wsgi.cpython-312.pyc
accounts/__pycache__/__init__.cpython-312.pyc
accounts/__pycache__/adapters.cpython-312.pyc
accounts/__pycache__/admin.cpython-312.pyc
accounts/__pycache__/apps.cpython-312.pyc
accounts/__pycache__/models.cpython-312.pyc
accounts/__pycache__/signals.cpython-312.pyc
accounts/__pycache__/urls.cpython-312.pyc
accounts/__pycache__/views.cpython-312.pyc
accounts/migrations/__pycache__/__init__.cpython-312.pyc
accounts/migrations/__pycache__/0001_initial.cpython-312.pyc
companies/__pycache__/__init__.cpython-312.pyc
companies/__pycache__/admin.cpython-312.pyc
companies/__pycache__/apps.cpython-312.pyc
companies/__pycache__/models.cpython-312.pyc
companies/__pycache__/signals.cpython-312.pyc
companies/__pycache__/urls.cpython-312.pyc
companies/__pycache__/views.cpython-312.pyc
companies/migrations/__pycache__/__init__.cpython-312.pyc
companies/migrations/__pycache__/0001_initial.cpython-312.pyc
core/__pycache__/__init__.cpython-312.pyc
core/__pycache__/admin.cpython-312.pyc
core/__pycache__/apps.cpython-312.pyc
core/__pycache__/models.cpython-312.pyc
core/__pycache__/views.cpython-312.pyc
core/migrations/__pycache__/__init__.cpython-312.pyc
core/migrations/__pycache__/0001_initial.cpython-312.pyc
email_service/__pycache__/__init__.cpython-312.pyc
email_service/__pycache__/admin.cpython-312.pyc
email_service/__pycache__/apps.cpython-312.pyc
email_service/__pycache__/models.cpython-312.pyc
email_service/__pycache__/services.cpython-312.pyc
email_service/migrations/__pycache__/__init__.cpython-312.pyc
email_service/migrations/__pycache__/0001_initial.cpython-312.pyc
media/__pycache__/__init__.cpython-312.pyc
media/__pycache__/admin.cpython-312.pyc
media/__pycache__/apps.cpython-312.pyc
media/__pycache__/models.cpython-312.pyc
media/migrations/__pycache__/__init__.cpython-312.pyc
media/migrations/__pycache__/0001_initial.cpython-312.pyc
parks/__pycache__/__init__.cpython-312.pyc
parks/__pycache__/admin.cpython-312.pyc
parks/__pycache__/apps.cpython-312.pyc
parks/__pycache__/models.cpython-312.pyc
parks/__pycache__/signals.cpython-312.pyc
parks/__pycache__/urls.cpython-312.pyc
parks/__pycache__/views.cpython-312.pyc
parks/migrations/__pycache__/__init__.cpython-312.pyc
parks/migrations/__pycache__/0001_initial.cpython-312.pyc
reviews/__pycache__/__init__.cpython-312.pyc
reviews/__pycache__/admin.cpython-312.pyc
reviews/__pycache__/apps.cpython-312.pyc
reviews/__pycache__/models.cpython-312.pyc
reviews/__pycache__/signals.cpython-312.pyc
reviews/__pycache__/urls.cpython-312.pyc
reviews/__pycache__/views.cpython-312.pyc
reviews/migrations/__pycache__/__init__.cpython-312.pyc
reviews/migrations/__pycache__/0001_initial.cpython-312.pyc
rides/__pycache__/__init__.cpython-312.pyc
rides/__pycache__/admin.cpython-312.pyc
rides/__pycache__/apps.cpython-312.pyc
rides/__pycache__/models.cpython-312.pyc
rides/__pycache__/signals.cpython-312.pyc
rides/__pycache__/urls.cpython-312.pyc
rides/__pycache__/views.cpython-312.pyc
rides/migrations/__pycache__/__init__.cpython-312.pyc
rides/migrations/__pycache__/0001_initial.cpython-312.pyc
thrillwiki/__pycache__/__init__.cpython-312.pyc
thrillwiki/__pycache__/settings.cpython-312.pyc
thrillwiki/__pycache__/urls.cpython-312.pyc
thrillwiki/__pycache__/views.cpython-312.pyc
thrillwiki/__pycache__/wsgi.cpython-312.pyc
# Byte-compiled / optimized / DLL files
# Python
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
build/
develop-eggs/
@@ -212,189 +22,96 @@ share/python-wheels/
*.egg
MANIFEST
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/
cover/
# Translations
*.mo
*.pot
# Django stuff:
# Django
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal
/backend/staticfiles/
/backend/media/
# Flask stuff:
instance/
.webassets-cache
# UV
.uv/
backend/.uv/
# Scrapy stuff:
.scrapy
# Node.js
node_modules/
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
.pnpm-store/
# Sphinx documentation
docs/_build/
# Vue.js / Vite
/frontend/dist/
/frontend/dist-ssr/
*.local
# PyBuilder
.pybuilder/
target/
# Environment variables
.env
.env.local
.env.development.local
.env.test.local
.env.production.local
backend/.env
frontend/.env
# Jupyter Notebook
.ipynb_checkpoints
# IDEs
.vscode/
.idea/
*.swp
*.swo
*.sublime-project
*.sublime-workspace
# IPython
profile_default/
ipython_config.py
# pyenv
# For a library or package, you might want to ignore these files since the code is
# intended to run in multiple environments; otherwise, check them in:
# .python-version
# pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
# However, in case of collaboration, if having platform-specific dependencies or dependencies
# having no cross-platform support, pipenv may install dependencies that don't work, or not
# install all needed dependencies.
#Pipfile.lock
# poetry
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
# This is especially recommended for binary packages to ensure reproducibility, and is more
# commonly ignored for libraries.
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
#poetry.lock
# pdm
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
#pdm.lock
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
# in version control.
# https://pdm.fming.dev/latest/usage/project/#working-with-version-control
.pdm.toml
.pdm-python
.pdm-build/
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
__pypackages__/
# Celery stuff
celerybeat-schedule
celerybeat.pid
# SageMath parsed files
*.sage.py
# Environments
***REMOVED***
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
.dmypy.json
dmypy.json
# Pyre type checker
.pyre/
# pytype static type analyzer
.pytype/
# Cython debug symbols
cython_debug/
# PyCharm
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
# be found at https://github.[AWS-SECRET-REMOVED]tBrains.gitignore
# and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/
# Pixi package manager
.pixi/
# Django Tailwind CLI
.django_tailwind_cli/
# General
# OS
.DS_Store
.AppleDouble
.LSOverride
Thumbs.db
Desktop.ini
# Icon must end with two \r
Icon
# Thumbnails
._*
# Files that might appear in the root of a volume
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns
.com.apple.timemachine.donotpresent
# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk
# ThrillWiki CI/CD Configuration
.thrillwiki-config
***REMOVED***.unraid
***REMOVED***.webhook
.github-token
# Logs
logs/
profiles
.thrillwiki-github-token
.thrillwiki-template-config
*.log
# Environment files with potential secrets
scripts/systemd/thrillwiki-automation***REMOVED***
scripts/systemd/thrillwiki-deployment***REMOVED***
scripts/systemd/****REMOVED***
logs/
profiles/
uv.lock
# Coverage
coverage/
*.lcov
.nyc_output
htmlcov/
.coverage
.coverage.*
# Testing
.pytest_cache/
.cache
# Temporary files
tmp/
temp/
*.tmp
*.temp
# Build outputs
/dist/
/build/
# Backup files
*.bak
*.orig
*.swp
# Archive files
*.tar.gz
*.zip
*.rar
# Security
*.pem
*.key
*.cert
# Local development
/uploads/
/backups/
.django_tailwind_cli/

439
README.md
View File

@@ -1,391 +1,150 @@
# ThrillWiki Development Environment Setup
# ThrillWiki Django + Vue.js Monorepo
ThrillWiki is a modern Django web application for theme park and roller coaster enthusiasts, featuring a sophisticated dark theme design with purple-to-blue gradients, HTMX interactivity, and comprehensive park/ride information management.
A modern monorepo architecture for ThrillWiki, combining a Django REST API backend with a Vue.js frontend.
## 🏗️ Technology Stack
## 🏗️ Architecture
- **Backend**: Django 5.0+ with GeoDjango (PostGIS)
- **Frontend**: HTMX + Alpine.js + Tailwind CSS
- **Database**: PostgreSQL with PostGIS extension
- **Package Management**: UV (Python package manager)
- **Authentication**: Django Allauth with Google/Discord OAuth
- **Styling**: Tailwind CSS with custom dark theme
- **History Tracking**: django-pghistory for audit trails
- **Testing**: Pytest + Playwright for E2E testing
This project uses a monorepo structure that cleanly separates backend and frontend concerns:
## 📋 Prerequisites
### Required Software
1. **Python 3.11+**
```bash
python --version # Should be 3.11 or higher
```
2. **UV Package Manager**
```bash
# Install UV if not already installed
curl -LsSf https://astral.sh/uv/install.sh | sh
# or
pip install uv
```
3. **PostgreSQL with PostGIS**
```bash
# macOS (Homebrew)
brew install postgresql postgis
# Ubuntu/Debian
sudo apt-get install postgresql postgresql-contrib postgis
# Start PostgreSQL service
brew services start postgresql # macOS
sudo systemctl start postgresql # Linux
```
4. **GDAL/GEOS Libraries** (for GeoDjango)
```bash
# macOS (Homebrew)
brew install gdal geos
# Ubuntu/Debian
sudo apt-get install gdal-bin libgdal-dev libgeos-dev
```
5. **Node.js** (for Tailwind CSS)
```bash
# Install Node.js 18+ for Tailwind CSS compilation
node --version # Should be 18 or higher
```
```
thrillwiki-monorepo/
├── backend/ # Django REST API
├── frontend/ # Vue.js SPA
└── shared/ # Shared resources and documentation
```
## 🚀 Quick Start
### 1. Clone and Setup Project
### Prerequisites
```bash
# Clone the repository
git clone <repository-url>
cd thrillwiki_django_no_react
- **Python 3.11+** with [uv](https://docs.astral.sh/uv/) for backend dependencies
- **Node.js 18+** with [pnpm](https://pnpm.io/) for frontend dependencies
# Install Python dependencies using UV
uv sync
```
### Development Setup
### 2. Database Setup
1. **Clone the repository**
```bash
git clone <repository-url>
cd thrillwiki-monorepo
```
```bash
# Create PostgreSQL database and user
createdb thrillwiki
createuser wiki
2. **Install dependencies**
```bash
# Install frontend dependencies
pnpm install
# Connect to PostgreSQL and setup
psql postgres
```
# Install backend dependencies
cd backend && uv sync
```
In the PostgreSQL shell:
```sql
-- Set password for wiki user
ALTER USER wiki WITH PASSWORD 'thrillwiki';
3. **Start development servers**
```bash
# Start both frontend and backend
pnpm run dev
-- Grant privileges
GRANT ALL PRIVILEGES ON DATABASE thrillwiki TO wiki;
# Or start individually
pnpm run dev:frontend # Vue.js on :3000
pnpm run dev:backend # Django on :8000
```
-- Enable PostGIS extension
\c thrillwiki
CREATE EXTENSION postgis;
\q
```
## 📁 Project Structure
### 3. Environment Configuration
### Backend (`/backend`)
- **Django REST API** with modular app architecture
- **UV package management** for Python dependencies
- **PostgreSQL** database (configurable)
- **Redis** for caching and sessions
The project uses these database settings (configured in [`thrillwiki/settings.py`](thrillwiki/settings.py)):
```python
DATABASES = {
"default": {
"ENGINE": "django.contrib.gis.db.backends.postgis",
"NAME": "thrillwiki",
"USER": "wiki",
"PASSWORD": "thrillwiki",
"HOST": "192.168.86.3", # Update to your PostgreSQL host
"PORT": "5432",
}
}
```
### Frontend (`/frontend`)
- **Vue 3** with Composition API
- **TypeScript** for type safety
- **Vite** for fast development and building
- **Tailwind CSS** for styling
- **Pinia** for state management
**Important**: Update the `HOST` setting in [`thrillwiki/settings.py`](thrillwiki/settings.py) to match your PostgreSQL server location:
- Use `"localhost"` or `"127.0.0.1"` for local development
- Current setting is `"192.168.86.3"` - update this to your PostgreSQL server IP
- For local development, change to `"localhost"` in settings.py
### 4. Database Migration
```bash
# Run database migrations
uv run manage.py migrate
# Create a superuser account
uv run manage.py createsuperuser
```
**Note**: If you're setting up for local development, first update the database HOST in [`thrillwiki/settings.py`](thrillwiki/settings.py) from `"192.168.86.3"` to `"localhost"` before running migrations.
### 5. Start Development Server
**CRITICAL**: Always use this exact command sequence for starting the development server:
```bash
lsof -ti :8000 | xargs kill -9; find . -type d -name "__pycache__" -exec rm -r {} +; uv run manage.py tailwind runserver
```
This command:
- Kills any existing processes on port 8000
- Cleans Python cache files
- Starts Tailwind CSS compilation
- Runs the Django development server
The application will be available at: http://localhost:8000
### Shared (`/shared`)
- Documentation and deployment guides
- Shared TypeScript types
- Build and deployment scripts
- Docker configurations
## 🛠️ Development Workflow
### Package Management
**ALWAYS use UV for package management**:
### Available Scripts
```bash
# Add new Python packages
uv add <package-name>
# Development
pnpm run dev # Start both servers
pnpm run dev:frontend # Frontend only
pnpm run dev:backend # Backend only
# Add development dependencies
uv add --dev <package-name>
# Building
pnpm run build # Build for production
pnpm run build:frontend # Frontend build only
# Never use pip install - always use UV
# Testing
pnpm run test # Run all tests
pnpm run test:frontend # Frontend tests
pnpm run test:backend # Backend tests
# Code Quality
pnpm run lint # Lint all code
pnpm run format # Format all code
```
### Django Management Commands
**ALWAYS use UV for Django commands**:
### Backend Commands
```bash
# Correct way to run Django commands
uv run manage.py <command>
cd backend
# Examples:
uv run manage.py makemigrations
# Django management
uv run manage.py migrate
uv run manage.py shell
uv run manage.py createsuperuser
uv run manage.py collectstatic
# NEVER use these patterns:
# python manage.py <command> ❌ Wrong
# uv run python manage.py <command> ❌ Wrong
# Testing
uv run manage.py test
```
### CSS Development
## 🔧 Configuration
The project uses **Tailwind CSS v4** with a custom dark theme. CSS files are located in:
- Source: [`static/css/src/input.css`](static/css/src/input.css)
- Compiled: [`static/css/`](static/css/) (auto-generated)
### Environment Variables
Tailwind automatically compiles when using the `tailwind runserver` command.
#### Tailwind CSS v4 Migration
This project has been migrated from Tailwind CSS v3 to v4. For complete migration details:
- **📖 Full Migration Documentation**: [`TAILWIND_V4_MIGRATION.md`](TAILWIND_V4_MIGRATION.md)
- **⚡ Quick Reference Guide**: [`TAILWIND_V4_QUICK_REFERENCE.md`](TAILWIND_V4_QUICK_REFERENCE.md)
**Key v4 Changes**:
- New CSS-first approach with `@theme` blocks
- Updated utility class names (e.g., `outline-none` → `outline-hidden`)
- New opacity syntax (e.g., `bg-blue-500/50` instead of `bg-blue-500 bg-opacity-50`)
- Enhanced performance and smaller bundle sizes
**Custom Theme Variables** (available in CSS):
```css
var(--color-primary) /* #4f46e5 - Indigo-600 */
var(--color-secondary) /* #e11d48 - Rose-600 */
var(--color-accent) /* #8b5cf6 - Violet-500 */
var(--font-family-sans) /* Poppins, sans-serif */
```
## 🏗️ Project Structure
```
thrillwiki_django_no_react/
├── accounts/ # User account management
├── analytics/ # Analytics and tracking
├── companies/ # Theme park companies
├── core/ # Core application logic
├── designers/ # Ride designers
├── history/ # History timeline features
├── location/ # Geographic location handling
├── media/ # Media file management
├── moderation/ # Content moderation
├── parks/ # Theme park management
├── reviews/ # User reviews
├── rides/ # Roller coaster/ride management
├── search/ # Search functionality
├── static/ # Static assets (CSS, JS, images)
├── templates/ # Django templates
├── thrillwiki/ # Main Django project settings
├── memory-bank/ # Development documentation
└── .clinerules # Project development rules
```
## 🔧 Key Features
### Authentication System
- Django Allauth integration
- Google OAuth authentication
- Discord OAuth authentication
- Custom user profiles with avatars
### Geographic Features
- PostGIS integration for location data
- Interactive park maps
- Location-based search and filtering
### Content Management
- Park and ride information management
- Photo galleries with upload capabilities
- User-generated reviews and ratings
- Content moderation system
### Modern Frontend
- HTMX for dynamic interactions
- Alpine.js for client-side behavior
- Tailwind CSS with custom dark theme
- Responsive design (mobile-first)
## 🧪 Testing
### Running Tests
Create `.env` files for local development:
```bash
# Run Python tests
uv run pytest
# Root .env (shared settings)
DATABASE_URL=postgresql://user:pass@localhost/thrillwiki
REDIS_URL=redis://localhost:6379
SECRET_KEY=your-secret-key
# Run with coverage
uv run coverage run -m pytest
uv run coverage report
# Backend .env
DJANGO_SETTINGS_MODULE=config.django.local
DEBUG=True
# Run E2E tests with Playwright
uv run pytest tests/e2e/
# Frontend .env
VITE_API_BASE_URL=http://localhost:8000/api
```
### Test Structure
- Unit tests: Located within each app's `tests/` directory
- E2E tests: [`tests/e2e/`](tests/e2e/)
- Test fixtures: [`tests/fixtures/`](tests/fixtures/)
## 📖 Documentation
## 📚 Documentation
- [Backend Documentation](./backend/README.md)
- [Frontend Documentation](./frontend/README.md)
- [Deployment Guide](./shared/docs/deployment/)
- [API Documentation](./shared/docs/api/)
### Memory Bank System
The project uses a comprehensive documentation system in [`memory-bank/`](memory-bank/):
## 🚀 Deployment
- [`memory-bank/activeContext.md`](memory-bank/activeContext.md) - Current development context
- [`memory-bank/documentation/design-system.md`](memory-bank/documentation/design-system.md) - Design system documentation
- [`memory-bank/features/`](memory-bank/features/) - Feature-specific documentation
- [`memory-bank/testing/`](memory-bank/testing/) - Testing documentation and results
See [Deployment Guide](./shared/docs/deployment/) for production setup instructions.
### Key Documentation Files
- [Design System](memory-bank/documentation/design-system.md) - UI/UX guidelines and patterns
- [Authentication System](memory-bank/features/auth/) - OAuth and user management
- [Layout Optimization](memory-bank/projects/) - Responsive design implementations
## 🤝 Contributing
## 🚨 Important Development Rules
1. Fork the repository
2. Create a feature branch
3. Make your changes
4. Run tests and linting
5. Submit a pull request
### Critical Commands
1. **Server Startup**: Always use the full command sequence:
```bash
lsof -ti :8000 | xargs kill -9; find . -type d -name "__pycache__" -exec rm -r {} +; uv run manage.py tailwind runserver
```
## 📄 License
2. **Package Management**: Only use UV:
```bash
uv add <package> # ✅ Correct
pip install <package> # ❌ Wrong
```
3. **Django Commands**: Always prefix with `uv run`:
```bash
uv run manage.py <command> # ✅ Correct
python manage.py <command> # ❌ Wrong
```
### Database Configuration
- Ensure PostgreSQL is running before starting development
- PostGIS extension must be enabled
- Update database host settings for your environment
### GeoDjango Requirements
- GDAL and GEOS libraries must be properly installed
- Library paths are configured in [`thrillwiki/settings.py`](thrillwiki/settings.py) for macOS Homebrew
- Current paths: `/opt/homebrew/lib/libgdal.dylib` and `/opt/homebrew/lib/libgeos_c.dylib`
- May need adjustment based on your system's library locations (Linux users will need different paths)
## 🔍 Troubleshooting
### Common Issues
1. **PostGIS Extension Error**
```bash
# Connect to database and enable PostGIS
psql thrillwiki
CREATE EXTENSION postgis;
```
2. **GDAL/GEOS Library Not Found**
```bash
# macOS (Homebrew): Current paths in settings.py
GDAL_LIBRARY_PATH = "/opt/homebrew/lib/libgdal.dylib"
GEOS_LIBRARY_PATH = "/opt/homebrew/lib/libgeos_c.dylib"
# Linux: Update paths in settings.py to something like:
# GDAL_LIBRARY_PATH = "/usr/lib/x86_64-linux-gnu/libgdal.so"
# GEOS_LIBRARY_PATH = "/usr/lib/x86_64-linux-gnu/libgeos_c.so"
# Find your library locations
find /usr -name "libgdal*" 2>/dev/null
find /usr -name "libgeos*" 2>/dev/null
find /opt -name "libgdal*" 2>/dev/null
find /opt -name "libgeos*" 2>/dev/null
```
3. **Port 8000 Already in Use**
```bash
# Kill existing processes
lsof -ti :8000 | xargs kill -9
```
4. **Tailwind CSS Not Compiling**
```bash
# Ensure Node.js is installed and use the full server command
node --version
uv run manage.py tailwind runserver
```
### Getting Help
1. Check the [`memory-bank/`](memory-bank/) documentation for detailed feature information
2. Review [`memory-bank/testing/`](memory-bank/testing/) for known issues and solutions
3. Ensure all prerequisites are properly installed
4. Verify database connection and PostGIS extension
## 🎯 Next Steps
After successful setup:
1. **Explore the Admin Interface**: http://localhost:8000/admin/
2. **Browse the Application**: http://localhost:8000/
3. **Review Documentation**: Check [`memory-bank/`](memory-bank/) for detailed feature docs
4. **Run Tests**: Ensure everything works with `uv run pytest`
5. **Start Development**: Follow the development workflow guidelines above
---
**Happy Coding!** 🎢✨
For detailed feature documentation and development context, see the [`memory-bank/`](memory-bank/) directory.
This project is licensed under the MIT License.

View File

@@ -0,0 +1,372 @@
# ThrillWiki Monorepo Architecture Validation
This document provides a comprehensive review and validation of the proposed monorepo architecture for migrating ThrillWiki from Django-only to Django + Vue.js.
## Architecture Overview Validation
### ✅ Core Requirements Met
1. **Clean Separation of Concerns**
- Backend: Django API, business logic, database management
- Frontend: Vue.js SPA with modern tooling
- Shared: Common resources and media files
2. **Development Workflow Preservation**
- UV package management for Python maintained
- pnpm for Node.js package management
- Existing development scripts adapted
- Hot reloading for both backend and frontend
3. **Project Structure Compatibility**
- Django apps preserved under `backend/apps/`
- Configuration maintained under `backend/config/`
- Static files strategy clearly defined
- Media files centralized in `shared/media/`
## Technical Architecture Validation
### Backend Architecture ✅
```mermaid
graph TB
A[Django Backend] --> B[Apps Directory]
A --> C[Config Directory]
A --> D[Static Files]
B --> E[accounts]
B --> F[parks]
B --> G[rides]
B --> H[moderation]
B --> I[location]
B --> J[media]
B --> K[email_service]
B --> L[core]
C --> M[Django Settings]
C --> N[URL Configuration]
C --> O[WSGI/ASGI]
D --> P[Admin Assets]
D --> Q[Backend Static]
```
**Validation Points:**
- ✅ All 8 Django apps properly mapped to new structure
- ✅ Configuration files maintain their organization
- ✅ Static file handling preserves Django admin functionality
- ✅ UV package management integration maintained
### Frontend Architecture ✅
```mermaid
graph TB
A[Vue.js Frontend] --> B[Source Code]
A --> C[Build System]
A --> D[Development Tools]
B --> E[Components]
B --> F[Views/Pages]
B --> G[Router]
B --> H[State Management]
B --> I[API Layer]
C --> J[Vite]
C --> K[TypeScript]
C --> L[Tailwind CSS]
D --> M[Hot Reload]
D --> N[Dev Server]
D --> O[Build Tools]
```
**Validation Points:**
- ✅ Modern Vue.js 3 + Composition API
- ✅ TypeScript for type safety
- ✅ Vite for fast development and builds
- ✅ Tailwind CSS for styling (matching current setup)
- ✅ Pinia for state management
- ✅ Vue Router for SPA navigation
### Integration Architecture ✅
```mermaid
graph LR
A[Vue.js Frontend] --> B[HTTP API Calls]
B --> C[Django REST API]
C --> D[Database]
C --> E[Media Files]
E --> F[Shared Media Directory]
F --> G[Frontend Access]
```
**Validation Points:**
- ✅ RESTful API integration between frontend and backend
- ✅ Media files accessible to both systems
- ✅ Authentication handling via API tokens
- ✅ CORS configuration for cross-origin requests
## File Migration Validation
### Critical File Mappings ✅
| Component | Current | New Location | Status |
|-----------|---------|--------------|--------|
| Django Apps | `/apps/` | `/backend/apps/` | ✅ Mapped |
| Configuration | `/config/` | `/backend/config/` | ✅ Mapped |
| Static Files | `/static/` | `/backend/static/` | ✅ Mapped |
| Media Files | `/media/` | `/shared/media/` | ✅ Mapped |
| Scripts | `/scripts/` | `/scripts/` | ✅ Preserved |
| Dependencies | `/pyproject.toml` | `/backend/pyproject.toml` | ✅ Mapped |
### Import Path Updates Required ✅
**Django Settings Updates:**
```python
# OLD
INSTALLED_APPS = [
'accounts',
'parks',
'rides',
# ...
]
# NEW
INSTALLED_APPS = [
'apps.accounts',
'apps.parks',
'apps.rides',
# ...
]
```
**Media Path Updates:**
```python
# NEW
MEDIA_ROOT = BASE_DIR.parent / 'shared' / 'media'
```
## Development Workflow Validation
### Package Management ✅
**Backend (UV):**
-`uv add <package>` for new dependencies
-`uv run manage.py <command>` for Django commands
-`uv sync` for dependency installation
**Frontend (pnpm):**
-`pnpm add <package>` for new dependencies
-`pnpm install` for dependency installation
-`pnpm run dev` for development server
**Root Workspace:**
-`pnpm run dev` starts both servers concurrently
- ✅ Individual server commands available
- ✅ Build and test scripts coordinated
### Development Scripts ✅
```bash
# Root level coordination
pnpm run dev # Both servers
pnpm run backend:dev # Django only
pnpm run frontend:dev # Vue.js only
pnpm run build # Production build
pnpm run test # All tests
pnpm run lint # All linting
pnpm run format # Code formatting
```
## Deployment Strategy Validation
### Container Strategy ✅
**Multi-container Approach:**
- ✅ Separate containers for backend and frontend
- ✅ Shared volumes for media files
- ✅ Database and Redis containers
- ✅ Nginx reverse proxy configuration
**Build Process:**
- ✅ Backend: Django static collection + uv dependencies
- ✅ Frontend: Vite production build + asset optimization
- ✅ Shared: Media file persistence across deployments
### Platform Compatibility ✅
**Supported Deployment Platforms:**
- ✅ Docker Compose (local and production)
- ✅ Vercel (frontend + serverless backend)
- ✅ Railway (container deployment)
- ✅ DigitalOcean App Platform
- ✅ AWS ECS/Fargate
- ✅ Google Cloud Run
## Performance Considerations ✅
### Backend Optimization
- ✅ Database connection pooling
- ✅ Redis caching strategy
- ✅ Static file CDN integration
- ✅ API response optimization
### Frontend Optimization
- ✅ Code splitting and lazy loading
- ✅ Asset optimization with Vite
- ✅ Tree shaking for minimal bundle size
- ✅ Modern build targets
### Development Performance
- ✅ Hot module replacement for Vue.js
- ✅ Django auto-reload for backend changes
- ✅ Fast dependency installation with UV and pnpm
- ✅ Concurrent development servers
## Security Validation ✅
### Backend Security
- ✅ Django security middleware maintained
- ✅ CORS configuration for API access
- ✅ Authentication token management
- ✅ Input validation and sanitization
### Frontend Security
- ✅ Content Security Policy headers
- ✅ XSS protection mechanisms
- ✅ Secure API communication (HTTPS)
- ✅ Environment variable protection
### Deployment Security
- ✅ SSL/TLS termination
- ✅ Security headers configuration
- ✅ Secret management strategy
- ✅ Container security best practices
## Risk Assessment and Mitigation
### Low Risk Items ✅
- **File organization**: Clear mapping and systematic approach
- **Package management**: Both UV and pnpm are stable and well-supported
- **Development workflow**: Incremental changes to existing process
### Medium Risk Items ⚠️
- **Import path updates**: Requires careful testing of all Django apps
- **Static file handling**: Need to verify Django admin continues working
- **API integration**: New frontend-backend communication layer
**Mitigation Strategies:**
- Comprehensive testing suite for Django apps after migration
- Static file serving verification in development and production
- API endpoint testing and documentation
- Gradual migration approach with rollback capabilities
### High Risk Items 🔴
- **Data migration**: Database changes during restructuring
- **Production deployment**: New deployment process requires validation
**Mitigation Strategies:**
- Database backup before any structural changes
- Staging environment testing before production deployment
- Blue-green deployment strategy for zero-downtime migration
- Monitoring and alerting for post-migration issues
## Testing Strategy Validation
### Backend Testing ✅
```bash
# Django tests
cd backend
uv run manage.py test
# Code quality
uv run flake8 .
uv run black --check .
```
### Frontend Testing ✅
```bash
# Vue.js tests
cd frontend
pnpm run test
pnpm run test:unit
pnpm run test:e2e
# Code quality
pnpm run lint
pnpm run type-check
```
### Integration Testing ✅
- API endpoint testing
- Frontend-backend communication testing
- Media file access testing
- Authentication flow testing
## Documentation Validation ✅
### Created Documentation
-**Monorepo Structure Plan**: Complete directory organization
-**Migration Mapping**: File-by-file migration guide
-**Deployment Guide**: Comprehensive deployment strategies
-**Architecture Validation**: This validation document
### Required Updates
- ✅ Root README.md update for monorepo structure
- ✅ Development setup instructions
- ✅ API documentation for frontend integration
- ✅ Deployment runbooks
## Implementation Readiness Assessment
### Prerequisites Met ✅
- [x] Current Django project analysis complete
- [x] Monorepo structure designed
- [x] File migration strategy defined
- [x] Development workflow planned
- [x] Deployment strategy documented
- [x] Risk assessment completed
### Ready for Implementation ✅
- [x] Clear step-by-step migration plan
- [x] File mapping completeness verified
- [x] Package management strategy confirmed
- [x] Testing approach defined
- [x] Rollback strategy available
### Success Criteria Defined ✅
1. **Functional Requirements**
- All existing Django functionality preserved
- Modern Vue.js frontend operational
- API integration working correctly
- Media file handling functional
2. **Performance Requirements**
- Development servers start within reasonable time
- Build process completes successfully
- Production deployment successful
3. **Quality Requirements**
- All tests passing after migration
- Code quality standards maintained
- Documentation updated and complete
## Final Recommendation ✅
**Approval Status: APPROVED FOR IMPLEMENTATION**
The proposed monorepo architecture for ThrillWiki is comprehensive, well-planned, and ready for implementation. The plan demonstrates:
1. **Technical Soundness**: Architecture follows modern best practices
2. **Risk Management**: Potential issues identified with mitigation strategies
3. **Implementation Clarity**: Clear step-by-step migration process
4. **Operational Readiness**: Deployment and maintenance procedures defined
**Next Steps:**
1. Switch to **Code Mode** for implementation
2. Begin with directory structure creation
3. Migrate backend files systematically
4. Create Vue.js frontend application
5. Test integration between systems
6. Update deployment configurations
The architecture provides a solid foundation for scaling ThrillWiki with modern frontend technologies while preserving the robust Django backend functionality.

View File

@@ -0,0 +1,628 @@
# ThrillWiki Monorepo Deployment Guide
This document outlines deployment strategies, build processes, and infrastructure considerations for the ThrillWiki Django + Vue.js monorepo.
## Build Process Overview
```mermaid
graph TB
A[Source Code] --> B[Backend Build]
A --> C[Frontend Build]
B --> D[Django Static Collection]
C --> E[Vue.js Production Build]
D --> F[Backend Container]
E --> G[Frontend Assets]
F --> H[Production Deployment]
G --> H
```
## Development Environment
### Prerequisites
- Python 3.11+ with UV package manager
- Node.js 18+ with pnpm
- PostgreSQL (production) / SQLite (development)
- Redis (for caching and sessions)
### Local Development Setup
```bash
# Clone repository
git clone <repository-url>
cd thrillwiki-monorepo
# Install root dependencies
pnpm install
# Backend setup
cd backend
uv sync
uv run manage.py migrate
uv run manage.py collectstatic
# Frontend setup
cd ../frontend
pnpm install
# Start development servers
cd ..
pnpm run dev # Starts both backend and frontend
```
## Build Strategies
### 1. Containerized Deployment (Recommended)
#### Multi-stage Dockerfile for Backend
```dockerfile
# backend/Dockerfile
FROM python:3.11-slim as builder
WORKDIR /app
COPY pyproject.toml uv.lock ./
RUN pip install uv
RUN uv sync --no-dev
FROM python:3.11-slim as runtime
WORKDIR /app
COPY --from=builder /app/.venv /app/.venv
ENV PATH="/app/.venv/bin:$PATH"
COPY . .
RUN python manage.py collectstatic --noinput
EXPOSE 8000
CMD ["gunicorn", "config.wsgi:application", "--bind", "0.0.0.0:8000"]
```
#### Dockerfile for Frontend
```dockerfile
# frontend/Dockerfile
FROM node:18-alpine as builder
WORKDIR /app
COPY package.json pnpm-lock.yaml ./
RUN npm install -g pnpm
RUN pnpm install --frozen-lockfile
COPY . .
RUN pnpm run build
FROM nginx:alpine as runtime
COPY --from=builder /app/dist /usr/share/nginx/html
COPY nginx.conf /etc/nginx/nginx.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
```
#### Docker Compose for Development
```yaml
# docker-compose.dev.yml
version: '3.8'
services:
db:
image: postgres:15
environment:
POSTGRES_DB: thrillwiki
POSTGRES_USER: thrillwiki
POSTGRES_PASSWORD: password
volumes:
- postgres_data:/var/lib/postgresql/data
ports:
- "5432:5432"
redis:
image: redis:7-alpine
ports:
- "6379:6379"
backend:
build:
context: ./backend
dockerfile: Dockerfile.dev
ports:
- "8000:8000"
volumes:
- ./backend:/app
- ./shared/media:/app/media
environment:
- DEBUG=1
- DATABASE_URL=postgresql://thrillwiki:password@db:5432/thrillwiki
- REDIS_URL=redis://redis:6379/0
depends_on:
- db
- redis
frontend:
build:
context: ./frontend
dockerfile: Dockerfile.dev
ports:
- "3000:3000"
volumes:
- ./frontend:/app
- /app/node_modules
environment:
- VITE_API_URL=http://localhost:8000
volumes:
postgres_data:
```
#### Docker Compose for Production
```yaml
# docker-compose.prod.yml
version: '3.8'
services:
db:
image: postgres:15
environment:
POSTGRES_DB: ${POSTGRES_DB}
POSTGRES_USER: ${POSTGRES_USER}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
volumes:
- postgres_data:/var/lib/postgresql/data
restart: unless-stopped
redis:
image: redis:7-alpine
restart: unless-stopped
backend:
build:
context: ./backend
dockerfile: Dockerfile
environment:
- DEBUG=0
- DATABASE_URL=${DATABASE_URL}
- REDIS_URL=${REDIS_URL}
- SECRET_KEY=${SECRET_KEY}
- ALLOWED_HOSTS=${ALLOWED_HOSTS}
volumes:
- ./shared/media:/app/media
- static_files:/app/staticfiles
depends_on:
- db
- redis
restart: unless-stopped
frontend:
build:
context: ./frontend
dockerfile: Dockerfile
restart: unless-stopped
nginx:
image: nginx:alpine
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx/nginx.conf:/etc/nginx/nginx.conf
- ./nginx/ssl:/etc/nginx/ssl
- static_files:/usr/share/nginx/html/static
- ./shared/media:/usr/share/nginx/html/media
depends_on:
- backend
- frontend
restart: unless-stopped
volumes:
postgres_data:
static_files:
```
### 2. Static Site Generation (Alternative)
For sites with mostly static content, consider pre-rendering:
```bash
# Frontend build with pre-rendering
cd frontend
pnpm run build:prerender
# Serve static files with minimal backend
```
## CI/CD Pipeline
### GitHub Actions Workflow
```yaml
# .github/workflows/deploy.yml
name: Deploy ThrillWiki
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
services:
postgres:
image: postgres:15
env:
POSTGRES_PASSWORD: postgres
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.11'
- name: Install UV
run: pip install uv
- name: Backend Tests
run: |
cd backend
uv sync
uv run manage.py test
uv run flake8 .
uv run black --check .
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: '18'
- name: Install pnpm
run: npm install -g pnpm
- name: Frontend Tests
run: |
cd frontend
pnpm install --frozen-lockfile
pnpm run test
pnpm run lint
pnpm run type-check
build:
needs: test
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
steps:
- uses: actions/checkout@v4
- name: Build and push Docker images
run: |
docker build -t thrillwiki-backend ./backend
docker build -t thrillwiki-frontend ./frontend
# Push to registry
- name: Deploy to production
run: |
# Deploy using your preferred method
# (AWS ECS, GCP Cloud Run, Azure Container Instances, etc.)
```
## Platform-Specific Deployments
### 1. Vercel Deployment (Frontend + API)
```json
// vercel.json
{
"version": 2,
"builds": [
{
"src": "frontend/package.json",
"use": "@vercel/static-build",
"config": {
"distDir": "dist"
}
},
{
"src": "backend/config/wsgi.py",
"use": "@vercel/python"
}
],
"routes": [
{
"src": "/api/(.*)",
"dest": "backend/config/wsgi.py"
},
{
"src": "/(.*)",
"dest": "frontend/dist/$1"
}
]
}
```
### 2. Railway Deployment
```toml
# railway.toml
[environments.production]
[environments.production.services.backend]
dockerfile = "backend/Dockerfile"
variables = { DEBUG = "0" }
[environments.production.services.frontend]
dockerfile = "frontend/Dockerfile"
[environments.production.services.postgres]
image = "postgres:15"
variables = { POSTGRES_DB = "thrillwiki" }
```
### 3. DigitalOcean App Platform
```yaml
# .do/app.yaml
name: thrillwiki
services:
- name: backend
source_dir: backend
github:
repo: your-username/thrillwiki-monorepo
branch: main
run_command: gunicorn config.wsgi:application
environment_slug: python
instance_count: 1
instance_size_slug: basic-xxs
envs:
- key: DEBUG
value: "0"
- name: frontend
source_dir: frontend
github:
repo: your-username/thrillwiki-monorepo
branch: main
build_command: pnpm run build
run_command: pnpm run preview
environment_slug: node-js
instance_count: 1
instance_size_slug: basic-xxs
databases:
- name: thrillwiki-db
engine: PG
version: "15"
```
## Environment Configuration
### Environment Variables
#### Backend (.env)
```bash
# Django Settings
DEBUG=0
SECRET_KEY=your-secret-key-here
ALLOWED_HOSTS=yourdomain.com,www.yourdomain.com
# Database
DATABASE_URL=postgresql://user:password@host:port/database
# Redis
REDIS_URL=redis://host:port/0
# File Storage
MEDIA_ROOT=/app/media
STATIC_ROOT=/app/staticfiles
# Email
EMAIL_BACKEND=django.core.mail.backends.smtp.EmailBackend
EMAIL_HOST=smtp.yourmailprovider.com
EMAIL_PORT=587
EMAIL_USE_TLS=True
EMAIL_HOST_USER=your-email@yourdomain.com
EMAIL_HOST_PASSWORD=your-email-password
# Third-party Services
SENTRY_DSN=your-sentry-dsn
AWS_ACCESS_KEY_ID=your-aws-key
AWS_SECRET_ACCESS_KEY=your-aws-secret
```
#### Frontend (.env.production)
```bash
VITE_API_URL=https://api.yourdomain.com
VITE_APP_TITLE=ThrillWiki
VITE_SENTRY_DSN=your-frontend-sentry-dsn
VITE_GOOGLE_ANALYTICS_ID=your-ga-id
```
## Performance Optimization
### Backend Optimizations
```python
# backend/config/settings/production.py
# Database optimization
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql',
'CONN_MAX_AGE': 60,
'OPTIONS': {
'MAX_CONNS': 20,
}
}
}
# Caching
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.redis.RedisCache',
'LOCATION': 'redis://127.0.0.1:6379/1',
'OPTIONS': {
'CLIENT_CLASS': 'django_redis.client.DefaultClient',
},
'KEY_PREFIX': 'thrillwiki'
}
}
# Static files with CDN
AWS_S3_CUSTOM_DOMAIN = 'cdn.yourdomain.com'
STATICFILES_STORAGE = 'storages.backends.s3boto3.StaticS3Boto3Storage'
DEFAULT_FILE_STORAGE = 'storages.backends.s3boto3.MediaS3Boto3Storage'
```
### Frontend Optimizations
```typescript
// frontend/vite.config.ts
export default defineConfig({
build: {
rollupOptions: {
output: {
manualChunks: {
vendor: ['vue', 'vue-router', 'pinia'],
ui: ['@headlessui/vue', '@heroicons/vue']
}
}
},
sourcemap: false,
minify: 'terser',
terserOptions: {
compress: {
drop_console: true,
drop_debugger: true
}
}
}
})
```
## Monitoring and Logging
### Application Monitoring
```python
# backend/config/settings/production.py
import sentry_sdk
from sentry_sdk.integrations.django import DjangoIntegration
sentry_sdk.init(
dsn="your-sentry-dsn",
integrations=[DjangoIntegration()],
traces_sample_rate=0.1,
send_default_pii=True
)
# Logging configuration
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'handlers': {
'file': {
'level': 'INFO',
'class': 'logging.FileHandler',
'filename': '/var/log/django/thrillwiki.log',
},
},
'root': {
'handlers': ['file'],
},
}
```
### Infrastructure Monitoring
- Use Prometheus + Grafana for metrics
- Implement health check endpoints
- Set up log aggregation (ELK stack or similar)
- Monitor database performance
- Track API response times
## Security Considerations
### Production Security Checklist
- [ ] HTTPS enforced with SSL certificates
- [ ] Security headers configured (HSTS, CSP, etc.)
- [ ] Database credentials secured
- [ ] Secret keys rotated regularly
- [ ] CORS properly configured
- [ ] Rate limiting implemented
- [ ] File upload validation
- [ ] SQL injection protection
- [ ] XSS protection enabled
- [ ] CSRF protection active
### Security Headers
```python
# backend/config/settings/production.py
SECURE_SSL_REDIRECT = True
SECURE_HSTS_SECONDS = 31536000
SECURE_HSTS_INCLUDE_SUBDOMAINS = True
SECURE_HSTS_PRELOAD = True
SECURE_CONTENT_TYPE_NOSNIFF = True
SECURE_BROWSER_XSS_FILTER = True
X_FRAME_OPTIONS = 'DENY'
# CORS for API
CORS_ALLOWED_ORIGINS = [
"https://yourdomain.com",
"https://www.yourdomain.com",
]
```
## Backup and Recovery
### Database Backup Strategy
```bash
# Automated backup script
#!/bin/bash
pg_dump $DATABASE_URL | gzip > backup_$(date +%Y%m%d_%H%M%S).sql.gz
aws s3 cp backup_*.sql.gz s3://your-backup-bucket/database/
```
### Media Files Backup
```bash
# Sync media files to S3
aws s3 sync ./shared/media/ s3://your-media-bucket/media/ --delete
```
## Scaling Strategies
### Horizontal Scaling
- Load balancer configuration
- Database read replicas
- CDN for static assets
- Redis clustering
- Auto-scaling groups
### Vertical Scaling
- Database connection pooling
- Application server optimization
- Memory usage optimization
- CPU-intensive task optimization
## Troubleshooting Guide
### Common Issues
1. **Build failures**: Check dependencies and environment variables
2. **Database connection errors**: Verify connection strings and firewall rules
3. **Static file 404s**: Ensure collectstatic runs and paths are correct
4. **CORS errors**: Check CORS configuration and allowed origins
5. **Memory issues**: Monitor application memory usage and optimize queries
### Debug Commands
```bash
# Backend debugging
cd backend
uv run manage.py check --deploy
uv run manage.py shell
uv run manage.py dbshell
# Frontend debugging
cd frontend
pnpm run build --debug
pnpm run preview
```
This deployment guide provides a comprehensive approach to deploying the ThrillWiki monorepo across various platforms while maintaining security, performance, and scalability.

View File

@@ -0,0 +1,353 @@
# ThrillWiki Migration Mapping Document
This document provides a comprehensive mapping of files from the current Django project to the new monorepo structure.
## Root Level Files
| Current Location | New Location | Notes |
|------------------|--------------|-------|
| `manage.py` | `backend/manage.py` | Core Django management |
| `pyproject.toml` | `backend/pyproject.toml` | Python dependencies |
| `uv.lock` | `backend/uv.lock` | UV lock file |
| `.gitignore` | `.gitignore` (update) | Merge with monorepo patterns |
| `README.md` | `README.md` (update) | Update for monorepo |
| `.pre-commit-config.yaml` | `.pre-commit-config.yaml` | Root level |
## Configuration Directory
| Current Location | New Location | Notes |
|------------------|--------------|-------|
| `config/django/` | `backend/config/django/` | Django settings |
| `config/settings/` | `backend/config/settings/` | Environment settings |
| `config/urls.py` | `backend/config/urls.py` | URL configuration |
| `config/wsgi.py` | `backend/config/wsgi.py` | WSGI configuration |
| `config/asgi.py` | `backend/config/asgi.py` | ASGI configuration |
## Django Apps
### Accounts App
| Current Location | New Location |
|------------------|--------------|
| `accounts/` | `backend/apps/accounts/` |
| `accounts/__init__.py` | `backend/apps/accounts/__init__.py` |
| `accounts/models.py` | `backend/apps/accounts/models.py` |
| `accounts/views.py` | `backend/apps/accounts/views.py` |
| `accounts/admin.py` | `backend/apps/accounts/admin.py` |
| `accounts/apps.py` | `backend/apps/accounts/apps.py` |
| `accounts/migrations/` | `backend/apps/accounts/migrations/` |
| `accounts/tests/` | `backend/apps/accounts/tests/` |
### Parks App
| Current Location | New Location |
|------------------|--------------|
| `parks/` | `backend/apps/parks/` |
| `parks/__init__.py` | `backend/apps/parks/__init__.py` |
| `parks/models.py` | `backend/apps/parks/models.py` |
| `parks/views.py` | `backend/apps/parks/views.py` |
| `parks/admin.py` | `backend/apps/parks/admin.py` |
| `parks/apps.py` | `backend/apps/parks/apps.py` |
| `parks/migrations/` | `backend/apps/parks/migrations/` |
| `parks/tests/` | `backend/apps/parks/tests/` |
### Rides App
| Current Location | New Location |
|------------------|--------------|
| `rides/` | `backend/apps/rides/` |
| `rides/__init__.py` | `backend/apps/rides/__init__.py` |
| `rides/models.py` | `backend/apps/rides/models.py` |
| `rides/views.py` | `backend/apps/rides/views.py` |
| `rides/admin.py` | `backend/apps/rides/admin.py` |
| `rides/apps.py` | `backend/apps/rides/apps.py` |
| `rides/migrations/` | `backend/apps/rides/migrations/` |
| `rides/tests/` | `backend/apps/rides/tests/` |
### Moderation App
| Current Location | New Location |
|------------------|--------------|
| `moderation/` | `backend/apps/moderation/` |
| `moderation/__init__.py` | `backend/apps/moderation/__init__.py` |
| `moderation/models.py` | `backend/apps/moderation/models.py` |
| `moderation/views.py` | `backend/apps/moderation/views.py` |
| `moderation/admin.py` | `backend/apps/moderation/admin.py` |
| `moderation/apps.py` | `backend/apps/moderation/apps.py` |
| `moderation/migrations/` | `backend/apps/moderation/migrations/` |
| `moderation/tests/` | `backend/apps/moderation/tests/` |
### Location App
| Current Location | New Location |
|------------------|--------------|
| `location/` | `backend/apps/location/` |
| `location/__init__.py` | `backend/apps/location/__init__.py` |
| `location/models.py` | `backend/apps/location/models.py` |
| `location/views.py` | `backend/apps/location/views.py` |
| `location/admin.py` | `backend/apps/location/admin.py` |
| `location/apps.py` | `backend/apps/location/apps.py` |
| `location/migrations/` | `backend/apps/location/migrations/` |
| `location/tests/` | `backend/apps/location/tests/` |
### Media App
| Current Location | New Location |
|------------------|--------------|
| `media/` | `backend/apps/media/` |
| `media/__init__.py` | `backend/apps/media/__init__.py` |
| `media/models.py` | `backend/apps/media/models.py` |
| `media/views.py` | `backend/apps/media/views.py` |
| `media/admin.py` | `backend/apps/media/admin.py` |
| `media/apps.py` | `backend/apps/media/apps.py` |
| `media/migrations/` | `backend/apps/media/migrations/` |
| `media/tests/` | `backend/apps/media/tests/` |
### Email Service App
| Current Location | New Location |
|------------------|--------------|
| `email_service/` | `backend/apps/email_service/` |
| `email_service/__init__.py` | `backend/apps/email_service/__init__.py` |
| `email_service/models.py` | `backend/apps/email_service/models.py` |
| `email_service/views.py` | `backend/apps/email_service/views.py` |
| `email_service/admin.py` | `backend/apps/email_service/admin.py` |
| `email_service/apps.py` | `backend/apps/email_service/apps.py` |
| `email_service/migrations/` | `backend/apps/email_service/migrations/` |
| `email_service/tests/` | `backend/apps/email_service/tests/` |
### Core App
| Current Location | New Location |
|------------------|--------------|
| `core/` | `backend/apps/core/` |
| `core/__init__.py` | `backend/apps/core/__init__.py` |
| `core/models.py` | `backend/apps/core/models.py` |
| `core/views.py` | `backend/apps/core/views.py` |
| `core/admin.py` | `backend/apps/core/admin.py` |
| `core/apps.py` | `backend/apps/core/apps.py` |
| `core/migrations/` | `backend/apps/core/migrations/` |
| `core/tests/` | `backend/apps/core/tests/` |
## Static Files and Templates
| Current Location | New Location | Notes |
|------------------|--------------|-------|
| `static/` | `backend/static/` | Django admin and backend assets |
| `staticfiles/` | `backend/staticfiles/` | Collected static files |
| `templates/` | `backend/templates/` | Django templates (if any) |
## Media Files
| Current Location | New Location | Notes |
|------------------|--------------|-------|
| `media/` | `shared/media/` | User uploaded content |
## Scripts and Development Tools
| Current Location | New Location | Notes |
|------------------|--------------|-------|
| `scripts/` | `scripts/` | Root level scripts |
| `scripts/dev_server.sh` | `scripts/backend_dev.sh` | Rename for clarity |
## New Frontend Structure (Created)
| New Location | Purpose |
|--------------|---------|
| `frontend/` | Vue.js application root |
| `frontend/package.json` | Node.js dependencies |
| `frontend/pnpm-lock.yaml` | pnpm lock file |
| `frontend/vite.config.ts` | Vite configuration |
| `frontend/tsconfig.json` | TypeScript configuration |
| `frontend/tailwind.config.js` | Tailwind CSS configuration |
| `frontend/src/` | Vue.js source code |
| `frontend/src/main.ts` | Application entry point |
| `frontend/src/App.vue` | Root component |
| `frontend/src/components/` | Vue components |
| `frontend/src/views/` | Page components |
| `frontend/src/router/` | Vue Router configuration |
| `frontend/src/stores/` | Pinia stores |
| `frontend/src/composables/` | Vue composables |
| `frontend/src/utils/` | Utility functions |
| `frontend/src/types/` | TypeScript type definitions |
| `frontend/src/assets/` | Static assets |
| `frontend/public/` | Public assets |
| `frontend/dist/` | Build output |
## New Shared Resources (Created)
| New Location | Purpose |
|--------------|---------|
| `shared/` | Cross-platform resources |
| `shared/media/` | User uploaded files |
| `shared/docs/` | Documentation |
| `shared/types/` | Shared TypeScript types |
| `shared/constants/` | Shared constants |
## Updated Root Files
### package.json (Root)
```json
{
"name": "thrillwiki-monorepo",
"private": true,
"workspaces": [
"frontend"
],
"scripts": {
"dev": "concurrently \"pnpm --filter frontend dev\" \"./scripts/backend_dev.sh\"",
"build": "pnpm --filter frontend build",
"backend:dev": "./scripts/backend_dev.sh",
"frontend:dev": "pnpm --filter frontend dev",
"test": "pnpm --filter frontend test && cd backend && uv run manage.py test",
"lint": "pnpm --filter frontend lint && cd backend && uv run flake8 .",
"format": "pnpm --filter frontend format && cd backend && uv run black ."
},
"devDependencies": {
"concurrently": "^8.2.2"
}
}
```
### .gitignore (Updated)
```gitignore
# Python
__pycache__/
*.py[cod]
*$py.class
*.so
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# Django
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal
/backend/static/
/backend/media/
# UV
.uv/
# Node.js
node_modules/
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
.pnpm-store/
# Vue.js / Vite
/frontend/dist/
/frontend/dist-ssr/
*.local
# Environment variables
.env
.env.local
.env.development.local
.env.test.local
.env.production.local
# IDEs
.vscode/
.idea/
*.swp
*.swo
# OS
.DS_Store
Thumbs.db
# Logs
logs/
*.log
# Coverage
coverage/
*.lcov
.nyc_output
```
## Configuration Updates Required
### Backend Django Settings
Update `INSTALLED_APPS` paths:
```python
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
# Local apps
'apps.accounts',
'apps.parks',
'apps.rides',
'apps.moderation',
'apps.location',
'apps.media',
'apps.email_service',
'apps.core',
]
```
Update media and static files paths:
```python
STATIC_URL = '/static/'
STATIC_ROOT = BASE_DIR / 'staticfiles'
STATICFILES_DIRS = [
BASE_DIR / 'static',
]
MEDIA_URL = '/media/'
MEDIA_ROOT = BASE_DIR.parent / 'shared' / 'media'
```
### Script Updates
Update `scripts/backend_dev.sh`:
```bash
#!/bin/bash
cd backend
lsof -ti :8000 | xargs kill -9 2>/dev/null || true
find . -type d -name "__pycache__" -exec rm -r {} + 2>/dev/null || true
uv run manage.py runserver 0.0.0.0:8000
```
## Migration Steps Summary
1. **Create new directory structure**
2. **Move backend files** to `backend/` directory
3. **Update import paths** in Django settings and apps
4. **Create frontend** Vue.js application
5. **Update scripts** and configuration files
6. **Test both backend and frontend** independently
7. **Configure API integration** between Django and Vue.js
8. **Update deployment** configurations
## Validation Checklist
- [ ] All Django apps moved to `backend/apps/`
- [ ] Configuration files updated with new paths
- [ ] Static and media file paths configured correctly
- [ ] Frontend Vue.js application created and configured
- [ ] Root package.json with workspace configuration
- [ ] Development scripts updated and tested
- [ ] Git configuration updated
- [ ] Documentation updated
- [ ] CI/CD pipelines updated (if applicable)
- [ ] Database migrations work correctly
- [ ] Both development servers start successfully
- [ ] API endpoints accessible from frontend

View File

@@ -0,0 +1,525 @@
# ThrillWiki Django + Vue.js Monorepo Architecture Plan
## Executive Summary
This document outlines the optimal monorepo directory structure for migrating the ThrillWiki Django project to a Django + Vue.js architecture. The design separates backend and frontend concerns while maintaining existing Django app organization and supporting modern development workflows.
## Current Project Analysis
### Django Apps Structure
- **accounts**: User management and authentication
- **parks**: Theme park data and operations
- **rides**: Ride information and management
- **moderation**: Content moderation system
- **location**: Geographic data handling
- **media**: File and image management
- **email_service**: Email functionality
- **core**: Core utilities and services
### Key Infrastructure
- **Package Management**: UV-based Python setup
- **Configuration**: `config/django/` for settings, `config/settings/` for modular settings
- **Development**: `scripts/dev_server.sh` with comprehensive setup
- **Static Assets**: Tailwind CSS integration, `static/` and `staticfiles/`
- **Media Handling**: Organized `media/` directory with park/ride subdirectories
## Proposed Monorepo Structure
```
thrillwiki-monorepo/
├── README.md
├── pyproject.toml # Python dependencies (backend only)
├── package.json # Node.js dependencies (monorepo coordination)
├── pnpm-workspace.yaml # pnpm workspace configuration
├── .env.example
├── .gitignore
├──
├── backend/ # Django Backend
│ ├── manage.py
│ ├── pyproject.toml # Backend-specific dependencies
│ ├── config/
│ │ ├── django/
│ │ │ ├── base.py
│ │ │ ├── local.py
│ │ │ ├── production.py
│ │ │ └── test.py
│ │ └── settings/
│ │ ├── database.py
│ │ ├── email.py
│ │ └── security.py
│ ├── thrillwiki/
│ │ ├── __init__.py
│ │ ├── urls.py
│ │ ├── wsgi.py
│ │ ├── asgi.py
│ │ └── views.py
│ ├── apps/ # Django apps
│ │ ├── accounts/
│ │ ├── parks/
│ │ ├── rides/
│ │ ├── moderation/
│ │ ├── location/
│ │ ├── media/
│ │ ├── email_service/
│ │ └── core/
│ ├── templates/ # Django templates (API responses, admin)
│ ├── static/ # Backend static files
│ │ └── admin/ # Django admin assets
│ ├── media/ # User uploads
│ │ ├── avatars/
│ │ ├── park/
│ │ └── submissions/
│ └── tests/ # Backend tests
├── frontend/ # Vue.js Frontend
│ ├── package.json
│ ├── pnpm-lock.yaml
│ ├── vite.config.js
│ ├── tailwind.config.js
│ ├── index.html
│ ├── src/
│ │ ├── main.js
│ │ ├── App.vue
│ │ ├── router/
│ │ │ └── index.js
│ │ ├── stores/ # Pinia/Vuex stores
│ │ │ ├── auth.js
│ │ │ ├── parks.js
│ │ │ └── rides.js
│ │ ├── components/
│ │ │ ├── common/ # Shared components
│ │ │ ├── parks/ # Park-specific components
│ │ │ ├── rides/ # Ride-specific components
│ │ │ └── moderation/ # Moderation components
│ │ ├── views/ # Page components
│ │ │ ├── Home.vue
│ │ │ ├── parks/
│ │ │ ├── rides/
│ │ │ └── auth/
│ │ ├── composables/ # Vue 3 composables
│ │ │ ├── useAuth.js
│ │ │ ├── useApi.js
│ │ │ └── useTheme.js
│ │ ├── services/ # API service layer
│ │ │ ├── api.js
│ │ │ ├── auth.js
│ │ │ ├── parks.js
│ │ │ └── rides.js
│ │ ├── assets/
│ │ │ ├── images/
│ │ │ └── styles/
│ │ │ ├── globals.css
│ │ │ └── components/
│ │ └── utils/
│ ├── public/
│ │ ├── favicon.ico
│ │ └── images/
│ ├── dist/ # Build output
│ └── tests/ # Frontend tests
│ ├── unit/
│ └── e2e/
├── shared/ # Shared Resources
│ ├── docs/ # Documentation
│ │ ├── api/ # API documentation
│ │ ├── deployment/ # Deployment guides
│ │ └── development/ # Development setup
│ ├── scripts/ # Build and deployment scripts
│ │ ├── dev/
│ │ │ ├── start-backend.sh
│ │ │ ├── start-frontend.sh
│ │ │ └── start-full-stack.sh
│ │ ├── build/
│ │ │ ├── build-frontend.sh
│ │ │ └── build-production.sh
│ │ ├── deploy/
│ │ └── utils/
│ ├── config/ # Shared configuration
│ │ ├── docker/
│ │ │ ├── Dockerfile.backend
│ │ │ ├── Dockerfile.frontend
│ │ │ └── docker-compose.yml
│ │ ├── nginx/
│ │ └── ci/ # CI/CD configuration
│ │ └── github-actions/
│ └── types/ # Shared TypeScript types
│ ├── api.ts
│ ├── parks.ts
│ └── rides.ts
├── logs/ # Application logs
├── backups/ # Database backups
├── uploads/ # Temporary upload directory
└── dist/ # Production build output
├── backend/ # Django static files
└── frontend/ # Vue.js build
```
## Directory Organization Rationale
### 1. Clear Separation of Concerns
- **backend/**: Contains all Django-related code, maintaining existing app structure
- **frontend/**: Vue.js application with modern structure (Vite + Vue 3)
- **shared/**: Common resources, documentation, and configuration
### 2. Backend Structure (`backend/`)
- Preserves existing Django app organization under `apps/`
- Maintains UV-based Python dependency management
- Keeps configuration structure with `config/django/` and `config/settings/`
- Separates templates for API responses vs. frontend UI
### 3. Frontend Structure (`frontend/`)
- Modern Vue 3 + Vite setup with TypeScript support
- Organized by feature areas (parks, rides, auth)
- Composables for Vue 3 Composition API patterns
- Service layer for API communication with Django backend
- Tailwind CSS integration with shared design system
### 4. Shared Resources (`shared/`)
- Centralized documentation and deployment scripts
- Docker configuration for containerized deployment
- TypeScript type definitions shared between frontend and API
- CI/CD pipeline configuration
## Static File Strategy
### Development
```mermaid
graph LR
A[Vue Dev Server :3000] --> B[Vite HMR]
C[Django Dev Server :8000] --> D[Django Static Files]
E[Tailwind CSS] --> F[Both Frontend & Backend]
```
### Production
```mermaid
graph LR
A[Vue Build] --> B[dist/frontend/]
C[Django Collectstatic] --> D[dist/backend/]
E[Nginx] --> F[Serves Both]
F --> G[Frontend Assets]
F --> H[API Endpoints]
F --> I[Media Files]
```
### Implementation Details
1. **Development Mode**:
- Frontend: Vite dev server on port 3000 with HMR
- Backend: Django dev server on port 8000
- Proxy API calls from frontend to backend
2. **Production Mode**:
- Frontend built to `dist/frontend/`
- Django static files collected to `dist/backend/`
- Nginx serves static files and proxies API calls
## Media File Management
### Current Structure Preservation
```
media/
├── avatars/ # User profile images
├── park/ # Park-specific media
│ ├── {park-slug}/
│ │ └── {ride-slug}/
└── submissions/ # User-submitted content
└── photos/
```
### Strategy
- **Development**: Django serves media files directly
- **Production**: CDN or object storage (S3/CloudFlare) integration
- **Frontend Access**: Media URLs provided via API responses
- **Upload Handling**: Django handles all file uploads, Vue.js provides UI
## Development Workflow Integration
### Package Management
- **Root**: Node.js dependencies for frontend and tooling (using pnpm)
- **Backend**: UV for Python dependencies (existing approach)
- **Frontend**: pnpm for Vue.js dependencies
### Development Scripts
```bash
# Root level scripts
pnpm run dev # Start both backend and frontend
pnpm run dev:backend # Start only Django
pnpm run dev:frontend # Start only Vue.js
pnpm run build # Build for production
pnpm run test # Run all tests
# Backend specific (using UV)
cd backend && uv run manage.py runserver
cd backend && uv run manage.py test
# Frontend specific
cd frontend && pnpm run dev
cd frontend && pnpm run build
cd frontend && pnpm run test
```
### Environment Configuration
```bash
# Root .env (shared settings)
DATABASE_URL=
REDIS_URL=
SECRET_KEY=
# Backend .env (Django specific)
DJANGO_SETTINGS_MODULE=config.django.local
DEBUG=True
# Frontend .env (Vue specific)
VITE_API_BASE_URL=http://localhost:8000/api
VITE_APP_TITLE=ThrillWiki
```
### Package Manager Configuration
#### Root pnpm-workspace.yaml
```yaml
packages:
- 'frontend'
# Backend is managed separately with uv
```
#### Root package.json
```json
{
"name": "thrillwiki-monorepo",
"private": true,
"packageManager": "pnpm@9.0.0",
"scripts": {
"dev": "concurrently \"pnpm run dev:backend\" \"pnpm run dev:frontend\"",
"dev:backend": "cd backend && uv run manage.py runserver",
"dev:frontend": "cd frontend && pnpm run dev",
"build": "pnpm run build:frontend && cd backend && uv run manage.py collectstatic --noinput",
"build:frontend": "cd frontend && pnpm run build",
"test": "pnpm run test:backend && pnpm run test:frontend",
"test:backend": "cd backend && uv run manage.py test",
"test:frontend": "cd frontend && pnpm run test",
"lint": "cd frontend && pnpm run lint && cd ../backend && uv run flake8 .",
"format": "cd frontend && pnpm run format && cd ../backend && uv run black ."
},
"devDependencies": {
"concurrently": "^8.2.0"
}
}
```
#### Frontend package.json
```json
{
"name": "thrillwiki-frontend",
"private": true,
"version": "0.1.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview",
"test": "vitest",
"test:e2e": "playwright test",
"lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix",
"format": "prettier --write src/",
"type-check": "vue-tsc --noEmit"
},
"dependencies": {
"vue": "^3.4.0",
"vue-router": "^4.3.0",
"pinia": "^2.1.0",
"axios": "^1.6.0"
},
"devDependencies": {
"@vitejs/plugin-vue": "^5.0.0",
"vite": "^5.0.0",
"vue-tsc": "^2.0.0",
"typescript": "^5.3.0",
"tailwindcss": "^3.4.0",
"autoprefixer": "^10.4.0",
"postcss": "^8.4.0",
"eslint": "^8.57.0",
"prettier": "^3.2.0",
"vitest": "^1.3.0",
"@playwright/test": "^1.42.0"
}
}
```
## File Migration Mapping
### High-Level Moves
```
Current → New Location
├── manage.py → backend/manage.py
├── pyproject.toml → backend/pyproject.toml (+ root package.json)
├── config/ → backend/config/
├── thrillwiki/ → backend/thrillwiki/
├── accounts/ → backend/apps/accounts/
├── parks/ → backend/apps/parks/
├── rides/ → backend/apps/rides/
├── moderation/ → backend/apps/moderation/
├── location/ → backend/apps/location/
├── media/ → backend/apps/media/
├── email_service/ → backend/apps/email_service/
├── core/ → backend/apps/core/
├── templates/ → backend/templates/ (API) + frontend/src/views/ (UI)
├── static/ → backend/static/ (admin) + frontend/src/assets/
├── media/ → media/ (shared, accessible to both)
├── scripts/ → shared/scripts/
├── docs/ → shared/docs/
├── tests/ → backend/tests/ + frontend/tests/
└── staticfiles/ → dist/backend/ (generated)
```
### Detailed Backend App Moves
Each Django app moves to `backend/apps/{app_name}/` with structure preserved:
- Models, views, serializers stay the same
- Templates for API responses remain in app directories
- Static files move to frontend if UI-related
- Tests remain with respective apps
## Build and Deployment Strategy
### Development Build Process
1. **Backend**: No build step, runs directly with Django dev server
2. **Frontend**: Vite development server with HMR
3. **Shared**: Scripts orchestrate starting both services
### Production Build Process
```mermaid
graph TD
A[CI/CD Trigger] --> B[Install Dependencies]
B --> C[Build Frontend]
B --> D[Collect Django Static]
C --> E[Generate Frontend Bundle]
D --> F[Collect Backend Assets]
E --> G[Create Docker Images]
F --> G
G --> H[Deploy to Production]
```
### Container Strategy
- **Multi-stage Docker builds**: Separate backend and frontend images
- **Nginx**: Reverse proxy and static file serving
- **Volume mounts**: For media files and logs
- **Environment-based configuration**: Development vs. production
## API Integration Strategy
### Backend API Structure
```python
# Enhanced DRF setup for SPA
REST_FRAMEWORK = {
'DEFAULT_RENDERER_CLASSES': [
'rest_framework.renderers.JSONRenderer',
],
'DEFAULT_AUTHENTICATION_CLASSES': [
'rest_framework.authentication.SessionAuthentication',
'rest_framework.authentication.TokenAuthentication',
],
}
# CORS for development
CORS_ALLOWED_ORIGINS = [
"http://localhost:3000", # Vue dev server
]
```
### Frontend API Service
```javascript
// API service with auth integration
class ApiService {
constructor() {
this.client = axios.create({
baseURL: import.meta.env.VITE_API_BASE_URL,
withCredentials: true,
});
}
// Park operations
getParks(params = {}) {
return this.client.get('/parks/', { params });
}
// Ride operations
getRides(parkId, params = {}) {
return this.client.get(`/parks/${parkId}/rides/`, { params });
}
}
```
## Configuration Management
### Shared Environment Variables
- Database connections
- Redis/Cache settings
- Secret keys and API keys
- Feature flags
### Application-Specific Settings
- **Django**: `backend/config/django/`
- **Vue.js**: `frontend/.env` files
- **Docker**: `shared/config/docker/`
### Development vs. Production
- Development: Multiple local servers, hot reloading
- Production: Containerized deployment, CDN integration
## Benefits of This Structure
1. **Clear Separation**: Backend and frontend concerns are clearly separated
2. **Scalability**: Each part can be developed, tested, and deployed independently
3. **Modern Workflow**: Supports latest Vue 3, Vite, and Django patterns
4. **Backward Compatibility**: Preserves existing Django app structure
5. **Developer Experience**: Hot reloading, TypeScript support, modern tooling
6. **Deployment Flexibility**: Can deploy as SPA + API or traditional Django
## Implementation Phases
### Phase 1: Structure Setup
1. Create new directory structure
2. Move Django code to `backend/`
3. Initialize Vue.js frontend
4. Set up basic API integration
### Phase 2: Frontend Development
1. Create Vue.js components for existing Django templates
2. Implement routing and state management
3. Integrate with Django API endpoints
4. Add authentication flow
### Phase 3: Build & Deploy
1. Set up build processes
2. Configure CI/CD pipelines
3. Implement production deployment
4. Performance optimization
## Considerations and Trade-offs
### Advantages
- Modern development experience
- Better code organization
- Independent scaling
- Rich frontend interactions
- API-first architecture
### Challenges
- Increased complexity
- Build process coordination
- Authentication across services
- SEO considerations (if needed)
- Development environment setup
## Next Steps
1. **Validate Architecture**: Review with development team
2. **Prototype Setup**: Create basic structure with sample components
3. **Migration Planning**: Detailed plan for moving existing code
4. **Tool Selection**: Finalize Vue.js ecosystem choices (Pinia vs. Vuex, etc.)
5. **Implementation**: Begin phase-by-phase migration
---
This architecture provides a solid foundation for migrating ThrillWiki to a modern Django + Vue.js monorepo while preserving existing functionality and enabling future growth.

31
backend/.env.example Normal file
View File

@@ -0,0 +1,31 @@
# Django Configuration
SECRET_KEY=your-secret-key-here
DEBUG=True
DJANGO_SETTINGS_MODULE=config.django.local
# Database
DATABASE_URL=postgresql://user:password@localhost:5432/thrillwiki
# Redis
REDIS_URL=redis://localhost:6379
# Email Configuration (Optional)
EMAIL_HOST=smtp.gmail.com
EMAIL_PORT=587
EMAIL_USE_TLS=True
EMAIL_HOST_USER=your-email@gmail.com
EMAIL_HOST_PASSWORD=your-app-password
# Media and Static Files
MEDIA_URL=/media/
STATIC_URL=/static/
# Security
ALLOWED_HOSTS=localhost,127.0.0.1
# API Configuration
CORS_ALLOWED_ORIGINS=http://localhost:3000
# Feature Flags
ENABLE_DEBUG_TOOLBAR=True
ENABLE_SILK_PROFILER=False

229
backend/README.md Normal file
View File

@@ -0,0 +1,229 @@
# ThrillWiki Backend
Django REST API backend for the ThrillWiki monorepo.
## 🏗️ Architecture
This backend follows Django best practices with a modular app structure:
```
backend/
├── apps/ # Django applications
│ ├── accounts/ # User management
│ ├── parks/ # Theme park data
│ ├── rides/ # Ride information
│ ├── moderation/ # Content moderation
│ ├── location/ # Geographic data
│ ├── media/ # File management
│ ├── email_service/ # Email functionality
│ └── core/ # Core utilities
├── config/ # Django configuration
│ ├── django/ # Settings files
│ └── settings/ # Modular settings
├── templates/ # Django templates
├── static/ # Static files
└── tests/ # Test files
```
## 🛠️ Technology Stack
- **Django 5.0+** - Web framework
- **Django REST Framework** - API framework
- **PostgreSQL** - Primary database
- **Redis** - Caching and sessions
- **UV** - Python package management
- **Celery** - Background task processing
## 🚀 Quick Start
### Prerequisites
- Python 3.11+
- [uv](https://docs.astral.sh/uv/) package manager
- PostgreSQL 14+
- Redis 6+
### Setup
1. **Install dependencies**
```bash
cd backend
uv sync
```
2. **Environment configuration**
```bash
cp .env.example .env
# Edit .env with your settings
```
3. **Database setup**
```bash
uv run manage.py migrate
uv run manage.py createsuperuser
```
4. **Start development server**
```bash
uv run manage.py runserver
```
## 🔧 Configuration
### Environment Variables
Required environment variables:
```bash
# Database
DATABASE_URL=postgresql://user:pass@localhost/thrillwiki
# Django
SECRET_KEY=your-secret-key
DEBUG=True
DJANGO_SETTINGS_MODULE=config.django.local
# Redis
REDIS_URL=redis://localhost:6379
# Email (optional)
EMAIL_HOST=smtp.gmail.com
EMAIL_PORT=587
EMAIL_USE_TLS=True
EMAIL_HOST_USER=your-email@gmail.com
EMAIL_HOST_PASSWORD=your-app-password
```
### Settings Structure
- `config/django/base.py` - Base settings
- `config/django/local.py` - Development settings
- `config/django/production.py` - Production settings
- `config/django/test.py` - Test settings
## 📁 Apps Overview
### Core Apps
- **accounts** - User authentication and profile management
- **parks** - Theme park models and operations
- **rides** - Ride information and relationships
- **core** - Shared utilities and base classes
### Support Apps
- **moderation** - Content moderation workflows
- **location** - Geographic data and services
- **media** - File upload and management
- **email_service** - Email sending and templates
## 🔌 API Endpoints
Base URL: `http://localhost:8000/api/`
### Authentication
- `POST /auth/login/` - User login
- `POST /auth/logout/` - User logout
- `POST /auth/register/` - User registration
### Parks
- `GET /parks/` - List parks
- `GET /parks/{id}/` - Park details
- `POST /parks/` - Create park (admin)
### Rides
- `GET /rides/` - List rides
- `GET /rides/{id}/` - Ride details
- `GET /parks/{park_id}/rides/` - Rides by park
## 🧪 Testing
```bash
# Run all tests
uv run manage.py test
# Run specific app tests
uv run manage.py test apps.parks
# Run with coverage
uv run coverage run manage.py test
uv run coverage report
```
## 🔧 Management Commands
Custom management commands:
```bash
# Import park data
uv run manage.py import_parks data/parks.json
# Generate test data
uv run manage.py generate_test_data
# Clean up expired sessions
uv run manage.py clearsessions
```
## 📊 Database
### Entity Relationships
- **Parks** have Operators (required) and PropertyOwners (optional)
- **Rides** belong to Parks and may have Manufacturers/Designers
- **Users** can create submissions and moderate content
### Migrations
```bash
# Create migrations
uv run manage.py makemigrations
# Apply migrations
uv run manage.py migrate
# Show migration status
uv run manage.py showmigrations
```
## 🔐 Security
- CORS configured for frontend integration
- CSRF protection enabled
- JWT token authentication
- Rate limiting on API endpoints
- Input validation and sanitization
## 📈 Performance
- Database query optimization
- Redis caching for frequent queries
- Background task processing with Celery
- Database connection pooling
## 🚀 Deployment
See the [Deployment Guide](../shared/docs/deployment/) for production setup.
## 🐛 Debugging
### Development Tools
- Django Debug Toolbar
- Django Extensions
- Silk profiler for performance analysis
### Logging
Logs are written to:
- Console (development)
- Files in `logs/` directory (production)
- External logging service (production)
## 🤝 Contributing
1. Follow Django coding standards
2. Write tests for new features
3. Update documentation
4. Run linting: `uv run flake8 .`
5. Format code: `uv run black .`

6
backend/apps/__init__.py Normal file
View File

@@ -0,0 +1,6 @@
"""
Django apps package.
This directory contains all Django applications for the ThrillWiki backend.
Each app is self-contained and follows Django best practices.
"""

View File

@@ -3,7 +3,7 @@ from django.apps import AppConfig
class AccountsConfig(AppConfig):
default_auto_field = "django.db.models.BigAutoField"
name = "accounts"
name = "apps.accounts"
def ready(self):
import accounts.signals # noqa
import apps.accounts.signals # noqa

View File

@@ -1,8 +1,8 @@
from django.core.management.base import BaseCommand
from django.contrib.auth import get_user_model
from parks.models import ParkReview, Park
from rides.models import Ride
from media.models import Photo
from apps.parks.models import ParkReview, Park
from apps.rides.models import Ride
from apps.media.models import Photo
User = get_user_model()

View File

@@ -1,5 +1,5 @@
from django.core.management.base import BaseCommand
from accounts.models import UserProfile
from apps.accounts.models import UserProfile
class Command(BaseCommand):

View File

@@ -1,7 +1,7 @@
from django.core.management.base import BaseCommand
from django.contrib.auth.models import Group
from accounts.models import User
from accounts.signals import create_default_groups
from apps.accounts.models import User
from apps.accounts.signals import create_default_groups
class Command(BaseCommand):

View File

@@ -4,7 +4,7 @@ from django.urls import reverse
from django.utils.translation import gettext_lazy as _
import os
import secrets
from core.history import TrackedModel
from apps.core.history import TrackedModel
# import pghistory

View File

@@ -4,7 +4,7 @@ from django.urls import reverse
from django.utils.translation import gettext_lazy as _
import os
import secrets
from core.history import TrackedModel
from apps.core.history import TrackedModel
import pghistory

View File

@@ -17,19 +17,19 @@ from django.http import HttpResponseRedirect, HttpResponse, HttpRequest
from django.urls import reverse
from django.contrib.auth import login
from django.core.files.uploadedfile import UploadedFile
from accounts.models import (
from apps.accounts.models import (
User,
PasswordReset,
TopList,
EmailVerification,
UserProfile,
)
from email_service.services import EmailService
from parks.models import ParkReview
from rides.models import RideReview
from apps.email_service.services import EmailService
from apps.parks.models import ParkReview
from apps.rides.models import RideReview
from allauth.account.views import LoginView, SignupView
from .mixins import TurnstileMixin
from typing import Dict, Any, Optional, Union, cast, TYPE_CHECKING
from typing import Dict, Any, Optional, Union, cast
from django_htmx.http import HttpResponseClientRefresh
from contextlib import suppress
import re

View File

@@ -0,0 +1,43 @@
# A generic Alembic configuration file.
[alembic]
# path to migration scripts
script_location = alembic
# The database URL is now set dynamically by ConPort's run_migrations function.
# sqlalchemy.url = sqlite:///your_database.db
# ... other Alembic settings ...
[loggers]
keys = root,sqlalchemy,alembic
[handlers]
keys = console
[formatters]
keys = generic
[logger_root]
level = WARN
handlers = console
qualname =
[logger_sqlalchemy]
level = WARN
handlers =
qualname = sqlalchemy.engine
[logger_alembic]
level = INFO
handlers =
qualname = alembic
[handler_console]
class = StreamHandler
args = (sys.stderr,)
level = NOTSET
formatter = generic
[formatter_generic]
format = %(levelname)-5.5s [%(name)s] %(message)s
datefmt = %H:%M:%S

View File

@@ -0,0 +1,76 @@
from logging.config import fileConfig
from sqlalchemy import engine_from_config
from sqlalchemy import pool
from alembic import context
# this is the Alembic Config object, which provides
# access to the values within the .ini file in use.
config = context.config
# Interpret the config file for Python logging.
# This line prevents the need to have a separate logging config file.
if config.config_file_name is not None:
fileConfig(config.config_file_name)
# add your model's MetaData object here
# for 'autogenerate' support
# from myapp import mymodel
# target_metadata = mymodel.Base.metadata
target_metadata = None
# other values from the config, defined by the needs of env.py,
# can be acquired:
# my_important_option = config.get_main_option("my_important_option")
# ... etc.
def run_migrations_offline() -> None:
"""Run migrations in 'offline' mode.
This configures the context with just a URL
and not an Engine, though an Engine is acceptable
here as well. By skipping the Engine creation
we don't even need a DBAPI to be available.
Calls to context.execute() here emit the given string to the
script output.
"""
url = config.get_main_option("sqlalchemy.url")
context.configure(
url=url,
target_metadata=target_metadata,
literal_binds=True,
dialect_opts={"paramstyle": "named"},
)
with context.begin_transaction():
context.run_migrations()
def run_migrations_online() -> None:
"""Run migrations in 'online' mode.
In this scenario we need to create an Engine
and associate a connection with the context.
"""
connectable = engine_from_config(
config.get_section(config.config_ini_section, {}),
prefix="sqlalchemy.",
poolclass=pool.NullPool,
)
with connectable.connect() as connection:
context.configure(connection=connection, target_metadata=target_metadata)
with context.begin_transaction():
context.run_migrations()
if context.is_offline_mode():
run_migrations_offline()
else:
run_migrations_online()

View File

@@ -0,0 +1,247 @@
"""Initial schema
Revision ID: 20250617
Revises:
Create Date: 2025-06-17 15:00:00.000000
"""
from alembic import op
import sqlalchemy as sa
import json
# revision identifiers, used by Alembic.
revision = "20250617"
down_revision = None
branch_labels = None
depends_on = None
def upgrade() -> None:
# ### commands auto-generated by Alembic - please adjust! ###
op.create_table(
"active_context",
sa.Column("id", sa.Integer(), nullable=False),
sa.Column("content", sa.Text(), nullable=False),
sa.PrimaryKeyConstraint("id"),
)
op.create_table(
"active_context_history",
sa.Column("id", sa.Integer(), nullable=False),
sa.Column("timestamp", sa.DateTime(), nullable=False),
sa.Column("version", sa.Integer(), nullable=False),
sa.Column("content", sa.Text(), nullable=False),
sa.Column("change_source", sa.String(length=255), nullable=True),
sa.PrimaryKeyConstraint("id"),
)
op.create_table(
"context_links",
sa.Column("id", sa.Integer(), nullable=False),
sa.Column("workspace_id", sa.String(length=1024), nullable=False),
sa.Column("source_item_type", sa.String(length=255), nullable=False),
sa.Column("source_item_id", sa.String(length=255), nullable=False),
sa.Column("target_item_type", sa.String(length=255), nullable=False),
sa.Column("target_item_id", sa.String(length=255), nullable=False),
sa.Column("relationship_type", sa.String(length=255), nullable=False),
sa.Column("description", sa.Text(), nullable=True),
sa.Column(
"timestamp",
sa.DateTime(),
server_default=sa.text("(CURRENT_TIMESTAMP)"),
nullable=False,
),
sa.PrimaryKeyConstraint("id"),
)
op.create_index(
op.f("ix_context_links_source_item_id"),
"context_links",
["source_item_id"],
unique=False,
)
op.create_index(
op.f("ix_context_links_source_item_type"),
"context_links",
["source_item_type"],
unique=False,
)
op.create_index(
op.f("ix_context_links_target_item_id"),
"context_links",
["target_item_id"],
unique=False,
)
op.create_index(
op.f("ix_context_links_target_item_type"),
"context_links",
["target_item_type"],
unique=False,
)
op.create_table(
"custom_data",
sa.Column("id", sa.Integer(), nullable=False),
sa.Column("timestamp", sa.DateTime(), nullable=False),
sa.Column("category", sa.String(length=255), nullable=False),
sa.Column("key", sa.String(length=255), nullable=False),
sa.Column("value", sa.Text(), nullable=False),
sa.PrimaryKeyConstraint("id"),
sa.UniqueConstraint("category", "key"),
)
op.create_table(
"decisions",
sa.Column("id", sa.Integer(), nullable=False),
sa.Column("timestamp", sa.DateTime(), nullable=False),
sa.Column("summary", sa.Text(), nullable=False),
sa.Column("rationale", sa.Text(), nullable=True),
sa.Column("implementation_details", sa.Text(), nullable=True),
sa.Column("tags", sa.Text(), nullable=True),
sa.PrimaryKeyConstraint("id"),
)
op.create_table(
"product_context",
sa.Column("id", sa.Integer(), nullable=False),
sa.Column("content", sa.Text(), nullable=False),
sa.PrimaryKeyConstraint("id"),
)
op.create_table(
"product_context_history",
sa.Column("id", sa.Integer(), nullable=False),
sa.Column("timestamp", sa.DateTime(), nullable=False),
sa.Column("version", sa.Integer(), nullable=False),
sa.Column("content", sa.Text(), nullable=False),
sa.Column("change_source", sa.String(length=255), nullable=True),
sa.PrimaryKeyConstraint("id"),
)
op.create_table(
"progress_entries",
sa.Column("id", sa.Integer(), nullable=False),
sa.Column("timestamp", sa.DateTime(), nullable=False),
sa.Column("status", sa.String(length=50), nullable=False),
sa.Column("description", sa.Text(), nullable=False),
sa.Column("parent_id", sa.Integer(), nullable=True),
sa.ForeignKeyConstraint(
["parent_id"], ["progress_entries.id"], ondelete="SET NULL"
),
sa.PrimaryKeyConstraint("id"),
)
op.create_table(
"system_patterns",
sa.Column("id", sa.Integer(), nullable=False),
sa.Column("timestamp", sa.DateTime(), nullable=False),
sa.Column("name", sa.String(length=255), nullable=False),
sa.Column("description", sa.Text(), nullable=True),
sa.Column("tags", sa.Text(), nullable=True),
sa.PrimaryKeyConstraint("id"),
sa.UniqueConstraint("name"),
)
# Seed initial data
op.execute("INSERT INTO product_context (id, content) VALUES (1, '{}')")
op.execute("INSERT INTO active_context (id, content) VALUES (1, '{}')")
# Create FTS5 virtual table for decisions
op.execute(
"""
CREATE VIRTUAL TABLE decisions_fts USING fts5(
summary,
rationale,
implementation_details,
tags,
content="decisions",
content_rowid="id"
);
"""
)
# Create triggers to keep the FTS table in sync with the decisions table
op.execute(
"""
CREATE TRIGGER decisions_after_insert AFTER INSERT ON decisions
BEGIN
INSERT INTO decisions_fts (rowid, summary, rationale, implementation_details, tags)
VALUES (new.id, new.summary, new.rationale, new.implementation_details, new.tags);
END;
"""
)
op.execute(
"""
CREATE TRIGGER decisions_after_delete AFTER DELETE ON decisions
BEGIN
INSERT INTO decisions_fts (decisions_fts, rowid, summary, rationale, implementation_details, tags)
VALUES ('delete', old.id, old.summary, old.rationale, old.implementation_details, old.tags);
END;
"""
)
op.execute(
"""
CREATE TRIGGER decisions_after_update AFTER UPDATE ON decisions
BEGIN
INSERT INTO decisions_fts (decisions_fts, rowid, summary, rationale, implementation_details, tags)
VALUES ('delete', old.id, old.summary, old.rationale, old.implementation_details, old.tags);
INSERT INTO decisions_fts (rowid, summary, rationale, implementation_details, tags)
VALUES (new.id, new.summary, new.rationale, new.implementation_details, new.tags);
END;
"""
)
# Create FTS5 virtual table for custom_data
op.execute(
"""
CREATE VIRTUAL TABLE custom_data_fts USING fts5(
category,
key,
value_text,
content="custom_data",
content_rowid="id"
);
"""
)
# Create triggers for custom_data_fts
op.execute(
"""
CREATE TRIGGER custom_data_after_insert AFTER INSERT ON custom_data
BEGIN
INSERT INTO custom_data_fts (rowid, category, key, value_text)
VALUES (new.id, new.category, new.key, new.value);
END;
"""
)
op.execute(
"""
CREATE TRIGGER custom_data_after_delete AFTER DELETE ON custom_data
BEGIN
INSERT INTO custom_data_fts (custom_data_fts, rowid, category, key, value_text)
VALUES ('delete', old.id, old.category, old.key, old.value);
END;
"""
)
op.execute(
"""
CREATE TRIGGER custom_data_after_update AFTER UPDATE ON custom_data
BEGIN
INSERT INTO custom_data_fts (custom_data_fts, rowid, category, key, value_text)
VALUES ('delete', old.id, old.category, old.key, old.value);
INSERT INTO custom_data_fts (rowid, category, key, value_text)
VALUES (new.id, new.category, new.key, new.value);
END;
"""
)
# ### end Alembic commands ###
def downgrade() -> None:
# ### commands auto-generated by Alembic - please adjust! ###
op.drop_table("system_patterns")
op.drop_table("progress_entries")
op.drop_table("product_context_history")
op.drop_table("product_context")
op.drop_table("decisions")
op.drop_table("custom_data")
op.drop_index(op.f("ix_context_links_target_item_type"), table_name="context_links")
op.drop_index(op.f("ix_context_links_target_item_id"), table_name="context_links")
op.drop_index(op.f("ix_context_links_source_item_type"), table_name="context_links")
op.drop_index(op.f("ix_context_links_source_item_id"), table_name="context_links")
op.drop_table("context_links")
op.drop_table("active_context_history")
op.drop_table("active_context")
# ### end Alembic commands ###

Binary file not shown.

View File

@@ -12,6 +12,7 @@ class SlugHistoryAdmin(admin.ModelAdmin):
date_hierarchy = "created_at"
ordering = ["-created_at"]
@admin.display(description="Object")
def content_object_link(self, obj):
"""Create a link to the related object's admin page"""
try:
@@ -20,8 +21,6 @@ class SlugHistoryAdmin(admin.ModelAdmin):
except (AttributeError, ValueError):
return str(obj.content_object)
content_object_link.short_description = "Object"
def has_add_permission(self, request):
"""Disable manual creation of slug history records"""
return False

View File

@@ -3,4 +3,4 @@ from django.apps import AppConfig
class CoreConfig(AppConfig):
default_auto_field = "django.db.models.BigAutoField"
name = "core"
name = "apps.core"

View File

@@ -9,7 +9,7 @@ from functools import wraps
from typing import Optional, List, Callable
from django.utils.decorators import method_decorator
from django.views.decorators.vary import vary_on_headers
from core.services.enhanced_cache_service import EnhancedCacheService
from apps.core.services.enhanced_cache_service import EnhancedCacheService
import logging
logger = logging.getLogger(__name__)

View File

@@ -167,7 +167,7 @@ class ApplicationHealthCheck(BaseHealthCheckBackend):
# Check if we can access critical models
try:
from parks.models import Park
from rides.models import Ride
from apps.rides.models import Ride
from django.contrib.auth import get_user_model
User = get_user_model()

View File

@@ -97,6 +97,7 @@ class HistoricalSlug(models.Model):
)
class Meta:
app_label = "core"
unique_together = ("content_type", "slug")
indexes = [
models.Index(fields=["content_type", "object_id"]),

View File

@@ -0,0 +1 @@
# Django management commands

View File

@@ -0,0 +1 @@
# Django management commands

View File

@@ -0,0 +1,101 @@
"""
Django management command to run the development server.
This command automatically sets up the development environment and starts
the server, replacing the need for the dev_server.sh script.
"""
import subprocess
import sys
from django.core.management.base import BaseCommand
from django.core.management import execute_from_command_line
class Command(BaseCommand):
help = "Run the development server with automatic setup"
def add_arguments(self, parser):
parser.add_argument(
"--port",
type=str,
default="8000",
help="Port to run the server on (default: 8000)",
)
parser.add_argument(
"--host",
type=str,
default="0.0.0.0",
help="Host to bind the server to (default: 0.0.0.0)",
)
parser.add_argument(
"--skip-setup",
action="store_true",
help="Skip the development setup and go straight to running the server",
)
parser.add_argument(
"--use-runserver-plus",
action="store_true",
help="Use runserver_plus if available (from django-extensions)",
)
def handle(self, *args, **options):
"""Run the development setup and start the server."""
if not options["skip_setup"]:
self.stdout.write(
self.style.SUCCESS(
"🚀 Setting up and starting ThrillWiki Development Server..."
)
)
# Run the setup_dev command first
execute_from_command_line(["manage.py", "setup_dev"])
else:
self.stdout.write(
self.style.SUCCESS("🚀 Starting ThrillWiki Development Server...")
)
# Determine which server command to use
server_command = self.get_server_command(options)
# Start the server
self.stdout.write("")
self.stdout.write(
self.style.SUCCESS(
f'🌟 Starting Django development server on http://{options["host"]}:{options["port"]}'
)
)
self.stdout.write("Press Ctrl+C to stop the server")
self.stdout.write("")
try:
if options["use_runserver_plus"] or self.has_runserver_plus():
execute_from_command_line(
[
"manage.py",
"runserver_plus",
f'{options["host"]}:{options["port"]}',
]
)
else:
execute_from_command_line(
["manage.py", "runserver", f'{options["host"]}:{options["port"]}']
)
except KeyboardInterrupt:
self.stdout.write("")
self.stdout.write(self.style.SUCCESS("👋 Development server stopped"))
def get_server_command(self, options):
"""Determine which server command to use."""
if options["use_runserver_plus"] or self.has_runserver_plus():
return "runserver_plus"
return "runserver"
def has_runserver_plus(self):
"""Check if runserver_plus is available (django-extensions)."""
try:
import django_extensions
return True
except ImportError:
return False

View File

@@ -0,0 +1,226 @@
"""
Django management command to set up the development environment.
This command performs all the setup tasks that the dev_server.sh script does,
allowing the project to run without requiring the shell script.
"""
import os
import subprocess
import sys
from pathlib import Path
from django.core.management.base import BaseCommand
from django.core.management import execute_from_command_line
from django.conf import settings
class Command(BaseCommand):
help = "Set up the development environment"
def add_arguments(self, parser):
parser.add_argument(
"--skip-migrations",
action="store_true",
help="Skip running database migrations",
)
parser.add_argument(
"--skip-static",
action="store_true",
help="Skip collecting static files",
)
parser.add_argument(
"--skip-tailwind",
action="store_true",
help="Skip building Tailwind CSS",
)
parser.add_argument(
"--skip-superuser",
action="store_true",
help="Skip creating development superuser",
)
def handle(self, *args, **options):
"""Run the development setup process."""
self.stdout.write(
self.style.SUCCESS("🚀 Setting up ThrillWiki Development Environment...")
)
# Create necessary directories
self.create_directories()
# Run database migrations if needed
if not options["skip_migrations"]:
self.run_migrations()
# Seed sample data
self.seed_sample_data()
# Create superuser if it doesn't exist
if not options["skip_superuser"]:
self.create_superuser()
# Collect static files
if not options["skip_static"]:
self.collect_static()
# Build Tailwind CSS
if not options["skip_tailwind"]:
self.build_tailwind()
# Run system checks
self.run_system_checks()
# Display environment info
self.display_environment_info()
self.stdout.write(
self.style.SUCCESS("✅ Development environment setup complete!")
)
def create_directories(self):
"""Create necessary directories."""
self.stdout.write("📁 Creating necessary directories...")
directories = ["logs", "profiles", "media", "staticfiles", "static/css"]
for directory in directories:
dir_path = Path(settings.BASE_DIR) / directory
dir_path.mkdir(parents=True, exist_ok=True)
self.stdout.write(self.style.SUCCESS("✅ Directories created"))
def run_migrations(self):
"""Run database migrations if needed."""
self.stdout.write("🗄️ Checking database migrations...")
try:
# Check if migrations are up to date
result = subprocess.run(
[sys.executable, "manage.py", "migrate", "--check"],
capture_output=True,
text=True,
)
if result.returncode == 0:
self.stdout.write(
self.style.SUCCESS("✅ Database migrations are up to date")
)
else:
self.stdout.write("🔄 Running database migrations...")
subprocess.run(
[sys.executable, "manage.py", "migrate", "--noinput"], check=True
)
self.stdout.write(
self.style.SUCCESS("✅ Database migrations completed")
)
except subprocess.CalledProcessError as e:
self.stdout.write(
self.style.WARNING(f"⚠️ Migration error (continuing): {e}")
)
def seed_sample_data(self):
"""Seed sample data to the database."""
self.stdout.write("🌱 Seeding sample data...")
try:
subprocess.run(
[sys.executable, "manage.py", "seed_sample_data"], check=True
)
self.stdout.write(self.style.SUCCESS("✅ Sample data seeded"))
except subprocess.CalledProcessError:
self.stdout.write(
self.style.WARNING("⚠️ Could not seed sample data (continuing)")
)
def create_superuser(self):
"""Create development superuser if it doesn't exist."""
self.stdout.write("👤 Checking for superuser...")
try:
from django.contrib.auth import get_user_model
User = get_user_model()
if User.objects.filter(is_superuser=True).exists():
self.stdout.write(self.style.SUCCESS("✅ Superuser already exists"))
else:
self.stdout.write("👤 Creating development superuser (admin/admin)...")
if not User.objects.filter(username="admin").exists():
User.objects.create_superuser("admin", "admin@example.com", "admin")
self.stdout.write(
self.style.SUCCESS("✅ Created superuser: admin/admin")
)
else:
self.stdout.write(
self.style.SUCCESS("✅ Admin user already exists")
)
except Exception as e:
self.stdout.write(self.style.WARNING(f"⚠️ Could not create superuser: {e}"))
def collect_static(self):
"""Collect static files for development."""
self.stdout.write("📦 Collecting static files...")
try:
subprocess.run(
[sys.executable, "manage.py", "collectstatic", "--noinput", "--clear"],
check=True,
)
self.stdout.write(self.style.SUCCESS("✅ Static files collected"))
except subprocess.CalledProcessError as e:
self.stdout.write(
self.style.WARNING(f"⚠️ Could not collect static files: {e}")
)
def build_tailwind(self):
"""Build Tailwind CSS if npm is available."""
self.stdout.write("🎨 Building Tailwind CSS...")
try:
# Check if npm is available
subprocess.run(["npm", "--version"], capture_output=True, check=True)
# Build Tailwind CSS
subprocess.run(
[sys.executable, "manage.py", "tailwind", "build"], check=True
)
self.stdout.write(self.style.SUCCESS("✅ Tailwind CSS built"))
except (subprocess.CalledProcessError, FileNotFoundError):
self.stdout.write(
self.style.WARNING(
"⚠️ npm not found or Tailwind build failed, skipping"
)
)
def run_system_checks(self):
"""Run Django system checks."""
self.stdout.write("🔍 Running system checks...")
try:
subprocess.run([sys.executable, "manage.py", "check"], check=True)
self.stdout.write(self.style.SUCCESS("✅ System checks passed"))
except subprocess.CalledProcessError:
self.stdout.write(
self.style.WARNING("❌ System checks failed, but continuing...")
)
def display_environment_info(self):
"""Display development environment information."""
self.stdout.write("")
self.stdout.write(self.style.SUCCESS("🌍 Development Environment:"))
self.stdout.write(f" - Settings Module: {settings.SETTINGS_MODULE}")
self.stdout.write(f" - Debug Mode: {settings.DEBUG}")
self.stdout.write(" - Database: PostgreSQL with PostGIS")
self.stdout.write(" - Cache: Local memory cache")
self.stdout.write(" - Admin URL: http://localhost:8000/admin/")
self.stdout.write(" - Admin User: admin / admin")
self.stdout.write(" - Silk Profiler: http://localhost:8000/silk/")
self.stdout.write(" - Debug Toolbar: Available on debug pages")
self.stdout.write(" - API Documentation: http://localhost:8000/api/docs/")
self.stdout.write("")
self.stdout.write("🌟 Ready to start development server with:")
self.stdout.write(" python manage.py runserver")
self.stdout.write("")

View File

@@ -1,8 +1,8 @@
from django.core.management.base import BaseCommand
from django.core.cache import cache
from parks.models import Park
from rides.models import Ride
from core.analytics import PageView
from apps.parks.models import Park
from apps.rides.models import Ride
from apps.core.analytics import PageView
class Command(BaseCommand):

View File

@@ -8,7 +8,7 @@ from django.core.handlers.wsgi import WSGIRequest
from django.utils.deprecation import MiddlewareMixin
from django.contrib.contenttypes.models import ContentType
from django.views.generic.detail import DetailView
from core.analytics import PageView
from apps.core.analytics import PageView
class RequestContextProvider(pghistory.context):

View File

@@ -2,7 +2,7 @@ from django.db import models
from django.contrib.contenttypes.fields import GenericForeignKey
from django.contrib.contenttypes.models import ContentType
from django.utils.text import slugify
from core.history import TrackedModel
from apps.core.history import TrackedModel
class SlugHistory(models.Model):

View File

@@ -11,8 +11,8 @@ from django.utils import timezone
from datetime import timedelta
from .analytics import PageView
from parks.models import Park
from rides.models import Ride
from apps.parks.models import Park
from apps.rides.models import Ride
def unified_locations_for_map(

View File

@@ -13,9 +13,9 @@ from .data_structures import (
GeoBounds,
MapFilters,
)
from parks.models import ParkLocation, CompanyHeadquarters
from rides.models import RideLocation
from location.models import Location
from apps.parks.models import ParkLocation, CompanyHeadquarters
from apps.rides.models import RideLocation
from apps.location.models import Location
class BaseLocationAdapter:

View File

@@ -12,8 +12,8 @@ from django.db.models import Q
from typing import Optional, List, Dict, Any, Set
from dataclasses import dataclass
from parks.models import Park, Company, ParkLocation
from rides.models import Ride
from apps.parks.models import Park, Company, ParkLocation
from apps.rides.models import Ride
@dataclass

View File

@@ -1,11 +1,11 @@
from django.urls import path
from core.views.search import (
from apps.core.views.search import (
AdaptiveSearchView,
FilterFormView,
LocationSearchView,
LocationSuggestionsView,
)
from rides.views import RideSearchView
from apps.rides.views import RideSearchView
app_name = "search"

View File

@@ -11,8 +11,8 @@ from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.permissions import AllowAny
from health_check.views import MainView
from core.services.enhanced_cache_service import CacheMonitor
from core.utils.query_optimization import IndexAnalyzer
from apps.core.services.enhanced_cache_service import CacheMonitor
from apps.core.utils.query_optimization import IndexAnalyzer
class HealthCheckAPIView(APIView):

Some files were not shown because too many files have changed in this diff Show More