Skip to content

Commit 18ca3e3

Browse files
committed
Resolve Docker networking issue for middleware token verification
Dynamic API client creation to handle different endpoints for server-side (container-to-container) vs client-side (API gateway) requests.
1 parent 584d7f7 commit 18ca3e3

File tree

5 files changed

+164
-23
lines changed

5 files changed

+164
-23
lines changed

ai/usage-log.md

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -281,4 +281,58 @@ Request to implement UserContext for managing user authentication state and crea
281281

282282
---
283283

284+
## Entry 9
285+
286+
# Date/Time:
287+
2025-09-16 16:00
288+
289+
# Tool:
290+
GitHub Copilot (model: Claude Sonnet 4)
291+
292+
# Prompt/Command:
293+
Configure Nginx reverse proxy for PeerPrep frontend and user service with proper routing, CORS handling, and Next.js client-side navigation support.
294+
295+
# Output Summary:
296+
- Configured Nginx API gateway for frontend and user service routing
297+
- Fixed Docker networking issues for login redirects
298+
- Enhanced API configuration for server-side vs client-side calls
299+
300+
# Action Taken:
301+
- [x] Accepted as-is
302+
- [ ] Modified
303+
- [ ] Rejected
304+
305+
# Author Notes:
306+
- Validated correctness, security, and performance of the configuration
307+
308+
---
309+
310+
## Entry 10
311+
312+
# Date/Time:
313+
2025-09-16 16:30
314+
315+
# Tool:
316+
GitHub Copilot (model: Claude Sonnet 4)
317+
318+
# Prompt/Command:
319+
Fix Docker networking bug where middleware (server-side) and browser (client-side) need different API endpoints for same service.
320+
321+
# Output Summary:
322+
- Added dynamic axios client creation to handle different execution contexts
323+
- Implemented separate URLs for server-side (http://user-service:4000) and client-side (http://localhost/api) calls
324+
- Fixed middleware token verification failing due to incorrect API endpoint routing
325+
- Added comprehensive documentation explaining the Docker networking solution
326+
327+
# Action Taken:
328+
- [x] Accepted as-is
329+
- [ ] Modified
330+
- [ ] Rejected
331+
332+
# Author Notes:
333+
- Validated dynamic client creation approach fixes Docker container-to-container communication issues
334+
- Confirmed solution maintains proper API gateway routing for browser requests
335+
336+
---
337+
284338

api-gateway/default.conf

Lines changed: 44 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
# AI Assistance Disclosure:
2+
# Tool: GitHub Copilot (model: Claude Sonnet 4), date: 2025-09-16
3+
# Purpose: To configure Nginx reverse proxy for PeerPrep frontend and user service with proper routing, CORS handling, and Next.js client-side navigation support.
4+
# Author Review: I validated correctness, security, and performance of the configuration.
5+
16
server {
27
listen 80;
38
listen [::]:80;
@@ -17,6 +22,38 @@ server {
1722
proxy_http_version 1.1;
1823
proxy_set_header Upgrade $http_upgrade;
1924
proxy_set_header Connection "upgrade";
25+
26+
# Next.js specific headers for proper routing
27+
proxy_set_header X-Forwarded-Host $host;
28+
proxy_set_header X-Forwarded-Server $host;
29+
proxy_redirect off;
30+
31+
# Ensure proper handling of Next.js routes
32+
try_files $uri $uri/ @nextjs;
33+
}
34+
35+
# Fallback for Next.js client-side routing
36+
location @nextjs {
37+
proxy_pass http://frontend:3000;
38+
proxy_set_header Host $host;
39+
proxy_set_header X-Real-IP $remote_addr;
40+
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
41+
proxy_set_header X-Forwarded-Proto $scheme;
42+
proxy_set_header X-Forwarded-Host $host;
43+
proxy_set_header X-Forwarded-Server $host;
44+
}
45+
46+
# Next.js static files and assets
47+
location /_next/ {
48+
proxy_pass http://frontend:3000;
49+
proxy_set_header Host $host;
50+
proxy_set_header X-Real-IP $remote_addr;
51+
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
52+
proxy_set_header X-Forwarded-Proto $scheme;
53+
54+
# Cache static assets
55+
expires 1y;
56+
add_header Cache-Control "public, immutable";
2057
}
2158

2259
# User Service API
@@ -58,13 +95,13 @@ server {
5895
}
5996

6097
# MongoDB Express Admin Interface (if localdb profile is used)
61-
location /admin/ {
62-
proxy_pass http://mongo-express:8081/;
63-
proxy_set_header Host $host;
64-
proxy_set_header X-Real-IP $remote_addr;
65-
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
66-
proxy_set_header X-Forwarded-Proto $scheme;
67-
}
98+
# location /admin/ {
99+
# proxy_pass http://mongo-express:8081/;
100+
# proxy_set_header Host $host;
101+
# proxy_set_header X-Real-IP $remote_addr;
102+
# proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
103+
# proxy_set_header X-Forwarded-Proto $scheme;
104+
# }
68105

69106
# Health check endpoint
70107
location /health {

docker-compose.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,8 @@ services:
5151
environment:
5252
- NODE_ENV=production # Set Node.js to production mode
5353

54+
### NOTE: LOCAL DATABASE SERVICES YET TO BE TESTED BY ME DO NOT USE YET ###
55+
5456
# MongoDB Database for User Services
5557
mongodb-user-services:
5658
image: mongo:7.0.12

frontend/src/app/components/auth/LoginComponent.tsx

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -67,12 +67,11 @@ export default function LoginForm() {
6767
response.data
6868
);
6969

70-
// Redirect after short delay
70+
// Use router.replace instead of push for better Docker/Nginx compatibility
7171
setTimeout(() => {
72-
// clear the toast
7372
toast.dismiss();
74-
router.push("/home");
75-
}, 500);
73+
router.replace("/home");
74+
}, 1000);
7675

7776
} catch (error) {
7877
console.error('Login error details:', error);

frontend/src/services/userServiceApi.ts

Lines changed: 61 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
1+
/**
2+
* AI Assistance Disclosure:
3+
* Tool: GitHub Copilot (model: Claude Sonnet 4), date: 2025-09-16
4+
* Purpose: To fix Docker networking bug where middleware (server-side) and browser (client-side) need different API endpoints for same service.
5+
* Author Review: I validated correctness, security, and performance of the dynamic client creation approach.
6+
*/
7+
18
// API Configuration for PeerPrep Frontend
29
// Based on docker-compose.yml configuration with API Gateway
310
import axios from 'axios';
@@ -16,19 +23,58 @@ export interface ApiResponse<T = unknown> {
1623
data?: T;
1724
}
1825

19-
// Base URL - All API calls go through the API Gateway on port 80
20-
const BASE_URL = process.env.NODE_ENV === 'production'
21-
? 'http://localhost/api' // Through API Gateway (Docker)
22-
: 'http://localhost:4000'; // Direct to user-service (Local dev)
26+
/**
27+
* Dynamic Base URL Detection
28+
*
29+
* This function determines the correct API endpoint based on execution context:
30+
* - Server-side (middleware): Direct container-to-container communication
31+
* - Client-side (browser): Through API Gateway proxy
32+
*
33+
* This fixes a Docker networking bug where the same code runs in different environments
34+
* and needs different URLs to reach the same service.
35+
*/
36+
const getBaseURL = () => {
37+
// Check if we're running server-side (middleware) or client-side (browser)
38+
const isServerSide = typeof window === 'undefined';
39+
40+
if (process.env.NODE_ENV === 'production') {
41+
if (isServerSide) {
42+
// Server-side: Direct call to user-service container
43+
// This bypasses the API gateway for internal Docker networking
44+
return 'http://user-service:4000';
45+
} else {
46+
// Client-side: Through API Gateway
47+
// Browser requests go through the nginx proxy on localhost
48+
return 'http://localhost/api';
49+
}
50+
} else {
51+
// Development: Direct to user-service (no Docker containers)
52+
return 'http://localhost:4000';
53+
}
54+
};
2355

24-
// Create axios instance with base configuration
25-
const apiClient = axios.create({
26-
baseURL: BASE_URL,
27-
headers: {
28-
'Content-Type': 'application/json',
29-
},
30-
timeout: 10000, // 10 seconds timeout
31-
});
56+
/**
57+
* Dynamic Axios Client Creation
58+
*
59+
* We create a new axios instance for each request instead of using a module-level singleton.
60+
* This is necessary because:
61+
*
62+
* 1. The same module code runs in both server-side (middleware) and client-side (browser) contexts
63+
* 2. Each context needs a different base URL to reach the user service
64+
* 3. A singleton axios instance would "freeze" the URL from the first context that loads it
65+
* 4. Dynamic creation ensures getBaseURL() runs fresh for each request context
66+
*
67+
* This fixes the Docker networking bug where middleware couldn't reach the user service.
68+
*/
69+
const createApiClient = () => {
70+
return axios.create({
71+
baseURL: getBaseURL(), // Fresh URL calculation for current execution context
72+
headers: {
73+
'Content-Type': 'application/json',
74+
},
75+
timeout: 10000, // 10 seconds timeout
76+
});
77+
};
3278

3379
// API Endpoints
3480
const API_ENDPOINTS = {
@@ -44,6 +90,7 @@ const API_ENDPOINTS = {
4490
*/
4591
const verifyToken = async (token: string) => {
4692
try {
93+
const apiClient = createApiClient();
4794
const response = await apiClient.get(`${API_ENDPOINTS.AUTH_SERVICE}/verify-token`, {
4895
headers: {
4996
'Authorization': `Bearer ${token}`
@@ -64,6 +111,7 @@ const verifyToken = async (token: string) => {
64111
*/
65112
const login = async (email: string, password: string) => {
66113
try {
114+
const apiClient = createApiClient();
67115
const response = await apiClient.post(`${API_ENDPOINTS.AUTH_SERVICE}/login`, {
68116
email,
69117
password
@@ -84,6 +132,7 @@ const login = async (email: string, password: string) => {
84132
*/
85133
const signup = async (username: string, email: string, password: string) => {
86134
try {
135+
const apiClient = createApiClient();
87136
const response = await apiClient.post(`${API_ENDPOINTS.USER_SERVICE}/`, {
88137
username,
89138
email,

0 commit comments

Comments
 (0)