Skip to content

Commit 2592bcd

Browse files
mattstrattonclaude
andcommitted
Initial POC: Django + Vue 3 + Inertia.js webapp for devopsdays.org
Replaces the Hugo static site workflow with a database-backed web application. City organizers can manage their event pages through a browser UI instead of git, YAML, and pull requests. Includes: - Django backend with full data model (events, sponsors, speakers, talks, etc.) - Public site with server-rendered Django templates + Tailwind CSS - Organizer editing UI with Vue 3 + Inertia.js - Django admin for core team - GitHub OAuth via django-allauth - Docker Compose for local dev (one command: docker compose up) - Production Docker setup with GHCR + GitHub Actions CI/CD - Pulumi program for EC2 provisioning - Seed script that imports real data from the Hugo repo - Full PRD documenting architecture, data model, and migration strategy Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
0 parents  commit 2592bcd

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

83 files changed

+7876
-0
lines changed

.dockerignore

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
.git
2+
__pycache__
3+
*.pyc
4+
node_modules
5+
frontend/dist
6+
.env
7+
.env.*
8+
media/
9+
staticfiles/
10+
*.md
11+
deploy/
12+
.claude/

.github/workflows/deploy.yml

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
name: Build & Deploy
2+
3+
on:
4+
push:
5+
branches: [main]
6+
workflow_dispatch:
7+
8+
env:
9+
REGISTRY: ghcr.io
10+
IMAGE_NAME: devopsdays/devopsdays-web-app
11+
12+
jobs:
13+
build-and-push:
14+
runs-on: ubuntu-latest
15+
permissions:
16+
contents: read
17+
packages: write
18+
19+
steps:
20+
- uses: actions/checkout@v4
21+
22+
- name: Log in to GitHub Container Registry
23+
uses: docker/login-action@v3
24+
with:
25+
registry: ghcr.io
26+
username: ${{ github.actor }}
27+
password: ${{ secrets.GITHUB_TOKEN }}
28+
29+
- name: Build and push Docker image
30+
uses: docker/build-push-action@v6
31+
with:
32+
context: .
33+
file: Dockerfile.prod
34+
push: true
35+
tags: |
36+
ghcr.io/devopsdays/devopsdays-web-app:latest
37+
ghcr.io/devopsdays/devopsdays-web-app:${{ github.sha }}
38+
39+
deploy:
40+
needs: build-and-push
41+
runs-on: ubuntu-latest
42+
if: github.ref == 'refs/heads/main'
43+
44+
steps:
45+
- name: Deploy to EC2
46+
uses: appleboy/ssh-action@v1
47+
with:
48+
host: ${{ secrets.EC2_HOST }}
49+
username: ec2-user
50+
key: ${{ secrets.EC2_SSH_KEY }}
51+
script: |
52+
cd /opt/devopsdays-web-app
53+
docker pull ghcr.io/devopsdays/devopsdays-web-app:latest
54+
docker compose -f docker-compose.prod.yml up -d --force-recreate web
55+
echo "Deploy complete!"

.gitignore

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
# Python
2+
__pycache__/
3+
*.py[cod]
4+
*$py.class
5+
*.so
6+
*.egg-info/
7+
dist/
8+
build/
9+
.eggs/
10+
11+
# Django
12+
*.log
13+
local_settings.py
14+
db.sqlite3
15+
media/
16+
17+
# Environment
18+
.env
19+
.env.local
20+
.env.*.local
21+
22+
# Node
23+
node_modules/
24+
npm-debug.log*
25+
yarn-debug.log*
26+
yarn-error.log*
27+
28+
# Vite
29+
frontend/dist/
30+
31+
# IDE
32+
.vscode/
33+
.idea/
34+
*.swp
35+
*.swo
36+
*~
37+
38+
# OS
39+
.DS_Store
40+
Thumbs.db
41+
42+
# Docker
43+
docker-compose.override.yml
44+
45+
# Static files collected by Django
46+
staticfiles/

