mirror of
https://github.com/pacnpal/thrillwiki_django_no_react.git
synced 2025-12-30 00:07:03 -05:00
Compare commits
3 Commits
652ea149bd
...
02c7cbd1cd
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
02c7cbd1cd | ||
|
|
d504d41de2 | ||
|
|
b0e0678590 |
443
.gitignore
vendored
443
.gitignore
vendored
@@ -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/
|
||||
|
||||
443
README.md
443
README.md
@@ -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
|
||||
|
||||
# Install backend dependencies
|
||||
cd backend && uv sync
|
||||
```
|
||||
|
||||
# Connect to PostgreSQL and setup
|
||||
psql postgres
|
||||
```
|
||||
3. **Start development servers**
|
||||
```bash
|
||||
# Start both frontend and backend
|
||||
pnpm run dev
|
||||
|
||||
# Or start individually
|
||||
pnpm run dev:frontend # Vue.js on :3000
|
||||
pnpm run dev:backend # Django on :8000
|
||||
```
|
||||
|
||||
In the PostgreSQL shell:
|
||||
```sql
|
||||
-- Set password for wiki user
|
||||
ALTER USER wiki WITH PASSWORD 'thrillwiki';
|
||||
## 📁 Project Structure
|
||||
|
||||
-- Grant privileges
|
||||
GRANT ALL PRIVILEGES ON DATABASE thrillwiki TO wiki;
|
||||
### Backend (`/backend`)
|
||||
- **Django REST API** with modular app architecture
|
||||
- **UV package management** for Python dependencies
|
||||
- **PostgreSQL** database (configurable)
|
||||
- **Redis** for caching and sessions
|
||||
|
||||
-- Enable PostGIS extension
|
||||
\c thrillwiki
|
||||
CREATE EXTENSION postgis;
|
||||
\q
|
||||
```
|
||||
### 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
|
||||
|
||||
### 3. Environment Configuration
|
||||
|
||||
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",
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**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.
|
||||
|
||||
372
architecture/architecture-validation.md
Normal file
372
architecture/architecture-validation.md
Normal 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.
|
||||
628
architecture/deployment-guide.md
Normal file
628
architecture/deployment-guide.md
Normal 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.
|
||||
353
architecture/migration-mapping.md
Normal file
353
architecture/migration-mapping.md
Normal 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
|
||||
525
architecture/monorepo-structure-plan.md
Normal file
525
architecture/monorepo-structure-plan.md
Normal 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
31
backend/.env.example
Normal 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
229
backend/README.md
Normal 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
6
backend/apps/__init__.py
Normal 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.
|
||||
"""
|
||||
@@ -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
|
||||
@@ -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()
|
||||
|
||||
@@ -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):
|
||||
@@ -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):
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
43
backend/apps/context_portal/alembic.ini
Normal file
43
backend/apps/context_portal/alembic.ini
Normal 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
|
||||
76
backend/apps/context_portal/alembic/env.py
Normal file
76
backend/apps/context_portal/alembic/env.py
Normal 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()
|
||||
@@ -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 ###
|
||||
BIN
backend/apps/context_portal/context.db
Normal file
BIN
backend/apps/context_portal/context.db
Normal file
Binary file not shown.
@@ -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
|
||||
@@ -3,4 +3,4 @@ from django.apps import AppConfig
|
||||
|
||||
class CoreConfig(AppConfig):
|
||||
default_auto_field = "django.db.models.BigAutoField"
|
||||
name = "core"
|
||||
name = "apps.core"
|
||||
@@ -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__)
|
||||
@@ -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()
|
||||
@@ -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"]),
|
||||
1
backend/apps/core/management/__init__.py
Normal file
1
backend/apps/core/management/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
# Django management commands
|
||||
1
backend/apps/core/management/commands/__init__.py
Normal file
1
backend/apps/core/management/commands/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
# Django management commands
|
||||
101
backend/apps/core/management/commands/rundev.py
Normal file
101
backend/apps/core/management/commands/rundev.py
Normal 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
|
||||
226
backend/apps/core/management/commands/setup_dev.py
Normal file
226
backend/apps/core/management/commands/setup_dev.py
Normal 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("")
|
||||
@@ -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):
|
||||
@@ -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):
|
||||
@@ -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):
|
||||
@@ -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(
|
||||
@@ -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:
|
||||
@@ -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
|
||||
@@ -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"
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user