Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# ──────────────────────────────────────────────────────────
# Exam Practice Pro — Environment Variables
# ──────────────────────────────────────────────────────────
# Copy this file to .env and fill in the values.
# NEVER commit your .env file to source control.
# ──────────────────────────────────────────────────────────

# ─── Server ────────────────────────────────────────────
NODE_ENV=development
PORT=3000

# ─── Stripe ────────────────────────────────────────────
# Get your keys from https://dashboard.stripe.com/apikeys
# Use sk_test_... and pk_test_... keys for development
# Use sk_live_... and pk_live_... keys for production
STRIPE_SECRET_KEY=your_stripe_secret_key_here
STRIPE_PUBLISHABLE_KEY=your_stripe_publishable_key_here
STRIPE_WEBHOOK_SECRET=your_stripe_webhook_secret_here

# ─── Authentication (JWT) ──────────────────────────────
# Generate a strong random secret: node -e "console.log(require('crypto').randomBytes(64).toString('hex'))"
JWT_SECRET=CHANGE_ME_TO_A_STRONG_RANDOM_SECRET
JWT_EXPIRES_IN=24h

# ─── CORS ──────────────────────────────────────────────
# Comma-separated list of allowed origins
CORS_ORIGINS=http://localhost:3000,http://localhost:3001

# ─── Logging ───────────────────────────────────────────
LOG_LEVEL=info

# ─── Database (future) ─────────────────────────────────
# DATABASE_URL=postgresql://user:password@localhost:5432/exam_practice_pro
# MONGODB_URI=mongodb://localhost:27017/exam_practice_pro
77 changes: 77 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
# ──────────────────────────────────────────────
# Exam Practice Pro — CI/CD Pipeline
# ──────────────────────────────────────────────
name: CI

on:
push:
branches: [main]
pull_request:
branches: [main]

jobs:
# ── Lint & Test ───────────────────────────────
test:
name: Lint & Test
runs-on: ubuntu-latest
permissions:
contents: read
strategy:
matrix:
node-version: [18, 20, 22]

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Setup Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
cache: npm

- name: Install dependencies
run: npm ci

- name: Run linter
run: npm run lint

- name: Run tests
run: npm test
env:
NODE_ENV: test
JWT_SECRET: ci-test-secret
STRIPE_SECRET_KEY: sk_test_placeholder
STRIPE_WEBHOOK_SECRET: whsec_placeholder

# ── Docker Build ──────────────────────────────
docker:
name: Docker Build
runs-on: ubuntu-latest
permissions:
contents: read
needs: test
if: github.ref == 'refs/heads/main'

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Build Docker image
run: docker build -t exam-practice-pro-api .

- name: Verify container starts
run: |
docker run -d --name test-api \
-e NODE_ENV=development \
-e JWT_SECRET=ci-test-secret \
-p 3000:3000 \
exam-practice-pro-api

sleep 5

# Health check
curl -f http://localhost:3000/health || exit 1

docker stop test-api
docker rm test-api
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,4 @@
node_modules/
.env
*.log
dist/
28 changes: 28 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# ──────────────────────────────────────────────
# Exam Practice Pro — Backend API
# ──────────────────────────────────────────────
FROM node:22-alpine AS base

WORKDIR /app

# Install production dependencies only
COPY package.json package-lock.json ./
RUN npm ci --omit=dev

# Copy application source
COPY src/ ./src/
COPY public/ ./public/

# Non-root user for security
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
USER appuser

# Health check
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
CMD wget -qO- http://localhost:3000/health || exit 1

EXPOSE 3000

ENV NODE_ENV=production

CMD ["node", "src/server.js"]
Loading