A comprehensive web-based application for delivering defensive driving training courses with automated scoring and certification tracking.
This application enables users to:
- Browse available defensive driving courses
- Take interactive quizzes with 10 questions per course
- Receive automatic scoring and pass/fail determination (85% threshold)
- Track course completions and certificates
Browser → Frontend (Vue.js/Quasar) → Backend API (Node.js/Express) → PostgreSQL
↓
Automatic Scoring
↓
Certificate Tracking
- Frontend: Vue.js 3 + Quasar Framework
- Backend: Node.js + Express
- Database: PostgreSQL 15
- Infrastructure: Docker + Terraform
- Container Runtime: Docker
- IaC: Terraform with Docker provider
- Monitoring: Grafana + Prometheus + Loki
- Logging: Winston with structured JSON logs
- Metrics: Prometheus client with custom business metrics
- Docker 20.10+ (Install Docker)
- Terraform 1.0+ (Install Terraform)
- Node.js 18+ and npm (for generating package-lock.json files during initial setup)
- Git (for cloning the repository)
Note: Node.js/npm is required initially to generate package-lock.json files needed for Docker builds. After setup, all services run in containers.
# Method 1: Using NodeSource repository (Recommended for latest LTS)
# Install Node.js 18.x
curl -fsSL https://deb.nodesource.com/setup_18.x | sudo -E bash -
sudo apt-get install -y nodejs
# Verify installation
node --version
npm --version
# Method 2: Using Ubuntu's default repository (may be older version)
sudo apt-get update
sudo apt-get install -y nodejs npm# 1. Update package index
sudo apt-get update
# 2. Install prerequisites
sudo apt-get install -y \
ca-certificates \
curl \
gnupg \
lsb-release
# 3. Add Docker's official GPG key
sudo mkdir -p /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
# 4. Set up the repository
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
$(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
# 5. Install Docker Engine
sudo apt-get update
sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
# 6. Add your user to the docker group (to run docker without sudo)
sudo usermod -aG docker $USER
# 7. Log out and back in, or run:
newgrp docker
# 8. Verify installation
docker --version
docker run hello-worldMethod 1: Using HashiCorp's Official Repository (Recommended)
# 1. Install prerequisites
sudo apt-get update
sudo apt-get install -y gnupg software-properties-common
# 2. Add HashiCorp GPG key
wget -O- https://apt.releases.hashicorp.com/gpg | \
gpg --dearmor | \
sudo tee /usr/share/keyrings/hashicorp-archive-keyring.gpg
# 3. Verify the key fingerprint
gpg --no-default-keyring \
--keyring /usr/share/keyrings/hashicorp-archive-keyring.gpg \
--fingerprint
# 4. Add the official HashiCorp repository
echo "deb [signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] \
https://apt.releases.hashicorp.com $(lsb_release -cs) main" | \
sudo tee /etc/apt/sources.list.d/hashicorp.list
# 5. Update and install Terraform
sudo apt-get update
sudo apt-get install -y terraform
# 6. Verify installation
terraform --versionMethod 2: Manual Binary Installation (Alternative)
# 1. Download Terraform (check https://www.terraform.io/downloads for latest version)
TERRAFORM_VERSION="1.6.6"
wget https://releases.hashicorp.com/terraform/${TERRAFORM_VERSION}/terraform_${TERRAFORM_VERSION}_linux_amd64.zip
# 2. Install unzip if not already installed
sudo apt-get install -y unzip
# 3. Unzip the binary
unzip terraform_${TERRAFORM_VERSION}_linux_amd64.zip
# 4. Move to a directory in your PATH
sudo mv terraform /usr/local/bin/
# 5. Verify installation
terraform --version
# 6. Clean up
rm terraform_${TERRAFORM_VERSION}_linux_amd64.zip# Check Node.js
node --version
# Expected: v18.x.x or higher
# Check npm
npm --version
# Expected: 9.x.x or higher
# Check Docker
docker --version
# Expected: Docker version 20.10.x or higher
# Check Docker Compose
docker compose version
# Expected: Docker Compose version 2.x.x
# Check Terraform
terraform --version
# Expected: Terraform v1.x.x or higher
# Check Git
git --version
# Expected: git version 2.x.xTerraform manages the entire infrastructure including network, database, backend, and frontend containers.
IMPORTANT: Before running Terraform, you must generate package-lock.json files for dependency management.
# 1. Generate package-lock.json files (required for Docker builds)
cd backend
npm install
cd ../frontend
npm install
cd ..
# 2. Navigate to terraform directory
cd terraform
# 3. Initialize Terraform
terraform init
# 4. Review the plan (optional)
terraform plan
# 5. Apply the configuration
terraform apply
# Type 'yes' when promptedWhat this does:
- Creates a Docker network (
asi-network) - Launches PostgreSQL container with persistent volume
- Builds and launches backend API container
- Builds and launches frontend container
- Deploys monitoring stack:
- Grafana dashboard (port 3001)
- Prometheus metrics collection (port 9090)
- Loki log aggregation (port 3100)
- Automatically initializes database schema
- Seeds 5 courses with 10 questions each
- Configures Winston logging with Loki integration
- Sets up Prometheus metrics scraping
Wait time: Initial build takes 2-5 minutes depending on your machine.
IMPORTANT: Before running Docker Compose, you must generate package-lock.json files for dependency management.
# 1. Generate package-lock.json files (required for Docker builds)
cd backend && npm install && cd ..
cd frontend && npm install && cd ..
# 2. From project root, start containers
docker-compose up --build -d
# 3. Check logs
docker-compose logs -fOnce containers are running:
Production Domains:
- Frontend: https://asi.com
- Backend API: https://api.asi.com
- Backend Health: https://api.asi.com/health
Local Development (via localhost):
- Frontend: http://localhost:8080
- Backend API: http://localhost:3000
- Backend Health: http://localhost:3000/health
- Database: localhost:5432 (credentials in terraform/variables.tf)
Note: For local development, use localhost URLs. The application is configured to use production domains (asi.com, api.asi.com) but you'll need to set up DNS/hosts entries or use a reverse proxy for those domains to work locally.
To use the production domain names (asi.com, api.asi.com) on your local machine:
Option 1: Edit /etc/hosts (Linux/Mac)
sudo nano /etc/hosts
# Add these lines:
127.0.0.1 asi.com
127.0.0.1 api.asi.comOption 2: Use localhost URLs Just use http://localhost:8080 for frontend and http://localhost:3000 for backend API. The application will work fine with localhost.
The application includes a comprehensive monitoring stack deployed via Terraform:
Monitoring Endpoints:
- Grafana Dashboard: http://localhost:3001 (admin/${GRAFANA_PASSWORD})
- Prometheus Metrics: http://localhost:9090
- Loki Logs: http://localhost:3100
- Backend Metrics: http://localhost:3000/metrics
Key Features:
- Grafana - Pre-configured dashboards for application metrics and logs
- Prometheus - Metrics collection (15s scrape interval)
- HTTP request duration and rates
- Authentication attempts (login/register success/failure)
- Database pool connections
- Error rates and types
- Loki - Log aggregation with correlation ID tracking
- Winston structured logging
- Automatic log shipping from backend
- 30-day retention
- Health Checks - Liveness and readiness probes
Quick Start:
# Access Grafana
open http://localhost:3001
# View pre-configured dashboard
# Navigate to: Home → ASI Backend Overview
# Query logs in Grafana
# Click: Explore → Select "Loki" datasource
# Check Prometheus targets
curl http://localhost:9090/api/v1/targets | jq '.data.activeTargets[] | {job: .labels.job, health: .health}'
# View backend health
curl http://localhost:3000/health/ready | jq .Available Metrics:
asi_http_request_duration_seconds- API response timesasi_http_request_total- Request counts by endpointasi_auth_attempts_total- Authentication success/failure ratesasi_db_pool_connections- Database connection pool statusasi_errors_total- Error counts by type and endpoint
Log Queries (LogQL):
# All backend logs
{service="asi-training-backend"}
# Only errors
{service="asi-training-backend"} |= "error"
# Track request by correlation ID
{service="asi-training-backend"} |= "550e8400-e29b-41d4-a716-446655440000"
For detailed monitoring documentation, see monitoring/README.md.
For production deployment considerations, alerting configuration, and integration with CloudWatch/ELK, see docs/MONITORING.md.
- Open http://localhost:8080 (or https://asi.com if configured) in your browser
- You'll see 5 defensive driving courses:
- Defensive Driving Basics
- Weather Conditions and Driving
- Traffic Laws and Regulations
- Distracted Driving Prevention
- Impaired Driving Awareness
- Click "View Course" on any course
- Read the course description and content
- Enter a User ID (use
1for testing) - Click "Start Course"
- Answer all 10 multiple-choice questions
- Use "Next" and "Previous" to navigate
- Your answers are auto-saved as you go
- Click "Complete Course" when finished
- See your score (percentage)
- Pass/Fail status (85% required to pass)
- Certificate number (if passed)
- Return to browse more courses
The application is configured for local development with hot-reload enabled for both backend and frontend.
Backend (Node.js/Express):
- Uses
nodemonto automatically restart the server when code changes - Watches
backend/src/directory - Changes to
.jsfiles trigger automatic restart - Server typically restarts in 1-2 seconds
Frontend (Vue.js/Quasar):
- Uses Quasar CLI dev server with Vite
- Watches
frontend/src/directory - Changes automatically reload in browser (HMR - Hot Module Replacement)
- Nearly instant updates without full page reload
-
Edit backend code:
# Edit any file in backend/src/ vim backend/src/controllers/coursesController.js # Changes are automatically detected # Server restarts within 1-2 seconds # Check logs: docker-compose logs -f backend
-
Edit frontend code:
# Edit any file in frontend/src/ vim frontend/src/pages/CoursesPage.vue # Changes automatically appear in browser # No manual refresh needed (HMR) # Check logs: docker-compose logs -f frontend
-
Edit configuration files:
- Changes to
quasar.config.jsorindex.htmlrequire container restart - Run:
docker-compose restart frontend
- Changes to
The following directories are mounted from your host to containers:
Backend:
./backend/src→/app/src(source code)backend_node_modulesvolume (prevents host overwrite)
Frontend:
./frontend/src→/app/src(source code)./frontend/index.html→/app/index.html./frontend/quasar.config.js→/app/quasar.config.jsfrontend_node_modulesvolume (prevents host overwrite)
# All containers
docker-compose logs -f
# Specific service
docker-compose logs -f backend
docker-compose logs -f frontend
# Last 50 lines
docker-compose logs --tail=50 backend# Restart all services
docker-compose restart
# Restart specific service
docker-compose restart backend
docker-compose restart frontend
# Rebuild after package.json changes
docker-compose up --build -dThe application includes pre-seeded sample data:
Each course includes:
- Title and description
- Reading content (200-300 words)
- 10 quiz questions
- Multiple choice answers (4 options each)
- Passing score: 85%
For testing, use user_id: 1 (no authentication required in current implementation)
cd terraform
terraform destroy
# Type 'yes' when prompteddocker-compose down
# To also remove volumes:
docker-compose down -v# Check Docker is running
docker ps
# Check container logs
docker logs asi-backend
docker logs asi-database
docker logs asi-frontend# Wait 10 seconds after starting - database needs time to initialize
# Check database is healthy
docker exec asi-database pg_isready -U asi_user
# Restart backend
docker restart asi-backendIf ports 3000, 5432, or 8080 are in use:
# Stop conflicting services or modify ports in terraform/variables.tf# Clean terraform state
cd terraform
rm -rf .terraform terraform.tfstate*
terraform init
terraform applycd backend
# Install dependencies
npm install
# Create .env file
cp .env.example .env
# Edit .env with local database URL
# Run database (via docker)
docker run -d -p 5432:5432 \
-e POSTGRES_DB=asi_training \
-e POSTGRES_USER=asi_user \
-e POSTGRES_PASSWORD=${POSTGRES_PASSWORD} \
postgres:15-alpine
# Start dev server with hot reload
npm run dev
# Backend runs on http://localhost:3000cd frontend
# Install dependencies
npm install
# Start dev server
npm run dev
# Frontend runs on http://localhost:8080asi/
├── backend/ # Node.js/Express API
│ ├── src/
│ │ ├── app.js # Express application setup
│ │ ├── server.js # Server startup
│ │ ├── config/
│ │ │ ├── database.js # PostgreSQL connection pool
│ │ │ └── init-db.js # Schema creation & seeding
│ │ ├── routes/
│ │ │ ├── health.js # Health check endpoints
│ │ │ └── courses.js # Course/quiz routes
│ │ ├── controllers/
│ │ │ └── coursesController.js
│ │ └── middleware/
│ │ └── errorHandler.js
│ ├── Dockerfile
│ ├── .dockerignore
│ ├── package.json
│ └── .env.example
│
├── frontend/ # Vue.js/Quasar SPA
│ ├── src/
│ │ ├── App.vue
│ │ ├── main.js
│ │ ├── app.css
│ │ ├── layouts/
│ │ │ └── MainLayout.vue
│ │ ├── pages/
│ │ │ ├── CoursesPage.vue
│ │ │ ├── CourseDetailPage.vue
│ │ │ ├── QuizPage.vue
│ │ │ └── ResultsPage.vue
│ │ ├── router/
│ │ │ └── index.js
│ │ └── services/
│ │ └── api.js
│ ├── public/
│ │ └── index.html
│ ├── Dockerfile
│ ├── nginx.conf
│ ├── quasar.config.js
│ └── package.json
│
├── terraform/ # Infrastructure as Code
│ ├── main.tf # Provider configuration
│ ├── variables.tf # Input variables
│ ├── outputs.tf # Output values
│ ├── network.tf # Docker network
│ ├── database.tf # PostgreSQL container
│ ├── backend.tf # Backend API container
│ ├── frontend.tf # Frontend Nginx container
│ └── monitoring.tf # Monitoring stack (Grafana, Prometheus, Loki)
│
├── monitoring/ # Monitoring Configuration
│ ├── README.md # Monitoring documentation
│ ├── loki-config.yaml # Loki server configuration
│ ├── prometheus.yml # Prometheus scrape targets
│ └── grafana/
│ └── provisioning/
│ ├── datasources/ # Auto-provisioned datasources
│ └── dashboards/ # Pre-configured dashboards
│
├── docs/ # Documentation
│ ├── asi.md # Interview context
│ ├── proposal.md # Product proposal
│ ├── product.md # Product specifications
│ ├── PUNCHLIST.md # Implementation checklist
│ └── MONITORING.md # Production monitoring guide
│
├── docker-compose.yml # Alternative to Terraform
├── .gitignore
├── Claude.md # AI assistant guidelines
└── README.md
GET /health- Liveness probe (returns 200 OK)GET /ready- Readiness probe (checks database connection)
GET /api/courses- List all coursesGET /api/courses/:id- Get course details with question count
POST /api/courses/:id/start- Create enrollment and get questions- Body:
{ "user_id": 1 } - Returns: enrollment details + full quiz questions with answers
- Body:
POST /api/enrollments/:id/answer- Submit answer for a question- Body:
{ "question_id": 5, "answer_id": 21 }
- Body:
POST /api/enrollments/:id/complete- Calculate score and determine pass/fail- Returns: score, status (completed/failed), certificate info
GET /api/enrollments/:id- Get enrollment details
The following endpoints are planned but not yet implemented:
POST /auth/register- User registrationPOST /auth/login- User loginPOST /auth/refresh- Refresh access token
- Certificate records are created on course completion
- PDF generation and email functionality are NOT implemented
Core Functionality:
- ✓ Course browsing and listing
- ✓ Course detail viewing with reading content
- ✓ Quiz enrollment system
- ✓ 10-question quizzes per course
- ✓ Answer submission and tracking
- ✓ Automatic scoring (correct/total × 100)
- ✓ Pass/fail determination (85% threshold)
- ✓ Certificate record generation
- ✓ Results display
Database:
- ✓ Complete schema (7 tables)
- ✓ Sample data seeding (5 courses, 50 questions)
- ✓ Foreign key relationships
- ✓ Connection pooling
- ✓ Health checks
Infrastructure:
- ✓ Terraform configuration for local Docker
- ✓ Docker network isolation
- ✓ Multi-container orchestration
- ✓ Persistent database volumes
- ✓ Health checks on all containers
- ✓ Auto-restart policies
Monitoring and Observability:
- ✓ Grafana dashboards with pre-configured visualizations
- ✓ Prometheus metrics collection (15s intervals)
- ✓ Loki log aggregation with 30-day retention
- ✓ Winston structured JSON logging
- ✓ Request correlation IDs for distributed tracing
- ✓ Custom business metrics (auth, enrollments, quiz scores)
- ✓ HTTP request metrics (duration, rate, errors)
- ✓ Database connection pool monitoring
- ✓ Health check endpoints (liveness and readiness)
- ✓ Error tracking and classification
Frontend:
- ✓ Vue.js 3 + Quasar UI components
- ✓ Responsive design
- ✓ Multiple page components
- ✓ API integration
- ✓ Loading states and error handling
- ✓ Progress tracking in quiz
- ✓ Navigation and routing
Authentication:
- ✗ User registration
- ✗ Login/logout
- ✗ JWT token generation and validation
- ✗ Refresh token flow
- ✗ Protected routes
Certificates:
- ✗ PDF generation (PDFKit integration)
- ✗ PDF download endpoint
- ✗ PDF storage (S3 or local)
Email:
- ✗ Email functionality
- ✗ SES integration (AWS)
- ✗ Mailhog integration (local)
- ✗ Certificate email delivery
User Management:
- ✗ User profile pages
- ✗ User dashboard
- ✗ Enrollment history
Admin Features:
- ✗ Admin interface
- ✗ Course content management
- ✗ User management
- ✗ Reporting and analytics
Testing:
- ✗ Unit tests
- ✗ Integration tests
- ✗ E2E tests with Cypress
Production Features:
- ✗ AWS deployment (ECS, RDS, ALB, S3)
- ✗ CI/CD pipeline with GitHub Actions
- ✗ Secrets management (AWS Secrets Manager)
- ✗ HTTPS/SSL certificates
- ✗ CloudWatch integration (logs and metrics)
- ✗ Alerting with PagerDuty/Opsgenie
The application uses 7 tables:
- users - User accounts (basic structure, no auth yet)
- courses - Course definitions
- questions - Quiz questions (10 per course)
- answers - Answer choices (4 per question, 1 correct)
- enrollments - User course enrollments
- user_answers - Individual answer submissions
- certificates - Certificate records (no PDF yet)
Database is automatically initialized and seeded on first backend startup.
- Start the application (via Terraform or Docker Compose)
- Wait 30 seconds for database initialization
- Open frontend: http://localhost:8080
- Select a course from the list
- Enter user ID:
1(or any number) - Start the course and read the content
- Take the quiz - answer all 10 questions
- Complete the course to see your score
- Check results - you need 85% (9/10 or 10/10) to pass
Using localhost:
# Health check
curl http://localhost:3000/health
# Get all courses
curl http://localhost:3000/api/courses
# Get course details
curl http://localhost:3000/api/courses/1
# Start a course (creates enrollment)
curl -X POST http://localhost:3000/api/courses/1/start \
-H "Content-Type: application/json" \
-d '{"user_id": 1}'Using production domains (if configured in /etc/hosts):
# Health check
curl https://api.asi.com/health
# Get all courses
curl https://api.asi.com/api/courses
# Get course details
curl https://api.asi.com/api/courses/1- No authentication - User ID is manually entered (simulated auth)
- No PDF certificates - Certificate records created but no PDF files
- No email - Email functionality not implemented
- Single test user - Use user_id: 1 for all testing
- No user persistence - Users not stored (enrollments work with any ID)
- No edit/delete - Cannot modify or delete enrollments once created
- Local only - Terraform configured for Docker provider, not AWS
See docs/proposal.md and docs/product.md for complete product specifications including:
- Full authentication system with JWT
- PDF certificate generation and storage
- Email delivery via SES/Mailhog
- User registration and profile management
- Admin interface for content management
- AWS production deployment
- CI/CD pipeline with GitHub Actions
- Comprehensive test coverage
- Monitoring and observability
This is a demonstration project for the ASI Head of Engineering interview. For questions or feedback, please contact the repository owner.
Proprietary - American Safety Institute
- Product Plan: docs/proposal.md
- Product Specifications: docs/product.md
- Implementation Checklist: docs/PUNCHLIST.md
- AI Guidelines: Claude.md