diff --git a/.dockerignore b/.dockerignore index 20a0603..30b60dc 100644 --- a/.dockerignore +++ b/.dockerignore @@ -66,5 +66,12 @@ apps/client/dist/ apps/server/dist/ packages/**/dist/ +# Compiled TypeScript files +**/*.js +**/*.d.ts +**/*.tsbuildinfo +!**/node_modules/** +!**/dist/** + # Lerna lerna-debug.log diff --git a/.gitignore b/.gitignore index c81d0b1..3dc7b21 100644 --- a/.gitignore +++ b/.gitignore @@ -24,6 +24,8 @@ logs/ # Coverage coverage/ *.lcov +.nyc_output/ +.c8/ # Runtime data pids/ @@ -44,5 +46,12 @@ apps/client/dist/ apps/server/dist/ packages/**/dist/ +# Compiled TypeScript files +**/*.js +**/*.d.ts +**/*.tsbuildinfo +!**/node_modules/** +!**/dist/** + # Lerna lerna-debug.log diff --git a/docker-compose.yml b/docker-compose.yml index 352de6c..d243096 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -2,13 +2,19 @@ services: db: image: postgres:16-alpine environment: - POSTGRES_USER: cv - POSTGRES_PASSWORD: cv - POSTGRES_DB: cv + POSTGRES_USER: ${POSTGRES_USER:-cv} + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-cv} + POSTGRES_DB: ${POSTGRES_DB:-cv} ports: - - "5432:5432" + - "${DB_PORT:-5432}:5432" volumes: - db-data:/var/lib/postgresql/data + healthcheck: + test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}"] + interval: 5s + timeout: 5s + retries: 5 + start_period: 10s server: build: @@ -17,47 +23,120 @@ services: working_dir: /app command: sh -c "cd apps/server && npm run prisma:deploy && npm run dev" environment: - PORT: 3000 - JWT_SECRET: ${JWT_SECRET:-your-super-secret-jwt-key-change-this-in-production} - DATABASE_URL: postgres://cv:cv@db:5432/cv - PRISMA_ENABLE_TRACING: false + PORT: ${SERVER_PORT:-3000} + JWT_SECRET: ${JWT_SECRET:-your-super-secret-jwt-key-here} + JWT_ACCESS_TOKEN_EXPIRY: ${JWT_ACCESS_TOKEN_EXPIRY:-15m} + JWT_REFRESH_TOKEN_EXPIRY: ${JWT_REFRESH_TOKEN_EXPIRY:-7d} + DATABASE_URL: ${DATABASE_URL:-postgresql://cv:cv@db:5432/cv} + POSTGRES_USER: ${POSTGRES_USER:-cv} + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-cv} + POSTGRES_DB: ${POSTGRES_DB:-cv} + PRISMA_ENABLE_TRACING: ${PRISMA_ENABLE_TRACING:-false} depends_on: - - db + db: + condition: service_healthy ports: - - "3000:3000" + - "${SERVER_PORT:-3000}:${SERVER_PORT:-3000}" volumes: - .:/app - - server-node-modules:/app/apps/server/node_modules - - server-root-node-modules:/app/node_modules + - npm-cache:/root/.npm healthcheck: - 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"] - interval: 2s + test: ["CMD", "sh", "/app/scripts/health-check-server.sh"] + interval: 5s timeout: 5s - retries: 3 - start_period: 10s + retries: 5 + start_period: 30s client: - image: node:22-alpine + build: + context: . + dockerfile: apps/client/Dockerfile + target: development working_dir: /app - command: sh -c "npm install && npm run codegen && npm run dev -w @cv/client" + command: sh -c "cd apps/client && npm run codegen && npm run dev" environment: - VITE_SERVER_URL: http://localhost:3000 - GRAPHQL_SCHEMA_URL: http://server:3000/graphql + VITE_SERVER_URL: ${VITE_SERVER_URL:-http://localhost:3000} + VITE_DOCS_URL: ${VITE_DOCS_URL:-http://localhost:3001} + GRAPHQL_SCHEMA_URL: ${GRAPHQL_SCHEMA_URL:-http://localhost:3000/graphql} depends_on: server: condition: service_healthy restart: true ports: - - "5173:5173" + - "${CLIENT_PORT:-5173}:${CLIENT_PORT:-5173}" + volumes: + - ./apps/client/src:/app/apps/client/src + - ./apps/client/public:/app/apps/client/public + - ./apps/client/index.html:/app/apps/client/index.html + - ./apps/client/vite.config.ts:/app/apps/client/vite.config.ts + - ./apps/client/tsconfig.json:/app/apps/client/tsconfig.json + - ./apps/client/package.json:/app/apps/client/package.json + - ./apps/client/package-lock.json:/app/apps/client/package-lock.json + - ./packages:/app/packages + - npm-cache:/root/.npm + - /app/apps/client/node_modules # Anonymous volume to prevent host node_modules from overriding + healthcheck: + test: ["CMD", "/app/scripts/health-check-client.sh"] + interval: 10s + timeout: 5s + retries: 3 + start_period: 30s + + docs: + build: + context: . + dockerfile: apps/docs/Dockerfile + target: development + working_dir: /app + command: sh -c "cd apps/docs && npm run dev" + environment: + VITE_CLIENT_URL: ${VITE_CLIENT_URL:-http://localhost:5173} + VITE_SERVER_URL: ${VITE_SERVER_URL:-http://localhost:3000} + VITE_DOCS_URL: ${VITE_DOCS_URL:-http://localhost:3001} + VITE_GRAPHQL_URL: ${VITE_GRAPHQL_URL:-http://localhost:3000/graphql} + VITE_DB_HOST: ${VITE_DB_HOST:-localhost} + VITE_DB_PORT: ${VITE_DB_PORT:-5432} + ports: + - "${DOCS_PORT:-3001}:3001" + volumes: + - ./apps/docs/src:/app/apps/docs/src + - ./apps/docs/content:/app/apps/docs/content + - /app/node_modules + - npm-cache:/root/.npm + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:3001"] + interval: 10s + timeout: 5s + retries: 3 + start_period: 10s + + prisma-studio: + build: + context: . + dockerfile: apps/server/Dockerfile + working_dir: /app/apps/server + command: npx prisma studio --hostname 0.0.0.0 --port 5555 + environment: + DATABASE_URL: ${DATABASE_URL:-postgresql://cv:cv@db:5432/cv} + POSTGRES_USER: ${POSTGRES_USER:-cv} + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-cv} + POSTGRES_DB: ${POSTGRES_DB:-cv} + depends_on: + db: + condition: service_healthy + ports: + - "5555:5555" volumes: - .:/app - - client-node-modules:/app/apps/client/node_modules - - client-root-node-modules:/app/node_modules + - npm-cache:/root/.npm + healthcheck: + test: ["CMD-SHELL", "curl -f http://localhost:5555 || exit 1"] + interval: 5s + timeout: 5s + retries: 5 + start_period: 15s volumes: db-data: - server-node-modules: - client-node-modules: - server-root-node-modules: - client-root-node-modules: + npm-cache: diff --git a/scripts/generate-local-copies.js b/scripts/generate-local-copies.js deleted file mode 100755 index 9b986d0..0000000 --- a/scripts/generate-local-copies.js +++ /dev/null @@ -1,65 +0,0 @@ -#!/usr/bin/env node - -/** - * Script to generate local copies of generated files for syntax highlighting - * These files are excluded from git but needed for IDE support - */ - -const fs = require('fs'); -const path = require('path'); - -const projectRoot = path.join(__dirname, '..'); - -// Files to copy for local development -const filesToCopy = [ - { - source: 'apps/client/src/generated/graphql.ts', - destination: 'apps/client/src/generated-local/graphql.ts', - description: 'GraphQL generated types and hooks' - } -]; - -// Prisma client files to copy (if they exist) -const prismaClientPath = path.join(projectRoot, 'apps/server/node_modules/@prisma/client'); -if (fs.existsSync(prismaClientPath)) { - const prismaFiles = fs.readdirSync(prismaClientPath, { recursive: true }) - .filter(file => typeof file === 'string' && file.endsWith('.d.ts')) - .slice(0, 5); // Limit to first 5 files to avoid too many copies - - prismaFiles.forEach(file => { - filesToCopy.push({ - source: `apps/server/node_modules/@prisma/client/${file}`, - destination: `apps/server/src/generated-local/prisma/${file}`, - description: 'Prisma client types' - }); - }); -} - -console.log('šŸ”„ Generating local copies of generated files for syntax highlighting...\n'); - -filesToCopy.forEach(({ source, destination, description }) => { - const sourcePath = path.join(projectRoot, source); - const destPath = path.join(projectRoot, destination); - const destDir = path.dirname(destPath); - - try { - // Create destination directory if it doesn't exist - if (!fs.existsSync(destDir)) { - fs.mkdirSync(destDir, { recursive: true }); - } - - // Copy file if source exists - if (fs.existsSync(sourcePath)) { - fs.copyFileSync(sourcePath, destPath); - console.log(`āœ… Copied: ${description}`); - console.log(` ${source} → ${destination}`); - } else { - console.log(`āš ļø Source not found: ${source}`); - } - } catch (error) { - console.error(`āŒ Error copying ${source}:`, error.message); - } -}); - -console.log('\nšŸŽ‰ Local copies generated successfully!'); -console.log('šŸ“ Note: These files are excluded from git but provide IDE support.'); diff --git a/scripts/health-check-client.sh b/scripts/health-check-client.sh new file mode 100755 index 0000000..8765cff --- /dev/null +++ b/scripts/health-check-client.sh @@ -0,0 +1,53 @@ +#!/bin/sh + +# Check if Vite server is responding +if ! curl -f -s "http://localhost:${CLIENT_PORT:-5173}" > /dev/null; then + echo "Client server not responding" + exit 1 +fi + +# Check for HMR WebSocket client availability +if ! curl -f -s "http://localhost:${CLIENT_PORT:-5173}/@vite/client" > /dev/null; then + echo "HMR client not available" + exit 1 +fi + +# Check if the main HTML page loads without errors +RESPONSE=$(curl -s "http://localhost:${CLIENT_PORT:-5173}") +if ! echo "$RESPONSE" | grep -q "html"; then + echo "Main page not loading properly" + exit 1 +fi + +# Check if there are any Vite error messages in the response +if echo "$RESPONSE" | grep -qi "error\|failed\|cannot"; then + echo "Vite errors detected in response" + exit 1 +fi + +# Check for import binding errors (GraphQL codegen issues) +if echo "$RESPONSE" | grep -qi "importing binding"; then + echo "Import binding errors detected - codegen may be out of sync" + exit 1 +fi + +# Check for TypeScript compilation errors +if echo "$RESPONSE" | grep -qi "syntaxerror\|referenceerror"; then + echo "JavaScript syntax/reference errors detected" + exit 1 +fi + +# Verify generated GraphQL file exists and is not empty +if [ ! -f "/app/apps/client/src/generated/graphql.ts" ]; then + echo "Generated GraphQL types file missing" + exit 1 +fi + +if [ ! -s "/app/apps/client/src/generated/graphql.ts" ]; then + echo "Generated GraphQL types file is empty" + exit 1 +fi + +echo "Client health check passed" +exit 0 + diff --git a/scripts/health-check-server.sh b/scripts/health-check-server.sh new file mode 100644 index 0000000..486b000 --- /dev/null +++ b/scripts/health-check-server.sh @@ -0,0 +1,34 @@ +#!/bin/sh + +# Server health check script +# Checks if the GraphQL server is responding with a proper health query + +# Default values +SERVER_PORT=${SERVER_PORT:-3000} +HEALTH_QUERY='{"query":"query HealthCheck { __typename }"}' + +# Check if the GraphQL server is responding +if ! curl -f -s \ + --request POST \ + --header "Content-Type: application/json" \ + --data "$HEALTH_QUERY" \ + "http://localhost:${SERVER_PORT}/graphql" > /dev/null; then + echo "GraphQL server not responding on port ${SERVER_PORT}" + exit 1 +fi + +# Check if the response contains expected GraphQL structure +RESPONSE=$(curl -s \ + --request POST \ + --header "Content-Type: application/json" \ + --data "$HEALTH_QUERY" \ + "http://localhost:${SERVER_PORT}/graphql") + +if ! echo "$RESPONSE" | grep -q '"data"'; then + echo "GraphQL server not returning valid response" + echo "Response: $RESPONSE" + exit 1 +fi + +echo "Server health check passed" +exit 0 \ No newline at end of file