CLAUDE.md

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
# devopsdays-web-app
2+
3+
## What is this?
4+
5+
A Django + Vue 3 web application replacing the Hugo static site for devopsdays.org. City organizers manage their event pages through a browser-based UI instead of git/YAML/PRs.
6+
7+
## Architecture
8+
9+
- **Backend:** Django 5.x, PostgreSQL 16, Redis
10+
- **Public site:** Django templates + Tailwind CSS (server-rendered)
11+
- **Editing UI:** Inertia.js + Vue 3 (SPA experience, Django handles routing and auth)
12+
- **Admin:** Django admin with customizations (for core team)
13+
- **Auth:** django-allauth with GitHub and Google OAuth
14+
15+
## Key Conventions
16+
17+
- Django apps live in `apps/` (events, sponsors, people, users, pages)
18+
- Public site templates are in `templates/public/` — plain Django templates, no JS framework
19+
- Vue components for the editing UI are in `frontend/src/pages/` — these are Inertia pages
20+
- Manage views return `inertia_render()` instead of `render()` — they pass props to Vue components
21+
- URL patterns: public site at `/`, editing UI at `/manage/`, admin at `/admin/`
22+
- All models use `BigAutoField` as the primary key
23+
- Slugs are the primary human-readable identifiers (event slugs are `YYYY-city`)
24+
25+
## Running Locally
26+
27+
```bash
28+
docker compose up
29+
docker compose exec web python manage.py migrate
30+
docker compose exec web python manage.py shell < scripts/seed_data.py
31+
```
32+
33+
## Data Model
34+
35+
Core entities: City, Event, EventPage, TeamMember, ProgramEntry, Sponsor, SponsorLevel, EventSponsor, Speaker, Talk, User, EventPermission, BlogPost, StaticPage.
36+
37+
See `apps/*/models.py` for full field definitions. The PRD at `../devopsdays-web/PRD-webapp-migration.md` has the complete data model documentation.
38+
39+
## Testing
40+
41+
```bash
42+
docker compose exec web python -m pytest
43+
```
44+
45+
## Important Notes
46+
47+
- This is a POC. Not all features from the PRD are implemented.
48+
- The seed script (`scripts/seed_data.py`) reads from the Hugo repo at `../devopsdays-web`
49+
- Tailwind is loaded via CDN in the public templates for the POC. Production should use Vite.
50+
- The Vite dev server runs in Docker alongside Django for HMR on Vue components.

Dockerfile

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
FROM python:3.12-slim
2+
3+
ENV PYTHONDONTWRITEBYTECODE=1
4+
ENV PYTHONUNBUFFERED=1
5+
6+
WORKDIR /app
7+
8+
# Install system dependencies
9+
RUN apt-get update && apt-get install -y --no-install-recommends \
10+
build-essential \
11+
libpq-dev \
12+
curl \
13+
&& rm -rf /var/lib/apt/lists/*
14+
15+
# Install Python dependencies
16+
COPY requirements.txt .
17+
RUN pip install --no-cache-dir -r requirements.txt
18+
19+
# Copy project
20+
COPY . .
21+
22+
# Collect static files (skipped in dev, run manually in prod)
23+
# RUN python manage.py collectstatic --noinput
24+
25+
EXPOSE 8000
26+
27+
CMD ["gunicorn", "config.wsgi:application", "--bind", "0.0.0.0:8000", "--reload", "--workers", "2"]

Dockerfile.prod

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
# Stage 1: Build frontend assets
2+
FROM node:22-alpine AS frontend-build
3+
4+
WORKDIR /app
5+
COPY package.json package-lock.json* ./
6+
RUN npm install
7+
8+
COPY vite.config.js tailwind.config.js postcss.config.js ./
9+
COPY frontend/ frontend/
10+
COPY templates/ templates/
11+
12+
RUN npm run build
13+
14+
15+
# Stage 2: Python application
16+
FROM python:3.12-slim AS production
17+
18+
ENV PYTHONDONTWRITEBYTECODE=1
19+
ENV PYTHONUNBUFFERED=1
20+
21+
WORKDIR /app
22+
23+
# Install system dependencies
24+
RUN apt-get update && apt-get install -y --no-install-recommends \
25+
libpq-dev \
26+
curl \
27+
&& rm -rf /var/lib/apt/lists/*
28+
29+
# Install Python dependencies
30+
COPY requirements.txt .
31+
RUN pip install --no-cache-dir -r requirements.txt
32+
33+
# Copy project
34+
COPY . .
35+
36+
# Copy built frontend assets from stage 1
37+
COPY --from=frontend-build /app/frontend/dist ./frontend/dist
38+
39+
# Collect static files
40+
RUN DJANGO_SECRET_KEY=build-placeholder \
41+
DATABASE_URL=sqlite:///tmp/build.db \
42+
python manage.py collectstatic --noinput
43+
44+
EXPOSE 8000
45+
46+
CMD ["gunicorn", "config.wsgi:application", "--bind", "0.0.0.0:8000", "--workers", "3", "--timeout", "120"]

0 commit comments

Comments
 (0)