because I got bored of customising my CV for every job

chore: update Docker configuration and scripts

+7
.dockerignore
··· 66 66 apps/server/dist/ 67 67 packages/**/dist/ 68 68 69 + # Compiled TypeScript files 70 + **/*.js 71 + **/*.d.ts 72 + **/*.tsbuildinfo 73 + !**/node_modules/** 74 + !**/dist/** 75 + 69 76 # Lerna 70 77 lerna-debug.log
+9
.gitignore
··· 24 24 # Coverage 25 25 coverage/ 26 26 *.lcov 27 + .nyc_output/ 28 + .c8/ 27 29 28 30 # Runtime data 29 31 pids/ ··· 43 45 apps/client/dist/ 44 46 apps/server/dist/ 45 47 packages/**/dist/ 48 + 49 + # Compiled TypeScript files 50 + **/*.js 51 + **/*.d.ts 52 + **/*.tsbuildinfo 53 + !**/node_modules/** 54 + !**/dist/** 46 55 47 56 # Lerna 48 57 lerna-debug.log
+106 -27
docker-compose.yml
··· 2 2 db: 3 3 image: postgres:16-alpine 4 4 environment: 5 - POSTGRES_USER: cv 6 - POSTGRES_PASSWORD: cv 7 - POSTGRES_DB: cv 5 + POSTGRES_USER: ${POSTGRES_USER:-cv} 6 + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-cv} 7 + POSTGRES_DB: ${POSTGRES_DB:-cv} 8 8 ports: 9 - - "5432:5432" 9 + - "${DB_PORT:-5432}:5432" 10 10 volumes: 11 11 - db-data:/var/lib/postgresql/data 12 + healthcheck: 13 + test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}"] 14 + interval: 5s 15 + timeout: 5s 16 + retries: 5 17 + start_period: 10s 12 18 13 19 server: 14 20 build: ··· 17 23 working_dir: /app 18 24 command: sh -c "cd apps/server && npm run prisma:deploy && npm run dev" 19 25 environment: 20 - PORT: 3000 21 - JWT_SECRET: ${JWT_SECRET:-your-super-secret-jwt-key-change-this-in-production} 22 - DATABASE_URL: postgres://cv:cv@db:5432/cv 23 - PRISMA_ENABLE_TRACING: false 26 + PORT: ${SERVER_PORT:-3000} 27 + JWT_SECRET: ${JWT_SECRET:-your-super-secret-jwt-key-here} 28 + JWT_ACCESS_TOKEN_EXPIRY: ${JWT_ACCESS_TOKEN_EXPIRY:-15m} 29 + JWT_REFRESH_TOKEN_EXPIRY: ${JWT_REFRESH_TOKEN_EXPIRY:-7d} 30 + DATABASE_URL: ${DATABASE_URL:-postgresql://cv:cv@db:5432/cv} 31 + POSTGRES_USER: ${POSTGRES_USER:-cv} 32 + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-cv} 33 + POSTGRES_DB: ${POSTGRES_DB:-cv} 34 + PRISMA_ENABLE_TRACING: ${PRISMA_ENABLE_TRACING:-false} 24 35 depends_on: 25 - - db 36 + db: 37 + condition: service_healthy 26 38 ports: 27 - - "3000:3000" 39 + - "${SERVER_PORT:-3000}:${SERVER_PORT:-3000}" 28 40 volumes: 29 41 - .:/app 30 - - server-node-modules:/app/apps/server/node_modules 31 - - server-root-node-modules:/app/node_modules 42 + - npm-cache:/root/.npm 32 43 healthcheck: 33 - test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "-O", "-", "--post-data", "{\"query\":\"query IntrospectionQuery { __schema { queryType { name } } }\"}", "--header", "Content-Type: application/json", "http://localhost:3000/graphql"] 34 - interval: 2s 44 + test: ["CMD", "sh", "/app/scripts/health-check-server.sh"] 45 + interval: 5s 35 46 timeout: 5s 36 - retries: 3 37 - start_period: 10s 47 + retries: 5 48 + start_period: 30s 38 49 39 50 client: 40 - image: node:22-alpine 51 + build: 52 + context: . 53 + dockerfile: apps/client/Dockerfile 54 + target: development 41 55 working_dir: /app 42 - command: sh -c "npm install && npm run codegen && npm run dev -w @cv/client" 56 + command: sh -c "cd apps/client && npm run codegen && npm run dev" 43 57 environment: 44 - VITE_SERVER_URL: http://localhost:3000 45 - GRAPHQL_SCHEMA_URL: http://server:3000/graphql 58 + VITE_SERVER_URL: ${VITE_SERVER_URL:-http://localhost:3000} 59 + VITE_DOCS_URL: ${VITE_DOCS_URL:-http://localhost:3001} 60 + GRAPHQL_SCHEMA_URL: ${GRAPHQL_SCHEMA_URL:-http://localhost:3000/graphql} 46 61 depends_on: 47 62 server: 48 63 condition: service_healthy 49 64 restart: true 50 65 ports: 51 - - "5173:5173" 66 + - "${CLIENT_PORT:-5173}:${CLIENT_PORT:-5173}" 67 + volumes: 68 + - ./apps/client/src:/app/apps/client/src 69 + - ./apps/client/public:/app/apps/client/public 70 + - ./apps/client/index.html:/app/apps/client/index.html 71 + - ./apps/client/vite.config.ts:/app/apps/client/vite.config.ts 72 + - ./apps/client/tsconfig.json:/app/apps/client/tsconfig.json 73 + - ./apps/client/package.json:/app/apps/client/package.json 74 + - ./apps/client/package-lock.json:/app/apps/client/package-lock.json 75 + - ./packages:/app/packages 76 + - npm-cache:/root/.npm 77 + - /app/apps/client/node_modules # Anonymous volume to prevent host node_modules from overriding 78 + healthcheck: 79 + test: ["CMD", "/app/scripts/health-check-client.sh"] 80 + interval: 10s 81 + timeout: 5s 82 + retries: 3 83 + start_period: 30s 84 + 85 + docs: 86 + build: 87 + context: . 88 + dockerfile: apps/docs/Dockerfile 89 + target: development 90 + working_dir: /app 91 + command: sh -c "cd apps/docs && npm run dev" 92 + environment: 93 + VITE_CLIENT_URL: ${VITE_CLIENT_URL:-http://localhost:5173} 94 + VITE_SERVER_URL: ${VITE_SERVER_URL:-http://localhost:3000} 95 + VITE_DOCS_URL: ${VITE_DOCS_URL:-http://localhost:3001} 96 + VITE_GRAPHQL_URL: ${VITE_GRAPHQL_URL:-http://localhost:3000/graphql} 97 + VITE_DB_HOST: ${VITE_DB_HOST:-localhost} 98 + VITE_DB_PORT: ${VITE_DB_PORT:-5432} 99 + ports: 100 + - "${DOCS_PORT:-3001}:3001" 101 + volumes: 102 + - ./apps/docs/src:/app/apps/docs/src 103 + - ./apps/docs/content:/app/apps/docs/content 104 + - /app/node_modules 105 + - npm-cache:/root/.npm 106 + healthcheck: 107 + test: ["CMD", "curl", "-f", "http://localhost:3001"] 108 + interval: 10s 109 + timeout: 5s 110 + retries: 3 111 + start_period: 10s 112 + 113 + prisma-studio: 114 + build: 115 + context: . 116 + dockerfile: apps/server/Dockerfile 117 + working_dir: /app/apps/server 118 + command: npx prisma studio --hostname 0.0.0.0 --port 5555 119 + environment: 120 + DATABASE_URL: ${DATABASE_URL:-postgresql://cv:cv@db:5432/cv} 121 + POSTGRES_USER: ${POSTGRES_USER:-cv} 122 + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-cv} 123 + POSTGRES_DB: ${POSTGRES_DB:-cv} 124 + depends_on: 125 + db: 126 + condition: service_healthy 127 + ports: 128 + - "5555:5555" 52 129 volumes: 53 130 - .:/app 54 - - client-node-modules:/app/apps/client/node_modules 55 - - client-root-node-modules:/app/node_modules 131 + - npm-cache:/root/.npm 132 + healthcheck: 133 + test: ["CMD-SHELL", "curl -f http://localhost:5555 || exit 1"] 134 + interval: 5s 135 + timeout: 5s 136 + retries: 5 137 + start_period: 15s 56 138 57 139 volumes: 58 140 db-data: 59 - server-node-modules: 60 - client-node-modules: 61 - server-root-node-modules: 62 - client-root-node-modules: 141 + npm-cache: 63 142
-65
scripts/generate-local-copies.js
··· 1 - #!/usr/bin/env node 2 - 3 - /** 4 - * Script to generate local copies of generated files for syntax highlighting 5 - * These files are excluded from git but needed for IDE support 6 - */ 7 - 8 - const fs = require('fs'); 9 - const path = require('path'); 10 - 11 - const projectRoot = path.join(__dirname, '..'); 12 - 13 - // Files to copy for local development 14 - const filesToCopy = [ 15 - { 16 - source: 'apps/client/src/generated/graphql.ts', 17 - destination: 'apps/client/src/generated-local/graphql.ts', 18 - description: 'GraphQL generated types and hooks' 19 - } 20 - ]; 21 - 22 - // Prisma client files to copy (if they exist) 23 - const prismaClientPath = path.join(projectRoot, 'apps/server/node_modules/@prisma/client'); 24 - if (fs.existsSync(prismaClientPath)) { 25 - const prismaFiles = fs.readdirSync(prismaClientPath, { recursive: true }) 26 - .filter(file => typeof file === 'string' && file.endsWith('.d.ts')) 27 - .slice(0, 5); // Limit to first 5 files to avoid too many copies 28 - 29 - prismaFiles.forEach(file => { 30 - filesToCopy.push({ 31 - source: `apps/server/node_modules/@prisma/client/${file}`, 32 - destination: `apps/server/src/generated-local/prisma/${file}`, 33 - description: 'Prisma client types' 34 - }); 35 - }); 36 - } 37 - 38 - console.log('🔄 Generating local copies of generated files for syntax highlighting...\n'); 39 - 40 - filesToCopy.forEach(({ source, destination, description }) => { 41 - const sourcePath = path.join(projectRoot, source); 42 - const destPath = path.join(projectRoot, destination); 43 - const destDir = path.dirname(destPath); 44 - 45 - try { 46 - // Create destination directory if it doesn't exist 47 - if (!fs.existsSync(destDir)) { 48 - fs.mkdirSync(destDir, { recursive: true }); 49 - } 50 - 51 - // Copy file if source exists 52 - if (fs.existsSync(sourcePath)) { 53 - fs.copyFileSync(sourcePath, destPath); 54 - console.log(`✅ Copied: ${description}`); 55 - console.log(` ${source} → ${destination}`); 56 - } else { 57 - console.log(`⚠️ Source not found: ${source}`); 58 - } 59 - } catch (error) { 60 - console.error(`❌ Error copying ${source}:`, error.message); 61 - } 62 - }); 63 - 64 - console.log('\n🎉 Local copies generated successfully!'); 65 - console.log('📝 Note: These files are excluded from git but provide IDE support.');
+53
scripts/health-check-client.sh
··· 1 + #!/bin/sh 2 + 3 + # Check if Vite server is responding 4 + if ! curl -f -s "http://localhost:${CLIENT_PORT:-5173}" > /dev/null; then 5 + echo "Client server not responding" 6 + exit 1 7 + fi 8 + 9 + # Check for HMR WebSocket client availability 10 + if ! curl -f -s "http://localhost:${CLIENT_PORT:-5173}/@vite/client" > /dev/null; then 11 + echo "HMR client not available" 12 + exit 1 13 + fi 14 + 15 + # Check if the main HTML page loads without errors 16 + RESPONSE=$(curl -s "http://localhost:${CLIENT_PORT:-5173}") 17 + if ! echo "$RESPONSE" | grep -q "html"; then 18 + echo "Main page not loading properly" 19 + exit 1 20 + fi 21 + 22 + # Check if there are any Vite error messages in the response 23 + if echo "$RESPONSE" | grep -qi "error\|failed\|cannot"; then 24 + echo "Vite errors detected in response" 25 + exit 1 26 + fi 27 + 28 + # Check for import binding errors (GraphQL codegen issues) 29 + if echo "$RESPONSE" | grep -qi "importing binding"; then 30 + echo "Import binding errors detected - codegen may be out of sync" 31 + exit 1 32 + fi 33 + 34 + # Check for TypeScript compilation errors 35 + if echo "$RESPONSE" | grep -qi "syntaxerror\|referenceerror"; then 36 + echo "JavaScript syntax/reference errors detected" 37 + exit 1 38 + fi 39 + 40 + # Verify generated GraphQL file exists and is not empty 41 + if [ ! -f "/app/apps/client/src/generated/graphql.ts" ]; then 42 + echo "Generated GraphQL types file missing" 43 + exit 1 44 + fi 45 + 46 + if [ ! -s "/app/apps/client/src/generated/graphql.ts" ]; then 47 + echo "Generated GraphQL types file is empty" 48 + exit 1 49 + fi 50 + 51 + echo "Client health check passed" 52 + exit 0 53 +
+34
scripts/health-check-server.sh
··· 1 + #!/bin/sh 2 + 3 + # Server health check script 4 + # Checks if the GraphQL server is responding with a proper health query 5 + 6 + # Default values 7 + SERVER_PORT=${SERVER_PORT:-3000} 8 + HEALTH_QUERY='{"query":"query HealthCheck { __typename }"}' 9 + 10 + # Check if the GraphQL server is responding 11 + if ! curl -f -s \ 12 + --request POST \ 13 + --header "Content-Type: application/json" \ 14 + --data "$HEALTH_QUERY" \ 15 + "http://localhost:${SERVER_PORT}/graphql" > /dev/null; then 16 + echo "GraphQL server not responding on port ${SERVER_PORT}" 17 + exit 1 18 + fi 19 + 20 + # Check if the response contains expected GraphQL structure 21 + RESPONSE=$(curl -s \ 22 + --request POST \ 23 + --header "Content-Type: application/json" \ 24 + --data "$HEALTH_QUERY" \ 25 + "http://localhost:${SERVER_PORT}/graphql") 26 + 27 + if ! echo "$RESPONSE" | grep -q '"data"'; then 28 + echo "GraphQL server not returning valid response" 29 + echo "Response: $RESPONSE" 30 + exit 1 31 + fi 32 + 33 + echo "Server health check passed" 34 + exit 0