A Django + HTMX web application for 508.dev's interview service. Users can browse interviewers, book sessions via cal.com, pay via Stripe, and upload resumes. Interviewers have an admin panel to manage their profile and view bookings.
- Backend: Django 5.1, PostgreSQL
- Frontend: HTMX, vanilla CSS (no frameworks)
- Payments: Stripe Checkout
- Scheduling: Cal.com embed
- Storage: MinIO (S3-compatible) for file uploads
- Testing: pytest, pytest-django, factory-boy, Playwright
interview-as-a-service/
├── interview_service/ # Django project settings
├── accounts/ # Authentication (login/logout)
├── interviewers/ # Interviewer profiles
├── bookings/ # Booking flow + Stripe integration
├── dashboard/ # Interviewer admin panel
├── pages/ # Static pages (homepage)
├── templates/ # HTML templates
├── static/ # CSS, JS assets
├── tests/ # pytest unit tests
└── e2e/ # Playwright E2E tests
- Python 3.12+
- Docker and Docker Compose
- Node.js (optional, for TypeScript)
# Clone the repository
git clone <repository-url>
cd interview-as-a-service
# Create Python virtual environment
python -m venv .venv
source .venv/bin/activate # On Windows: .venv\Scripts\activate
# Install dependencies
pip install -e ".[dev]"# Copy the example environment file
cp .env.example .env
# Edit .env with your settings (for development, defaults work fine)Key variables for development:
SECRET_KEY=dev-secret-key-change-in-production
DEBUG=true
POSTGRES_DB=interview_service
POSTGRES_USER=postgres
POSTGRES_PASSWORD=postgres
POSTGRES_HOST=localhost
POSTGRES_PORT=5432
STRIPE_SECRET_KEY=sk_test_...
STRIPE_PUBLISHABLE_KEY=pk_test_...# Start PostgreSQL and MinIO
docker compose -f docker-compose.dev.yml up -d
# Verify services are running
docker compose -f docker-compose.dev.yml psThis starts:
- PostgreSQL on
localhost:5432 - MinIO on
localhost:9000(API) andlocalhost:9001(Console)
MinIO Console credentials: minioadmin / minioadmin
# Run database migrations
python manage.py migrate
# Create a superuser for Django admin
python manage.py createsuperuser
# Collect static files (optional in dev)
python manage.py collectstatic --noinput# Open Django shell to create test data
python manage.py shell
# In the shell:
from django.contrib.auth.models import User
from interviewers.models import Interviewer, Technology, InterviewSubject
# Create technologies
python = Technology.objects.create(name="Python", slug="python")
react = Technology.objects.create(name="React", slug="react")
system_design = InterviewSubject.objects.create(name="System Design", slug="system-design")
# Create an interviewer
user = User.objects.create_user("interviewer1", "interviewer@example.com", "testpass123")
user.first_name = "Jane"
user.last_name = "Doe"
user.save()
interviewer = Interviewer.objects.create(
user=user,
bio="10 years at Google, expert in distributed systems.",
cal_event_type_id="jane-doe/interview",
hourly_rate=150.00,
companies="Google, Meta, Stripe"
)
interviewer.technologies.add(python, react)
interviewer.subjects.add(system_design)# Start Django development server with hot reload
python manage.py runserverThe application is now available at: http://localhost:8000
- Homepage: http://localhost:8000/
- Interviewers: http://localhost:8000/interviewers/
- Django Admin: http://localhost:8000/admin/
- Interviewer Login: http://localhost:8000/accounts/login/
To test Stripe webhooks locally, use the Stripe CLI:
# Install Stripe CLI (macOS)
brew install stripe/stripe-cli/stripe
# Login to Stripe
stripe login
# Forward webhooks to your local server
stripe listen --forward-to localhost:8000/bookings/webhook/stripe/
# Note the webhook signing secret and add it to .env
# STRIPE_WEBHOOK_SECRET=whsec_...# Stop the Django server
Ctrl+C
# Stop Docker services
docker compose -f docker-compose.dev.yml down
# Stop and remove volumes (clears database)
docker compose -f docker-compose.dev.yml down -v# Run all tests
pytest
# Run with verbose output
pytest -v
# Run specific test file
pytest tests/test_models.py
# Run with coverage
pytest --cov=. --cov-report=html# Install Playwright browsers (first time only)
playwright install
# Start the development server in one terminal
python manage.py runserver
# Run E2E tests in another terminal
pytest e2e/
# Run specific E2E test
pytest e2e/test_homepage.py
# Run in headed mode (see the browser)
pytest e2e/ --headed- Docker and Docker Compose
- Domain name with SSL certificate
- Stripe account (live keys)
- Cal.com account
- SMTP server for emails
# Copy and edit the environment file
cp .env.example .envEdit .env with production values:
# Django
SECRET_KEY=your-secure-random-secret-key
DEBUG=false
ALLOWED_HOSTS=yourdomain.com,www.yourdomain.com
# Database
POSTGRES_DB=interview_service
POSTGRES_USER=interview_user
POSTGRES_PASSWORD=secure-database-password
POSTGRES_HOST=db
POSTGRES_PORT=5432
# MinIO / S3 Storage
MINIO_ACCESS_KEY=your-minio-access-key
MINIO_SECRET_KEY=your-minio-secret-key
MINIO_BUCKET_NAME=interview-service
MINIO_ENDPOINT_URL=http://minio:9000
# Stripe (Live keys)
STRIPE_SECRET_KEY=sk_live_...
STRIPE_PUBLISHABLE_KEY=pk_live_...
STRIPE_WEBHOOK_SECRET=whsec_...
# Cal.com
CAL_COM_API_KEY=your-cal-com-api-key
# Email (SMTP)
EMAIL_HOST=smtp.mailgun.org
EMAIL_PORT=587
EMAIL_HOST_USER=postmaster@yourdomain.com
EMAIL_HOST_PASSWORD=your-smtp-password
DEFAULT_FROM_EMAIL=noreply@yourdomain.com
# Security
SECURE_SSL_REDIRECT=true# Build the Docker images
docker compose -f docker-compose.prod.yml build
# Start all services
docker compose -f docker-compose.prod.yml up -d
# Check service status
docker compose -f docker-compose.prod.yml ps
# View logs
docker compose -f docker-compose.prod.yml logs -f webThis starts:
- web: Django application (Gunicorn)
- db: PostgreSQL database
- minio: MinIO object storage
- nginx: Reverse proxy serving static files
# Run migrations
docker compose -f docker-compose.prod.yml exec web python manage.py migrate
# Create superuser
docker compose -f docker-compose.prod.yml exec web python manage.py createsuperuser
# Collect static files (done automatically in Dockerfile, but can re-run)
docker compose -f docker-compose.prod.yml exec web python manage.py collectstatic --noinput- Go to Stripe Dashboard > Webhooks
- Add endpoint:
https://yourdomain.com/bookings/webhook/stripe/ - Select events:
checkout.session.completed - Copy the signing secret to
STRIPE_WEBHOOK_SECRETin.env - Restart the web service:
docker compose -f docker-compose.prod.yml restart web
The included nginx.conf is basic. For production with SSL:
- Option A: Use a reverse proxy like Cloudflare or AWS ALB
- Option B: Add Let's Encrypt with certbot:
# Install certbot and obtain certificate
# Then update nginx.conf with SSL configurationExample SSL nginx configuration:
server {
listen 80;
server_name yourdomain.com;
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl;
server_name yourdomain.com;
ssl_certificate /etc/letsencrypt/live/yourdomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/yourdomain.com/privkey.pem;
location /static/ {
alias /app/staticfiles/;
}
location / {
proxy_pass http://web:8000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}# View logs
docker compose -f docker-compose.prod.yml logs -f
# Restart services
docker compose -f docker-compose.prod.yml restart
# Stop all services
docker compose -f docker-compose.prod.yml down
# Update and redeploy
git pull
docker compose -f docker-compose.prod.yml build
docker compose -f docker-compose.prod.yml up -d
docker compose -f docker-compose.prod.yml exec web python manage.py migrate
# Database backup
docker compose -f docker-compose.prod.yml exec db pg_dump -U $POSTGRES_USER $POSTGRES_DB > backup.sql
# Django shell
docker compose -f docker-compose.prod.yml exec web python manage.py shell- User visits homepage →
pages/views.py:home - User browses interviewers →
interviewers/views.py:interviewer_list - User clicks interviewer card → HTMX loads modal via
interviewer_detail_modal - User clicks "Book Now" →
bookings/views.py:booking_start(Cal.com embed) - User selects time →
bookings/views.py:booking_form - User submits form →
bookings/views.py:create_booking→ Stripe Checkout - Payment success → Stripe webhook →
bookings/webhooks.py:stripe_webhook - Booking confirmed → Emails sent to customer and interviewer
| File | Purpose |
|---|---|
interviewers/models.py |
Interviewer, Technology, InterviewSubject models |
bookings/models.py |
Booking model with status workflow |
bookings/stripe.py |
Stripe checkout session creation |
bookings/webhooks.py |
Stripe webhook handler |
bookings/emails.py |
Email notification functions |
dashboard/views.py |
Interviewer dashboard views |
templates/base.html |
Base template with HTMX setup |
static/css/styles.css |
All CSS styles |
# Ensure PostgreSQL is running
docker compose -f docker-compose.dev.yml ps
# Check PostgreSQL logs
docker compose -f docker-compose.dev.yml logs db# Collect static files
python manage.py collectstatic --noinput
# In production, ensure nginx is serving /static/# Check webhook endpoint is accessible
curl -X POST https://yourdomain.com/bookings/webhook/stripe/
# Verify STRIPE_WEBHOOK_SECRET is set correctly
# Check Stripe dashboard for webhook delivery attempts# The createbucket service should auto-create it
# Manually create if needed:
docker compose -f docker-compose.dev.yml exec minio mc mb local/interview-serviceSee LICENSE file.