diff --git a/.env.example b/.env.example index 27a739c..524fa0a 100644 --- a/.env.example +++ b/.env.example @@ -1,67 +1,32 @@ -# API Gateway Configuration -API_GATEWAY_PORT=3000 +# Krai / Guap Finance — local development template. +# Copy to .env and adjust as needed. Keep .env out of source control. + +# Application NODE_ENV=development -AUTH_SERVICE_URL=http://auth-service:3001 -COURSE_SERVICE_URL=http://course-service:3002 -PROGRESS_SERVICE_URL=http://progress-service:3003 +PORT=3000 +DEMO_MODE=true +LOG_LEVEL=debug +LOG_FORMAT=pretty -# Auth Service Configuration -AUTH_SERVICE_PORT=3001 -JWT_SECRET=your-super-secret-jwt-key-change-in-production -JWT_REFRESH_SECRET=your-super-secret-refresh-key-change-in-production +# JWT — generate with: openssl rand -hex 48 +JWT_SECRET=replace-with-32-or-more-chars-from-openssl-rand-hex-48 +JWT_REFRESH_SECRET=replace-with-32-or-more-chars-from-openssl-rand-hex-48 JWT_EXPIRATION=1h JWT_REFRESH_EXPIRATION=7d +BCRYPT_ROUNDS=4 -# Course Service Configuration -COURSE_SERVICE_PORT=3002 -STORAGE_TYPE=local -STORAGE_PATH=/app/uploads -MAX_FILE_SIZE=52428800 - -# Progress Service Configuration -PROGRESS_SERVICE_PORT=3003 - -# Database Configuration -DB_HOST=postgres -DB_PORT=5432 -DB_USER=postgres -DB_PASSWORD=postgres123 -DB_NAME=guap_finance_auth - -# Linode Managed PostgreSQL (Production) -LINODE_DB_HOST=your-linode-db-host.akamaidb.net -LINODE_DB_PORT=21542 -LINODE_DB_USER=akmadmin -LINODE_DB_PASSWORD=your-linode-db-password -LINODE_DB_NAME=guap_finance_courses - -# Redis Configuration -REDIS_HOST=redis -REDIS_PORT=6379 -REDIS_PASSWORD= -REDIS_URL=redis://redis:6379 - -# Email Configuration (SMTP) -SMTP_HOST=smtp.gmail.com -SMTP_PORT=587 -SMTP_USER=your-email@gmail.com -SMTP_PASSWORD=your-app-password -SMTP_FROM=noreply@guapfinance.com - -# Frontend Configuration -NEXT_PUBLIC_API_URL=http://localhost:3000 -FRONTEND_URL=http://localhost:8000 - -# AWS Configuration (if using S3) -AWS_REGION=us-east-1 -AWS_ACCESS_KEY_ID=your-access-key -AWS_SECRET_ACCESS_KEY=your-secret-key -AWS_S3_BUCKET=guap-finance-uploads - -# Logging -LOG_LEVEL=debug +# CORS — comma-separated list of allowed origins +CORS_ORIGINS=http://localhost:3000,http://localhost:8000 -# Security -CORS_ORIGIN=http://localhost:8000 +# Rate limiting RATE_LIMIT_WINDOW_MS=900000 -RATE_LIMIT_MAX_REQUESTS=100 +RATE_LIMIT_MAX=300 +AUTH_RATE_LIMIT_MAX=20 + +# PostgreSQL — leave blank to run in in-memory demo mode +LINODE_DB_HOST= +LINODE_DB_PORT=5432 +LINODE_DB_USER= +LINODE_DB_PASSWORD= +LINODE_DB_NAME=guap_finance +LINODE_DB_SSL=true diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml deleted file mode 100644 index 6e494fb..0000000 --- a/.github/workflows/cd.yml +++ /dev/null @@ -1,90 +0,0 @@ -name: CD Deploy - -on: - push: - branches: [main] - workflow_dispatch: - inputs: - environment: - description: 'Deployment Environment' - required: true - default: 'staging' - type: choice - options: - - staging - - production - -jobs: - deploy: - runs-on: ubuntu-latest - environment: ${{ github.event.inputs.environment || 'staging' }} - - steps: - - uses: actions/checkout@v4 - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 - - - name: Login to Docker Hub - uses: docker/login-action@v3 - with: - username: ${{ secrets.DOCKER_USERNAME }} - password: ${{ secrets.DOCKER_PASSWORD }} - - - name: Extract Git SHA - run: echo "GIT_SHA=${GITHUB_SHA:0:7}" >> $GITHUB_ENV - - - name: Build and Push auth-service - uses: docker/build-push-action@v5 - with: - context: . - file: ./services/auth-service/Dockerfile - push: true - tags: | - guap-finance/auth-service:${{ env.GIT_SHA }} - guap-finance/auth-service:latest - - - name: Build and Push course-service - uses: docker/build-push-action@v5 - with: - context: . - file: ./services/course-service/Dockerfile - push: true - tags: | - guap-finance/course-service:${{ env.GIT_SHA }} - guap-finance/course-service:latest - - - name: Build and Push progress-service - uses: docker/build-push-action@v5 - with: - context: . - file: ./services/progress-service/Dockerfile - push: true - tags: | - guap-finance/progress-service:${{ env.GIT_SHA }} - guap-finance/progress-service:latest - - - name: Build and Push api-gateway - uses: docker/build-push-action@v5 - with: - context: . - file: ./api-gateway/Dockerfile - push: true - tags: | - guap-finance/api-gateway:${{ env.GIT_SHA }} - guap-finance/api-gateway:latest - - - name: Build and Push frontend - uses: docker/build-push-action@v5 - with: - context: . - file: ./frontend/Dockerfile - push: true - tags: | - guap-finance/frontend:${{ env.GIT_SHA }} - guap-finance/frontend:latest - - - name: Update deployment status - run: | - echo "Deployment completed successfully!" - echo "Image tags: ${{ env.GIT_SHA }} and latest pushed to Docker Hub" \ No newline at end of file diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8619084..27bcc75 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -52,35 +52,6 @@ jobs: - name: Build run: npm run build - services: - name: ${{ matrix.svc }} — build - runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - svc: [auth-service, course-service, progress-service] - defaults: - run: - working-directory: services/${{ matrix.svc }} - steps: - - uses: actions/checkout@v4 - - - uses: actions/setup-node@v4 - with: - node-version: '20' - cache: npm - cache-dependency-path: services/${{ matrix.svc }}/package-lock.json - - - name: Install - run: npm ci --no-audit --no-fund - - - name: Lint - run: npm run lint - continue-on-error: true - - - name: Build - run: npm run build - security: name: Security audit runs-on: ubuntu-latest @@ -111,9 +82,9 @@ jobs: format: table docker: - name: Docker — build images + name: Docker — build image runs-on: ubuntu-latest - needs: [api-gateway, services] + needs: api-gateway steps: - uses: actions/checkout@v4 @@ -137,32 +108,7 @@ jobs: -e JWT_REFRESH_SECRET="$(openssl rand -hex 48)" \ -e NODE_ENV=production \ krai/api-gateway:ci - sleep 5 - curl --fail --silent --show-error --retry 10 --retry-delay 2 \ - http://localhost:3000/health + curl --fail --silent --show-error --retry 15 --retry-delay 2 \ + --retry-connrefused http://localhost:3000/health docker logs krai-gw | tail -30 docker rm -f krai-gw - - - name: Build auth-service image - uses: docker/build-push-action@v5 - with: - context: . - file: ./services/auth-service/Dockerfile - push: false - tags: krai/auth-service:ci - - - name: Build course-service image - uses: docker/build-push-action@v5 - with: - context: . - file: ./services/course-service/Dockerfile - push: false - tags: krai/course-service:ci - - - name: Build progress-service image - uses: docker/build-push-action@v5 - with: - context: . - file: ./services/progress-service/Dockerfile - push: false - tags: krai/progress-service:ci diff --git a/.github/workflows/infrastructure.yml b/.github/workflows/infrastructure.yml deleted file mode 100644 index 5ec875d..0000000 --- a/.github/workflows/infrastructure.yml +++ /dev/null @@ -1,72 +0,0 @@ -name: Infrastructure Deploy - -on: - push: - branches: - - main - paths: - - 'infrastructure/**' - - 'k8s/**' - workflow_dispatch: - inputs: - environment: - description: 'Environment' - required: true - default: 'staging' - type: choice - options: - - staging - - production - -jobs: - terraform: - runs-on: ubuntu-latest - defaults: - run: - working-directory: infrastructure/terraform/environments/${{ github.event.inputs.environment || 'staging' }} - - steps: - - uses: actions/checkout@v4 - - - name: Setup Terraform - uses: hashicorp/setup-terraform@v3 - with: - terraform_version: 1.6.0 - - - name: Terraform Init - id: init - run: terraform init -reconfigure -input=false - - - name: Terraform Plan - id: plan - run: terraform plan -input=false -out=tfplan - continue-on-error: true - - - name: Terraform Apply - if: github.event_name == 'push' && github.ref == 'refs/heads/main' - run: terraform apply -input=false -auto-approve tfplan - - kubernetes-deploy: - needs: terraform - runs-on: ubuntu-latest - environment: ${{ github.event.inputs.environment || 'staging' }} - - steps: - - uses: actions/checkout@v4 - - - name: Setup kubectl - uses: azure/setup-kubectl@v4 - - - name: Configure kubectl - run: | - echo "${{ secrets.KUBE_CONFIG }}" | base64 -d > kube_config - echo "KUBECONFIG=$(pwd)/kube_config" >> $GITHUB_ENV - - - name: Deploy to Kubernetes - run: | - kubectl apply -f k8s/ --namespace=guap-finance - kubectl rollout status deployment/auth-service -n guap-finance - kubectl rollout status deployment/course-service -n guap-finance - kubectl rollout status deployment/progress-service -n guap-finance - kubectl rollout status deployment/api-gateway -n guap-finance - kubectl rollout status deployment/frontend -n guap-finance diff --git a/CLAUDE.md b/CLAUDE.md index 15cfd7f..bc888c9 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -3,18 +3,14 @@ ## What this repo is A credit-education platform with a Node/TypeScript backend, vanilla-HTML -frontend, and PostgreSQL persistence. Two deployment shapes: - -- **Single-VPS (recommended)** — the modular `api-gateway/` (Express + zod) - serves both the API and the static frontend, behind nginx + Let's Encrypt. -- **Microservices** — three TypeORM-backed services (`auth-service`, - `course-service`, `progress-service`) plus the gateway, orchestrated by - `docker/docker-compose.yml`. +frontend, and PostgreSQL persistence. Single deployment shape: the +modular `api-gateway/` (Express + zod) serves both the API and the static +frontend, behind nginx + Let's Encrypt on a single VPS. ## Layout ``` -api-gateway/ enterprise gateway; primary entry point +api-gateway/ the gateway — sole entry point src/ config/env.ts zod-validated env loader (the source of truth) middleware/ requestId, security (helmet+cors), auth, rate-limit, @@ -26,8 +22,7 @@ api-gateway/ enterprise gateway; primary entry point db/ pool.ts, migrate.ts (file-based SQL migrations) app.ts composition root server.ts listen + graceful shutdown - tests/ jest unit + supertest integration; 26 tests -services/ legacy microservices (auth/course/progress) + tests/ jest unit + supertest integration; 28 tests infrastructure/ migrations/ canonical SQL schema (run by api-gateway/dist/db/migrate.js) nginx/ krai.conf + krai-proxy.conf snippet for nginx vhost @@ -35,10 +30,9 @@ infrastructure/ backup/ backup-postgres.sh (daily cron-friendly dump) monitoring/ prometheus.yml docker/ - docker-compose.yml full microservices dev stack docker-compose.prod.yml single-VPS production stack deploy-vps.sh one-shot provisioning for a fresh Ubuntu VPS -frontend/ static HTML +frontend/ static HTML (served by the gateway) data/courses-full.json canonical course catalog ``` @@ -47,7 +41,7 @@ data/courses-full.json canonical course catalog ```bash cd api-gateway npm install -npm test # 26 tests, ~5s +npm test # 28 tests, ~5s npm run dev # ts-node-dev on :3000 ``` @@ -86,13 +80,6 @@ systemd unit and nginx vhost; obtains an SSL cert; locks down the firewall. - systemd unit hardened (NoNewPrivileges, ProtectSystem=strict, CapabilityBoundingSet=, MemoryDenyWriteExecute, syscall filter). -## What still uses the legacy microservices - -`services/{auth,course,progress}-service/` are intact but no longer the -primary path. The gateway implements the full feature set; the -microservices are kept for the existing K8s manifests / Docker Compose -demo. Treat the gateway as the source of truth for new business logic. - ## When making changes - Add tests under `api-gateway/tests/`. `npm test` is fast (~5s) and the diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 3c8dfd2..e7a865d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,172 +1,71 @@ -# Contributing to Guap Finance +# Contributing -Thank you for your interest in contributing to Guap Finance! +## Development setup -## Code of Conduct - -By participating in this project, you agree to maintain a respectful and inclusive environment for everyone. - -## How to Contribute - -### 1. Fork the Repository - -Click the "Fork" button on GitHub to create your own copy of the repository. - -### 2. Clone Your Fork - -```bash -git clone https://github.com/YOUR_USERNAME/Tutorial-Credit-Course.git -cd Tutorial-Credit-Course -``` - -### 3. Create a Feature Branch +Requires Node.js 20+. PostgreSQL is optional — the gateway runs in +in-memory demo mode when no `LINODE_DB_*` env vars are set. ```bash -git checkout -b feature/your-feature-name +git clone +cd Tutorial-Credit-Course/api-gateway +npm install +npm test # 28 tests, ~5s +npm run dev # http://localhost:3000 ``` -### 4. Make Your Changes - -- Write clean, maintainable code -- Follow existing code style -- Add comments for complex logic -- Update documentation as needed - -### 5. Commit Your Changes +## Branching -We use Conventional Commits: +Create a feature branch off `main`: ```bash -git commit -m "feat: add new course category feature" -git commit -m "fix: resolve login timeout issue" -git commit -m "docs: update API documentation" -git commit -m "test: add unit tests for progress service" -git commit -m "refactor: improve auth token validation" +git checkout -b feature/short-description ``` -### 6. Push to Your Fork +## Code standards -```bash -git push origin feature/your-feature-name -``` - -### 7. Open a Pull Request +- TypeScript strict mode; no `any` (ESLint enforces this). +- All new routes wrap async handlers with `asyncHandler` and validate + input with `validate(zodSchema)`. +- New env vars: add to `src/config/env.ts` (zod schema), + `.env.prod.example`, and `infrastructure/systemd/gateway.env.example`. +- New SQL: drop a numbered file in `infrastructure/migrations/` — the + migrator is sequential and checksum-aware. +- Never weaken security defaults (rate limits, CORS, bcrypt rounds) + without flagging it in the PR description. -1. Go to the original repository -2. Click "New Pull Request" -3. Select your branch -4. Fill out the PR template - -## Commit Message Format - -``` -(): +## Required checks before opening a PR - - -