Skip to content

508-dev/interview-as-a-service

Repository files navigation

Interview-as-a-Service

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.

Tech Stack

  • 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

Project Structure

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

Development Setup

Prerequisites

  • Python 3.12+
  • Docker and Docker Compose
  • Node.js (optional, for TypeScript)

1. Clone and Setup Environment

# 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]"

2. Configure Environment Variables

# 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_...

3. Start Database Services (Docker)

# Start PostgreSQL and MinIO
docker compose -f docker-compose.dev.yml up -d

# Verify services are running
docker compose -f docker-compose.dev.yml ps

This starts:

  • PostgreSQL on localhost:5432
  • MinIO on localhost:9000 (API) and localhost:9001 (Console)

MinIO Console credentials: minioadmin / minioadmin

4. Initialize Django

# Create database migrations for all apps
python manage.py makemigrations

# Apply migrations to create tables
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

5. Load Sample Data (Optional)

# 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)

6. Run the Development Server

# Start Django development server with hot reload
python manage.py runserver

The application is now available at: http://localhost:8000

7. Stripe Webhook Testing (Development)

Stripe webhooks are required for booking status to update from PENDING to CONFIRMED after payment. Since Stripe cannot reach localhost, you need to use the Stripe CLI to forward webhook events to your local server.

Install the Stripe CLI

# Arch Linux
yay -S stripe-cli

# Manjaro Linux
pamac install stripe-cli

# Linux (Debian/Ubuntu)
curl -s https://packages.stripe.dev/api/security/keypair/stripe-cli-gpg/public | gpg --dearmor | sudo tee /usr/share/keyrings/stripe.gpg
echo "deb [signed-by=/usr/share/keyrings/stripe.gpg] https://packages.stripe.dev/stripe-cli-debian-local stable main" | sudo tee -a /etc/apt/sources.list.d/stripe.list
sudo apt update && sudo apt install stripe

# macOS
brew install stripe/stripe-cli/stripe


# Or download directly from https://github.com/stripe/stripe-cli/releases

Forward Webhooks to Local Server

# Login to your Stripe account (opens browser)
stripe login

# Start forwarding webhooks to your local Django server
stripe listen --forward-to localhost:8000/bookings/webhook/stripe/

The CLI will output something like:

Ready! Your webhook signing secret is whsec_abc123def456...

Update Your Environment

Add the webhook signing secret to your .env file:

STRIPE_WEBHOOK_SECRET=whsec_abc123def456...

Important: The secret must start with whsec_. If you see rk_test_ or sk_test_, that's the wrong key.

Restart your Django server after updating .env.

Verify Webhooks Are Working

  1. Keep stripe listen running in one terminal
  2. Run Django server in another terminal
  3. Complete a test booking and payment
  4. You should see webhook events in the Stripe CLI output:
    2024-01-15 10:30:00  --> checkout.session.completed [evt_xxx]
    2024-01-15 10:30:00  <-- [200] POST http://localhost:8000/bookings/webhook/stripe/
    
  5. The booking status should change from PENDING to CONFIRMED

Stopping Development Services

# 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

Running Tests

Unit Tests (pytest)

# 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

E2E Tests (Playwright)

# 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

Production Deployment

Prerequisites

  • Docker and Docker Compose
  • Domain name with SSL certificate
  • Stripe account (live keys)
  • Cal.com account
  • SMTP server for emails

1. Configure Production Environment

# Copy and edit the environment file
cp .env.example .env

Edit .env with production values:

# Django
SECRET_KEY=your-secure-random-secret-key
DEBUG=false
ALLOWED_HOSTS=example.com,www.example.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@example.com
EMAIL_HOST_PASSWORD=your-smtp-password
DEFAULT_FROM_EMAIL=noreply@example.com

# Security
SECURE_SSL_REDIRECT=true

2. Build and Start Production Stack

# 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 web

This starts:

  • web: Django application (Gunicorn)
  • db: PostgreSQL database
  • minio: MinIO object storage
  • nginx: Reverse proxy serving static files

