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
14 changes: 14 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
node_modules/
.git/
.github/
.husky/
.env
.env.local
.env.*.local
*.log
*.md
!README.md
k8s/
tests/
coverage/
.DS_Store
31 changes: 31 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# Application
TODO_APP_PORT=3000
TODO_APP_NODE_ENV=development
TODO_APP_CORS_ORIGIN=http://localhost:8080
TODO_APP_FRONTEND_PORT=8080

# Database (PostgreSQL)
TODO_APP_DB_HOST=localhost
TODO_APP_DB_PORT=5432
TODO_APP_DB_NAME=todo_app
TODO_APP_DB_USER=todo_user
TODO_APP_DB_PASSWORD=changeme
TODO_APP_DB_POOL_MAX=20
TODO_APP_DB_POOL_IDLE_TIMEOUT=30000
TODO_APP_DB_POOL_CONN_TIMEOUT=5000

# Redis
TODO_APP_REDIS_URL=redis://localhost:6379
TODO_APP_REDIS_PORT=6379

# Authentication (placeholder for app-core workstream)
TODO_APP_JWT_SECRET=changeme-use-a-strong-secret
TODO_APP_JWT_EXPIRY=15m
TODO_APP_REFRESH_TOKEN_EXPIRY=7d

# Logging
TODO_APP_LOG_LEVEL=info

# Rate Limiting
TODO_APP_RATE_LIMIT_WINDOW_MS=900000
TODO_APP_RATE_LIMIT_MAX=100
90 changes: 90 additions & 0 deletions .github/workflows/cd.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
name: CD

on:
push:
branches: [main]

env:
REGISTRY: ghcr.io
BACKEND_IMAGE: ghcr.io/${{ github.repository }}/backend
FRONTEND_IMAGE: ghcr.io/${{ github.repository }}/frontend

jobs:
build-and-push:
name: Build and Push Images
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- uses: actions/checkout@v4

- name: Log in to Container Registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Build and push backend
uses: docker/build-push-action@v5
with:
context: .
file: Dockerfile.backend
push: true
tags: |
${{ env.BACKEND_IMAGE }}:${{ github.sha }}
${{ env.BACKEND_IMAGE }}:latest
cache-from: type=gha
cache-to: type=gha,mode=max

- name: Build and push frontend
uses: docker/build-push-action@v5
with:
context: .
file: Dockerfile.frontend
push: true
tags: |
${{ env.FRONTEND_IMAGE }}:${{ github.sha }}
${{ env.FRONTEND_IMAGE }}:latest
cache-from: type=gha
cache-to: type=gha,mode=max

deploy-staging:
name: Deploy to Staging
runs-on: ubuntu-latest
needs: build-and-push
environment: staging
steps:
- uses: actions/checkout@v4

- name: Deploy to staging
run: |
echo "Deploying to staging environment..."
echo "Backend image: ${{ env.BACKEND_IMAGE }}:${{ github.sha }}"
echo "Frontend image: ${{ env.FRONTEND_IMAGE }}:${{ github.sha }}"
# Replace with actual deployment commands (kubectl apply, helm upgrade, etc.)
# kubectl set image deployment/todo-backend backend=${{ env.BACKEND_IMAGE }}:${{ github.sha }}
# kubectl set image deployment/todo-frontend frontend=${{ env.FRONTEND_IMAGE }}:${{ github.sha }}

deploy-production:
name: Deploy to Production
runs-on: ubuntu-latest
needs: deploy-staging
environment:
name: production
# Requires manual approval via GitHub environment protection rules
steps:
- uses: actions/checkout@v4

- name: Deploy to production
run: |
echo "Deploying to production environment..."
echo "Backend image: ${{ env.BACKEND_IMAGE }}:${{ github.sha }}"
echo "Frontend image: ${{ env.FRONTEND_IMAGE }}:${{ github.sha }}"
# Replace with actual deployment commands
# kubectl set image deployment/todo-backend backend=${{ env.BACKEND_IMAGE }}:${{ github.sha }} --namespace=production
# kubectl set image deployment/todo-frontend frontend=${{ env.FRONTEND_IMAGE }}:${{ github.sha }} --namespace=production
121 changes: 121 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
name: CI

on:
push:
branches: [main, productionize, 'productionize/**']
pull_request:
branches: [main, productionize]

