mirror of
https://github.com/pacnpal/thrillwiki_django_no_react.git
synced 2025-12-30 05:27:01 -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
|
# Python
|
||||||
/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
|
|
||||||
__pycache__/
|
__pycache__/
|
||||||
*.py[cod]
|
*.py[cod]
|
||||||
*$py.class
|
*$py.class
|
||||||
|
|
||||||
# C extensions
|
|
||||||
*.so
|
*.so
|
||||||
|
|
||||||
# Distribution / packaging
|
|
||||||
.Python
|
.Python
|
||||||
build/
|
build/
|
||||||
develop-eggs/
|
develop-eggs/
|
||||||
@@ -212,189 +22,96 @@ share/python-wheels/
|
|||||||
*.egg
|
*.egg
|
||||||
MANIFEST
|
MANIFEST
|
||||||
|
|
||||||
# PyInstaller
|
# Django
|
||||||
# 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:
|
|
||||||
*.log
|
*.log
|
||||||
local_settings.py
|
local_settings.py
|
||||||
db.sqlite3
|
db.sqlite3
|
||||||
db.sqlite3-journal
|
db.sqlite3-journal
|
||||||
|
/backend/staticfiles/
|
||||||
|
/backend/media/
|
||||||
|
|
||||||
# Flask stuff:
|
# UV
|
||||||
instance/
|
.uv/
|
||||||
.webassets-cache
|
backend/.uv/
|
||||||
|
|
||||||
# Scrapy stuff:
|
# Node.js
|
||||||
.scrapy
|
node_modules/
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
pnpm-debug.log*
|
||||||
|
lerna-debug.log*
|
||||||
|
.pnpm-store/
|
||||||
|
|
||||||
# Sphinx documentation
|
# Vue.js / Vite
|
||||||
docs/_build/
|
/frontend/dist/
|
||||||
|
/frontend/dist-ssr/
|
||||||
|
*.local
|
||||||
|
|
||||||
# PyBuilder
|
# Environment variables
|
||||||
.pybuilder/
|
.env
|
||||||
target/
|
.env.local
|
||||||
|
.env.development.local
|
||||||
|
.env.test.local
|
||||||
|
.env.production.local
|
||||||
|
backend/.env
|
||||||
|
frontend/.env
|
||||||
|
|
||||||
# Jupyter Notebook
|
# IDEs
|
||||||
.ipynb_checkpoints
|
.vscode/
|
||||||
|
.idea/
|
||||||
|
*.swp
|
||||||
|
*.swo
|
||||||
|
*.sublime-project
|
||||||
|
*.sublime-workspace
|
||||||
|
|
||||||
# IPython
|
# OS
|
||||||
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
|
|
||||||
.DS_Store
|
.DS_Store
|
||||||
.AppleDouble
|
Thumbs.db
|
||||||
.LSOverride
|
Desktop.ini
|
||||||
|
|
||||||
# Icon must end with two \r
|
# Logs
|
||||||
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
|
*.log
|
||||||
.thrillwiki-github-token
|
|
||||||
.thrillwiki-template-config
|
|
||||||
|
|
||||||
# Environment files with potential secrets
|
# Coverage
|
||||||
scripts/systemd/thrillwiki-automation***REMOVED***
|
coverage/
|
||||||
scripts/systemd/thrillwiki-deployment***REMOVED***
|
*.lcov
|
||||||
scripts/systemd/****REMOVED***
|
.nyc_output
|
||||||
logs/
|
htmlcov/
|
||||||
profiles/
|
.coverage
|
||||||
uv.lock
|
.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)
|
This project uses a monorepo structure that cleanly separates backend and frontend concerns:
|
||||||
- **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
|
|
||||||
|
|
||||||
## 📋 Prerequisites
|
```
|
||||||
|
thrillwiki-monorepo/
|
||||||
### Required Software
|
├── backend/ # Django REST API
|
||||||
|
├── frontend/ # Vue.js SPA
|
||||||
1. **Python 3.11+**
|
└── shared/ # Shared resources and documentation
|
||||||
```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
|
|
||||||
```
|
|
||||||
|
|
||||||
## 🚀 Quick Start
|
## 🚀 Quick Start
|
||||||
|
|
||||||
### 1. Clone and Setup Project
|
### Prerequisites
|
||||||
|
|
||||||
```bash
|
- **Python 3.11+** with [uv](https://docs.astral.sh/uv/) for backend dependencies
|
||||||
# Clone the repository
|
- **Node.js 18+** with [pnpm](https://pnpm.io/) for frontend dependencies
|
||||||
git clone <repository-url>
|
|
||||||
cd thrillwiki_django_no_react
|
|
||||||
|
|
||||||
# Install Python dependencies using UV
|
### Development Setup
|
||||||
uv sync
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2. Database Setup
|
1. **Clone the repository**
|
||||||
|
```bash
|
||||||
|
git clone <repository-url>
|
||||||
|
cd thrillwiki-monorepo
|
||||||
|
```
|
||||||
|
|
||||||
```bash
|
2. **Install dependencies**
|
||||||
# Create PostgreSQL database and user
|
```bash
|
||||||
createdb thrillwiki
|
# Install frontend dependencies
|
||||||
createuser wiki
|
pnpm install
|
||||||
|
|
||||||
|
# Install backend dependencies
|
||||||
|
cd backend && uv sync
|
||||||
|
```
|
||||||
|
|
||||||
# Connect to PostgreSQL and setup
|
3. **Start development servers**
|
||||||
psql postgres
|
```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:
|
## 📁 Project Structure
|
||||||
```sql
|
|
||||||
-- Set password for wiki user
|
|
||||||
ALTER USER wiki WITH PASSWORD 'thrillwiki';
|
|
||||||
|
|
||||||
-- Grant privileges
|
### Backend (`/backend`)
|
||||||
GRANT ALL PRIVILEGES ON DATABASE thrillwiki TO wiki;
|
- **Django REST API** with modular app architecture
|
||||||
|
- **UV package management** for Python dependencies
|
||||||
|
- **PostgreSQL** database (configurable)
|
||||||
|
- **Redis** for caching and sessions
|
||||||
|
|
||||||
-- Enable PostGIS extension
|
### Frontend (`/frontend`)
|
||||||
\c thrillwiki
|
- **Vue 3** with Composition API
|
||||||
CREATE EXTENSION postgis;
|
- **TypeScript** for type safety
|
||||||
\q
|
- **Vite** for fast development and building
|
||||||
```
|
- **Tailwind CSS** for styling
|
||||||
|
- **Pinia** for state management
|
||||||
|
|
||||||
### 3. Environment Configuration
|
### Shared (`/shared`)
|
||||||
|
- Documentation and deployment guides
|
||||||
The project uses these database settings (configured in [`thrillwiki/settings.py`](thrillwiki/settings.py)):
|
- Shared TypeScript types
|
||||||
```python
|
- Build and deployment scripts
|
||||||
DATABASES = {
|
- Docker configurations
|
||||||
"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
|
|
||||||
|
|
||||||
## 🛠️ Development Workflow
|
## 🛠️ Development Workflow
|
||||||
|
|
||||||
### Package Management
|
### Available Scripts
|
||||||
|
|
||||||
**ALWAYS use UV for package management**:
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Add new Python packages
|
# Development
|
||||||
uv add <package-name>
|
pnpm run dev # Start both servers
|
||||||
|
pnpm run dev:frontend # Frontend only
|
||||||
|
pnpm run dev:backend # Backend only
|
||||||
|
|
||||||
# Add development dependencies
|
# Building
|
||||||
uv add --dev <package-name>
|
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
|
### Backend Commands
|
||||||
|
|
||||||
**ALWAYS use UV for Django commands**:
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Correct way to run Django commands
|
cd backend
|
||||||
uv run manage.py <command>
|
|
||||||
|
|
||||||
# Examples:
|
# Django management
|
||||||
uv run manage.py makemigrations
|
|
||||||
uv run manage.py migrate
|
uv run manage.py migrate
|
||||||
uv run manage.py shell
|
|
||||||
uv run manage.py createsuperuser
|
uv run manage.py createsuperuser
|
||||||
uv run manage.py collectstatic
|
uv run manage.py collectstatic
|
||||||
|
|
||||||
# NEVER use these patterns:
|
# Testing
|
||||||
# python manage.py <command> ❌ Wrong
|
uv run manage.py test
|
||||||
# uv run python manage.py <command> ❌ Wrong
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### CSS Development
|
## 🔧 Configuration
|
||||||
|
|
||||||
The project uses **Tailwind CSS v4** with a custom dark theme. CSS files are located in:
|
### Environment Variables
|
||||||
- Source: [`static/css/src/input.css`](static/css/src/input.css)
|
|
||||||
- Compiled: [`static/css/`](static/css/) (auto-generated)
|
|
||||||
|
|
||||||
Tailwind automatically compiles when using the `tailwind runserver` command.
|
Create `.env` files for local development:
|
||||||
|
|
||||||
#### 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
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Run Python tests
|
# Root .env (shared settings)
|
||||||
uv run pytest
|
DATABASE_URL=postgresql://user:pass@localhost/thrillwiki
|
||||||
|
REDIS_URL=redis://localhost:6379
|
||||||
|
SECRET_KEY=your-secret-key
|
||||||
|
|
||||||
# Run with coverage
|
# Backend .env
|
||||||
uv run coverage run -m pytest
|
DJANGO_SETTINGS_MODULE=config.django.local
|
||||||
uv run coverage report
|
DEBUG=True
|
||||||
|
|
||||||
# Run E2E tests with Playwright
|
# Frontend .env
|
||||||
uv run pytest tests/e2e/
|
VITE_API_BASE_URL=http://localhost:8000/api
|
||||||
```
|
```
|
||||||
|
|
||||||
### Test Structure
|
## 📖 Documentation
|
||||||
- Unit tests: Located within each app's `tests/` directory
|
|
||||||
- E2E tests: [`tests/e2e/`](tests/e2e/)
|
|
||||||
- Test fixtures: [`tests/fixtures/`](tests/fixtures/)
|
|
||||||
|
|
||||||
## 📚 Documentation
|
- [Backend Documentation](./backend/README.md)
|
||||||
|
- [Frontend Documentation](./frontend/README.md)
|
||||||
|
- [Deployment Guide](./shared/docs/deployment/)
|
||||||
|
- [API Documentation](./shared/docs/api/)
|
||||||
|
|
||||||
### Memory Bank System
|
## 🚀 Deployment
|
||||||
The project uses a comprehensive documentation system in [`memory-bank/`](memory-bank/):
|
|
||||||
|
|
||||||
- [`memory-bank/activeContext.md`](memory-bank/activeContext.md) - Current development context
|
See [Deployment Guide](./shared/docs/deployment/) for production setup instructions.
|
||||||
- [`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
|
|
||||||
|
|
||||||
### Key Documentation Files
|
## 🤝 Contributing
|
||||||
- [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
|
|
||||||
|
|
||||||
## 🚨 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
|
## 📄 License
|
||||||
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
|
|
||||||
```
|
|
||||||
|
|
||||||
2. **Package Management**: Only use UV:
|
This project is licensed under the MIT License.
|
||||||
```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.
|
|
||||||
|
|||||||
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):
|
class AccountsConfig(AppConfig):
|
||||||
default_auto_field = "django.db.models.BigAutoField"
|
default_auto_field = "django.db.models.BigAutoField"
|
||||||
name = "accounts"
|
name = "apps.accounts"
|
||||||
|
|
||||||
def ready(self):
|
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.core.management.base import BaseCommand
|
||||||
from django.contrib.auth import get_user_model
|
from django.contrib.auth import get_user_model
|
||||||
from parks.models import ParkReview, Park
|
from apps.parks.models import ParkReview, Park
|
||||||
from rides.models import Ride
|
from apps.rides.models import Ride
|
||||||
from media.models import Photo
|
from apps.media.models import Photo
|
||||||
|
|
||||||
User = get_user_model()
|
User = get_user_model()
|
||||||
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
from django.core.management.base import BaseCommand
|
from django.core.management.base import BaseCommand
|
||||||
from accounts.models import UserProfile
|
from apps.accounts.models import UserProfile
|
||||||
|
|
||||||
|
|
||||||
class Command(BaseCommand):
|
class Command(BaseCommand):
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
from django.core.management.base import BaseCommand
|
from django.core.management.base import BaseCommand
|
||||||
from django.contrib.auth.models import Group
|
from django.contrib.auth.models import Group
|
||||||
from accounts.models import User
|
from apps.accounts.models import User
|
||||||
from accounts.signals import create_default_groups
|
from apps.accounts.signals import create_default_groups
|
||||||
|
|
||||||
|
|
||||||
class Command(BaseCommand):
|
class Command(BaseCommand):
|
||||||
@@ -4,7 +4,7 @@ from django.urls import reverse
|
|||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
import os
|
import os
|
||||||
import secrets
|
import secrets
|
||||||
from core.history import TrackedModel
|
from apps.core.history import TrackedModel
|
||||||
|
|
||||||
# import pghistory
|
# import pghistory
|
||||||
|
|
||||||
@@ -4,7 +4,7 @@ from django.urls import reverse
|
|||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
import os
|
import os
|
||||||
import secrets
|
import secrets
|
||||||
from core.history import TrackedModel
|
from apps.core.history import TrackedModel
|
||||||
import pghistory
|
import pghistory
|
||||||
|
|
||||||
|
|
||||||
@@ -17,19 +17,19 @@ from django.http import HttpResponseRedirect, HttpResponse, HttpRequest
|
|||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.contrib.auth import login
|
from django.contrib.auth import login
|
||||||
from django.core.files.uploadedfile import UploadedFile
|
from django.core.files.uploadedfile import UploadedFile
|
||||||
from accounts.models import (
|
from apps.accounts.models import (
|
||||||
User,
|
User,
|
||||||
PasswordReset,
|
PasswordReset,
|
||||||
TopList,
|
TopList,
|
||||||
EmailVerification,
|
EmailVerification,
|
||||||
UserProfile,
|
UserProfile,
|
||||||
)
|
)
|
||||||
from email_service.services import EmailService
|
from apps.email_service.services import EmailService
|
||||||
from parks.models import ParkReview
|
from apps.parks.models import ParkReview
|
||||||
from rides.models import RideReview
|
from apps.rides.models import RideReview
|
||||||
from allauth.account.views import LoginView, SignupView
|
from allauth.account.views import LoginView, SignupView
|
||||||
from .mixins import TurnstileMixin
|
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 django_htmx.http import HttpResponseClientRefresh
|
||||||
from contextlib import suppress
|
from contextlib import suppress
|
||||||
import re
|
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"
|
date_hierarchy = "created_at"
|
||||||
ordering = ["-created_at"]
|
ordering = ["-created_at"]
|
||||||
|
|
||||||
|
@admin.display(description="Object")
|
||||||
def content_object_link(self, obj):
|
def content_object_link(self, obj):
|
||||||
"""Create a link to the related object's admin page"""
|
"""Create a link to the related object's admin page"""
|
||||||
try:
|
try:
|
||||||
@@ -20,8 +21,6 @@ class SlugHistoryAdmin(admin.ModelAdmin):
|
|||||||
except (AttributeError, ValueError):
|
except (AttributeError, ValueError):
|
||||||
return str(obj.content_object)
|
return str(obj.content_object)
|
||||||
|
|
||||||
content_object_link.short_description = "Object"
|
|
||||||
|
|
||||||
def has_add_permission(self, request):
|
def has_add_permission(self, request):
|
||||||
"""Disable manual creation of slug history records"""
|
"""Disable manual creation of slug history records"""
|
||||||
return False
|
return False
|
||||||
@@ -3,4 +3,4 @@ from django.apps import AppConfig
|
|||||||
|
|
||||||
class CoreConfig(AppConfig):
|
class CoreConfig(AppConfig):
|
||||||
default_auto_field = "django.db.models.BigAutoField"
|
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 typing import Optional, List, Callable
|
||||||
from django.utils.decorators import method_decorator
|
from django.utils.decorators import method_decorator
|
||||||
from django.views.decorators.vary import vary_on_headers
|
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
|
import logging
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
@@ -167,7 +167,7 @@ class ApplicationHealthCheck(BaseHealthCheckBackend):
|
|||||||
# Check if we can access critical models
|
# Check if we can access critical models
|
||||||
try:
|
try:
|
||||||
from parks.models import Park
|
from parks.models import Park
|
||||||
from rides.models import Ride
|
from apps.rides.models import Ride
|
||||||
from django.contrib.auth import get_user_model
|
from django.contrib.auth import get_user_model
|
||||||
|
|
||||||
User = get_user_model()
|
User = get_user_model()
|
||||||
@@ -97,6 +97,7 @@ class HistoricalSlug(models.Model):
|
|||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
|
app_label = "core"
|
||||||
unique_together = ("content_type", "slug")
|
unique_together = ("content_type", "slug")
|
||||||
indexes = [
|
indexes = [
|
||||||
models.Index(fields=["content_type", "object_id"]),
|
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.management.base import BaseCommand
|
||||||
from django.core.cache import cache
|
from django.core.cache import cache
|
||||||
from parks.models import Park
|
from apps.parks.models import Park
|
||||||
from rides.models import Ride
|
from apps.rides.models import Ride
|
||||||
from core.analytics import PageView
|
from apps.core.analytics import PageView
|
||||||
|
|
||||||
|
|
||||||
class Command(BaseCommand):
|
class Command(BaseCommand):
|
||||||
@@ -8,7 +8,7 @@ from django.core.handlers.wsgi import WSGIRequest
|
|||||||
from django.utils.deprecation import MiddlewareMixin
|
from django.utils.deprecation import MiddlewareMixin
|
||||||
from django.contrib.contenttypes.models import ContentType
|
from django.contrib.contenttypes.models import ContentType
|
||||||
from django.views.generic.detail import DetailView
|
from django.views.generic.detail import DetailView
|
||||||
from core.analytics import PageView
|
from apps.core.analytics import PageView
|
||||||
|
|
||||||
|
|
||||||
class RequestContextProvider(pghistory.context):
|
class RequestContextProvider(pghistory.context):
|
||||||
@@ -2,7 +2,7 @@ from django.db import models
|
|||||||
from django.contrib.contenttypes.fields import GenericForeignKey
|
from django.contrib.contenttypes.fields import GenericForeignKey
|
||||||
from django.contrib.contenttypes.models import ContentType
|
from django.contrib.contenttypes.models import ContentType
|
||||||
from django.utils.text import slugify
|
from django.utils.text import slugify
|
||||||
from core.history import TrackedModel
|
from apps.core.history import TrackedModel
|
||||||
|
|
||||||
|
|
||||||
class SlugHistory(models.Model):
|
class SlugHistory(models.Model):
|
||||||
@@ -11,8 +11,8 @@ from django.utils import timezone
|
|||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
|
|
||||||
from .analytics import PageView
|
from .analytics import PageView
|
||||||
from parks.models import Park
|
from apps.parks.models import Park
|
||||||
from rides.models import Ride
|
from apps.rides.models import Ride
|
||||||
|
|
||||||
|
|
||||||
def unified_locations_for_map(
|
def unified_locations_for_map(
|
||||||
@@ -13,9 +13,9 @@ from .data_structures import (
|
|||||||
GeoBounds,
|
GeoBounds,
|
||||||
MapFilters,
|
MapFilters,
|
||||||
)
|
)
|
||||||
from parks.models import ParkLocation, CompanyHeadquarters
|
from apps.parks.models import ParkLocation, CompanyHeadquarters
|
||||||
from rides.models import RideLocation
|
from apps.rides.models import RideLocation
|
||||||
from location.models import Location
|
from apps.location.models import Location
|
||||||
|
|
||||||
|
|
||||||
class BaseLocationAdapter:
|
class BaseLocationAdapter:
|
||||||
@@ -12,8 +12,8 @@ from django.db.models import Q
|
|||||||
from typing import Optional, List, Dict, Any, Set
|
from typing import Optional, List, Dict, Any, Set
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
|
|
||||||
from parks.models import Park, Company, ParkLocation
|
from apps.parks.models import Park, Company, ParkLocation
|
||||||
from rides.models import Ride
|
from apps.rides.models import Ride
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
@@ -1,11 +1,11 @@
|
|||||||
from django.urls import path
|
from django.urls import path
|
||||||
from core.views.search import (
|
from apps.core.views.search import (
|
||||||
AdaptiveSearchView,
|
AdaptiveSearchView,
|
||||||
FilterFormView,
|
FilterFormView,
|
||||||
LocationSearchView,
|
LocationSearchView,
|
||||||
LocationSuggestionsView,
|
LocationSuggestionsView,
|
||||||
)
|
)
|
||||||
from rides.views import RideSearchView
|
from apps.rides.views import RideSearchView
|
||||||
|
|
||||||
app_name = "search"
|
app_name = "search"
|
||||||
|
|
||||||
@@ -11,8 +11,8 @@ from rest_framework.views import APIView
|
|||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
from rest_framework.permissions import AllowAny
|
from rest_framework.permissions import AllowAny
|
||||||
from health_check.views import MainView
|
from health_check.views import MainView
|
||||||
from core.services.enhanced_cache_service import CacheMonitor
|
from apps.core.services.enhanced_cache_service import CacheMonitor
|
||||||
from core.utils.query_optimization import IndexAnalyzer
|
from apps.core.utils.query_optimization import IndexAnalyzer
|
||||||
|
|
||||||
|
|
||||||
class HealthCheckAPIView(APIView):
|
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