3. Initialize Production Database

Note: Migration files should be created during development (makemigrations) and committed to the repository. Production only runs migrate to apply them.

# Apply migrations to create tables
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

4. Configure Stripe Webhooks

Stripe webhooks are required for booking status to update from PENDING to CONFIRMED after payment. Unlike development (which uses the Stripe CLI), production webhooks are sent directly from Stripe to your server.

Create the Webhook Endpoint

  1. Go to Stripe Dashboard > Developers > Webhooks
  2. Ensure you're in Live mode (toggle in top-left) for production
  3. Click Add endpoint
  4. Configure the endpoint:
    • Endpoint URL: https://example.com/bookings/webhook/stripe/
    • Description: Interview service booking confirmations (optional)
    • Listen to: Events on your account
    • Select events: Click "Select events", expand "Checkout", check checkout.session.completed
  5. Click Add endpoint

Get the Signing Secret

  1. After creating the endpoint, click on it to view details
  2. Under Signing secret, click Reveal
  3. Copy the secret (starts with whsec_)

Update Production Environment

Add the signing secret to your production .env:

STRIPE_WEBHOOK_SECRET=whsec_your_production_signing_secret

Restart the web service to apply changes:

docker compose -f docker-compose.prod.yml restart web

Verify Webhook Delivery

  1. In Stripe Dashboard, go to your webhook endpoint
  2. Click Send test webhook
  3. Select checkout.session.completed and click Send test webhook
  4. Check the Webhook attempts tab - you should see a 200 response
  5. If you see errors, check your application logs:
    docker compose -f docker-compose.prod.yml logs web

Troubleshooting

Error Cause Solution
400 response Invalid signature Verify STRIPE_WEBHOOK_SECRET matches the signing secret shown in Stripe Dashboard
404 response Wrong URL Ensure endpoint URL is exactly /bookings/webhook/stripe/ (with trailing slash)
500 response Application error Check Django logs for exceptions
Connection timeout Server unreachable Verify your domain resolves and HTTPS is working

5. SSL/HTTPS Configuration

The included nginx.conf is basic. For production with SSL:

  1. Option A: Use a reverse proxy like Cloudflare or AWS ALB
  2. Option B: Add Let's Encrypt with certbot:
# Install certbot and obtain certificate
# Then update nginx.conf with SSL configuration

Example SSL nginx configuration:

server {
    listen 80;
    server_name example.com;
    return 301 https://$server_name$request_uri;
}

server {
    listen 443 ssl;
    server_name example.com;

    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.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;
    }
}

Production Management Commands

# 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

Architecture Overview

Request Flow

  1. User visits homepage → pages/views.py:home
  2. User browses interviewers → interviewers/views.py:interviewer_list
  3. User clicks interviewer card → HTMX loads modal via interviewer_detail_modal
  4. User clicks "Book Now" → bookings/views.py:booking_start (Cal.com embed)
  5. User selects time → bookings/views.py:booking_form
  6. User submits form → bookings/views.py:create_booking → Stripe Checkout
  7. Payment success → Stripe webhook → bookings/webhooks.py:stripe_webhook
  8. Booking confirmed → Emails sent to customer and interviewer

Key Files

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

Troubleshooting

Database connection refused

# Ensure PostgreSQL is running
docker compose -f docker-compose.dev.yml ps

# Check PostgreSQL logs
docker compose -f docker-compose.dev.yml logs db

Static files not loading

# Collect static files
python manage.py collectstatic --noinput

# In production, ensure nginx is serving /static/

Stripe webhooks not working

# Check webhook endpoint is accessible
curl -X POST https://example.com/bookings/webhook/stripe/

# Verify STRIPE_WEBHOOK_SECRET is set correctly
# Check Stripe dashboard for webhook delivery attempts

MinIO bucket not created

# The createbucket service should auto-create it
# Manually create if needed:
docker compose -f docker-compose.dev.yml exec minio mc mb local/interview-service

License

See LICENSE file.

About

Our interview as a service website.

Resources

License

Stars

Watchers

Forks

Contributors