jobs:
lint:
name: Lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '18'
cache: 'npm'
cache-dependency-path: backend/package-lock.json

- name: Install dependencies
working-directory: backend
run: npm ci

- name: Run lint
working-directory: backend
run: npm run lint

test:
name: Test
runs-on: ubuntu-latest
services:
postgres:
image: postgres:15-alpine
env:
POSTGRES_DB: todo_app_test
POSTGRES_USER: todo_user
POSTGRES_PASSWORD: todo_password
ports:
- 5432:5432
options: >-
--health-cmd "pg_isready -U todo_user -d todo_app_test"
--health-interval 10s
--health-timeout 5s
--health-retries 5
steps:
- uses: actions/checkout@v4

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '18'
cache: 'npm'
cache-dependency-path: backend/package-lock.json

- name: Install dependencies
working-directory: backend
run: npm ci

- name: Run migrations
working-directory: backend
env:
TODO_APP_DB_HOST: localhost
TODO_APP_DB_PORT: 5432
TODO_APP_DB_NAME: todo_app_test
TODO_APP_DB_USER: todo_user
TODO_APP_DB_PASSWORD: todo_password
run: npm run migrate

- name: Run tests
working-directory: backend
env:
TODO_APP_DB_HOST: localhost
TODO_APP_DB_PORT: 5432
TODO_APP_DB_NAME: todo_app_test
TODO_APP_DB_USER: todo_user
TODO_APP_DB_PASSWORD: todo_password
TODO_APP_NODE_ENV: test
run: npm test

build-backend:
name: Build Backend Image
runs-on: ubuntu-latest
needs: [lint, test]
steps:
- uses: actions/checkout@v4

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Build backend image
uses: docker/build-push-action@v5
with:
context: .
file: Dockerfile.backend
push: false
tags: todo-backend:${{ github.sha }}
cache-from: type=gha
cache-to: type=gha,mode=max

build-frontend:
name: Build Frontend Image
runs-on: ubuntu-latest
needs: [lint, test]
steps:
- uses: actions/checkout@v4

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Build frontend image
uses: docker/build-push-action@v5
with:
context: .
file: Dockerfile.frontend
push: false
tags: todo-frontend:${{ github.sha }}
cache-from: type=gha
cache-to: type=gha,mode=max
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,6 @@ node_modules/
*.log
.env
.env.local
.env.*.local
dist/
coverage/
10 changes: 10 additions & 0 deletions .husky/pre-commit
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#!/bin/sh

# Lint backend
(cd backend && npm run lint)

# Secret scanning - check for common secret patterns in staged files
git diff --cached --name-only | xargs grep -l -E '(password|secret|api_key|token)\s*[:=]\s*["\x27][^"\x27]{8,}' 2>/dev/null && {
echo "WARNING: Potential secrets detected in staged files. Please review before committing."
exit 1
} || true
28 changes: 28 additions & 0 deletions Dockerfile.backend
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Stage 1: Install dependencies
FROM node:18-alpine AS deps
WORKDIR /app
COPY backend/package.json backend/package-lock.json ./
RUN npm ci --only=production

# Stage 2: Production image
FROM node:18-alpine AS production
WORKDIR /app

# Create non-root user
RUN addgroup -g 1001 -S appgroup && \
adduser -S appuser -u 1001 -G appgroup

COPY --from=deps /app/node_modules ./node_modules
COPY backend/ ./

# Set ownership
RUN chown -R appuser:appgroup /app

USER appuser

EXPOSE 3000

HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
CMD wget --no-verbose --tries=1 --spider http://localhost:3000/health || exit 1

CMD ["node", "server.js"]
17 changes: 17 additions & 0 deletions Dockerfile.frontend
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
FROM nginx:alpine

# Remove default nginx config
RUN rm /etc/nginx/conf.d/default.conf

# Copy custom nginx config
COPY nginx.conf /etc/nginx/conf.d/default.conf

# Copy frontend files
COPY frontend/ /usr/share/nginx/html/

EXPOSE 8080

HEALTHCHECK --interval=30s --timeout=5s --start-period=5s --retries=3 \
CMD wget --no-verbose --tries=1 --spider http://localhost:8080/ || exit 1

CMD ["nginx", "-g", "daemon off;"]
Loading
Loading