+7
.dockerignore
+7
.dockerignore
+9
.gitignore
+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
+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
-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
+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
+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