mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-14 18:01:09 +02:00
docs: add client-side env injection and CORS troubleshooting
Documented two additional staging deployment issues discovered today: Problem 5: Client-Side Calling localhost Instead of Public IP - Root cause: process.env not available in browser - Solution: Use hooks.server.ts transformPageChunk to inject window.__VAR__ - Pattern: Separate _CLIENT env vars for browser access Problem 6: CORS Blocking Cross-Origin Requests - Root cause: Different ports = different origins - Solution: Add CORS_ORIGINS env var to mana-core-auth - Verification: Test with curl OPTIONS preflight Also added: - Updated checklist with CORS and client URL verification - 4 new lessons learned (#7-#10) - Environment variable flow diagram 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
00281a86d3
commit
113495add3
1 changed files with 158 additions and 0 deletions
|
|
@ -13,6 +13,8 @@ Common issues and solutions for the manacore-monorepo.
|
|||
- [chat-backend Container Unhealthy](#problem-2-chat-backend-container-unhealthy)
|
||||
- [SvelteKit Static Environment Variable Imports](#problem-3-sveltekit-static-environment-variable-imports)
|
||||
- [Orphan Docker Containers](#problem-4-orphan-docker-containers)
|
||||
- [Client-Side Calling localhost Instead of Public IP](#problem-5-client-side-calling-localhost-instead-of-public-ip)
|
||||
- [CORS Blocking Cross-Origin Requests](#problem-6-cors-blocking-cross-origin-requests)
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -599,6 +601,149 @@ docker rm -f manadeck-backend-staging manacore-nginx-staging
|
|||
|
||||
---
|
||||
|
||||
### Problem 5: Client-Side Calling localhost Instead of Public IP
|
||||
|
||||
**Symptoms:**
|
||||
|
||||
- Browser console shows: `POST http://localhost:3001/api/v1/auth/register net::ERR_CONNECTION_REFUSED`
|
||||
- API calls work from server but fail from browser
|
||||
- The injected `window.__PUBLIC_MANA_CORE_AUTH_URL__` is empty or undefined
|
||||
|
||||
**Root Cause:**
|
||||
|
||||
SvelteKit's environment variables work differently on server vs client:
|
||||
|
||||
- **Server-side (SSR):** Has access to `process.env`
|
||||
- **Client-side (browser):** Does NOT have access to `process.env` - needs explicit injection
|
||||
|
||||
The initial fix using `process.env` only worked for SSR. Browser code falls back to `localhost`.
|
||||
|
||||
**Solution - Runtime Environment Injection:**
|
||||
|
||||
1. **Add client URLs to docker-compose.staging.yml:**
|
||||
|
||||
```yaml
|
||||
chat-web:
|
||||
environment:
|
||||
# Server-side URLs (Docker internal network)
|
||||
PUBLIC_BACKEND_URL: http://chat-backend:3002
|
||||
PUBLIC_MANA_CORE_AUTH_URL: http://mana-core-auth:3001
|
||||
# Client-side URLs (browser access via public IP)
|
||||
PUBLIC_BACKEND_URL_CLIENT: http://46.224.108.214:3002
|
||||
PUBLIC_MANA_CORE_AUTH_URL_CLIENT: http://46.224.108.214:3001
|
||||
```
|
||||
|
||||
2. **Inject into HTML via hooks.server.ts:**
|
||||
|
||||
```typescript
|
||||
// apps/chat/apps/web/src/hooks.server.ts
|
||||
import type { Handle } from '@sveltejs/kit';
|
||||
|
||||
const PUBLIC_MANA_CORE_AUTH_URL_CLIENT =
|
||||
process.env.PUBLIC_MANA_CORE_AUTH_URL_CLIENT || process.env.PUBLIC_MANA_CORE_AUTH_URL || '';
|
||||
|
||||
export const handle: Handle = async ({ event, resolve }) => {
|
||||
return resolve(event, {
|
||||
transformPageChunk: ({ html }) => {
|
||||
const envScript = `<script>
|
||||
window.__PUBLIC_MANA_CORE_AUTH_URL__ = "${PUBLIC_MANA_CORE_AUTH_URL_CLIENT}";
|
||||
</script>`;
|
||||
return html.replace('<head>', `<head>${envScript}`);
|
||||
},
|
||||
});
|
||||
};
|
||||
```
|
||||
|
||||
3. **Read from window in client code:**
|
||||
|
||||
```typescript
|
||||
// apps/chat/apps/web/src/lib/stores/auth.svelte.ts
|
||||
function getAuthUrl(): string {
|
||||
if (browser && typeof window !== 'undefined') {
|
||||
const injectedUrl = (window as unknown as { __PUBLIC_MANA_CORE_AUTH_URL__?: string })
|
||||
.__PUBLIC_MANA_CORE_AUTH_URL__;
|
||||
return injectedUrl || 'http://localhost:3001';
|
||||
}
|
||||
return process.env.PUBLIC_MANA_CORE_AUTH_URL || 'http://localhost:3001';
|
||||
}
|
||||
```
|
||||
|
||||
**How to Verify:**
|
||||
|
||||
Open browser DevTools (F12) → Console:
|
||||
|
||||
```javascript
|
||||
window.__PUBLIC_MANA_CORE_AUTH_URL__;
|
||||
// Should show: "http://46.224.108.214:3001"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Problem 6: CORS Blocking Cross-Origin Requests
|
||||
|
||||
**Symptoms:**
|
||||
|
||||
- Browser console shows: `Access to fetch at 'http://46.224.108.214:3001/...' from origin 'http://46.224.108.214:3000' has been blocked by CORS policy`
|
||||
- API calls work via curl but fail from browser
|
||||
- Preflight OPTIONS requests fail
|
||||
|
||||
**Root Cause:**
|
||||
|
||||
Browser security blocks requests between different origins (port counts as different origin):
|
||||
|
||||
- chat-web: `http://46.224.108.214:3000`
|
||||
- mana-core-auth: `http://46.224.108.214:3001`
|
||||
|
||||
Even though they're on the same IP, different ports = different origins = CORS blocked.
|
||||
|
||||
**Solution:**
|
||||
|
||||
Add `CORS_ORIGINS` environment variable to mana-core-auth in docker-compose.staging.yml:
|
||||
|
||||
```yaml
|
||||
mana-core-auth:
|
||||
environment:
|
||||
# ... other env vars ...
|
||||
# CORS - Allow chat-web and other staging origins
|
||||
CORS_ORIGINS: http://46.224.108.214:3000,http://46.224.108.214:3002,http://localhost:3000
|
||||
```
|
||||
|
||||
**CORS Configuration in mana-core-auth:**
|
||||
|
||||
The service reads `CORS_ORIGINS` from environment:
|
||||
|
||||
```typescript
|
||||
// services/mana-core-auth/src/config/configuration.ts
|
||||
cors: {
|
||||
origin: process.env.CORS_ORIGINS?.split(',') || [
|
||||
'http://localhost:3000',
|
||||
'http://localhost:8081',
|
||||
],
|
||||
}
|
||||
|
||||
// services/mana-core-auth/src/main.ts
|
||||
const corsOrigins = configService.get<string[]>('cors.origin') || [];
|
||||
app.enableCors({
|
||||
origin: corsOrigins,
|
||||
credentials: true,
|
||||
});
|
||||
```
|
||||
|
||||
**How to Verify:**
|
||||
|
||||
```bash
|
||||
# Test CORS preflight
|
||||
curl -X OPTIONS http://46.224.108.214:3001/api/v1/auth/register \
|
||||
-H "Origin: http://46.224.108.214:3000" \
|
||||
-H "Access-Control-Request-Method: POST" \
|
||||
-v
|
||||
|
||||
# Should see in response headers:
|
||||
# Access-Control-Allow-Origin: http://46.224.108.214:3000
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Complete Staging Deployment Checklist
|
||||
|
||||
#### Before Deployment
|
||||
|
|
@ -606,6 +751,8 @@ docker rm -f manadeck-backend-staging manacore-nginx-staging
|
|||
- [ ] Verify `docker-compose.staging.yml` has correct health check paths
|
||||
- [ ] Verify CI/CD workflow (`cd-staging.yml`) has matching health check paths
|
||||
- [ ] Check that required databases exist or CI creates them
|
||||
- [ ] Verify `CORS_ORIGINS` includes all frontend origins
|
||||
- [ ] Verify `PUBLIC_*_CLIENT` env vars have correct public IPs for browser access
|
||||
|
||||
#### During Deployment Failure
|
||||
|
||||
|
|
@ -698,6 +845,17 @@ docker compose exec -T chat-backend wget -q -O - http://localhost:3002/api/v1/he
|
|||
|
||||
6. **Verify Before Commit:** Always check both `docker-compose.staging.yml` AND CI/CD workflows for matching paths.
|
||||
|
||||
7. **Server vs Client URLs:** Docker internal URLs (e.g., `http://mana-core-auth:3001`) only work server-side. Browsers need public IPs. Use separate `_CLIENT` env vars for browser access.
|
||||
|
||||
8. **SvelteKit Runtime Injection:** Use `hooks.server.ts` with `transformPageChunk` to inject environment variables into HTML at runtime. This is the only reliable way to pass server env vars to client code.
|
||||
|
||||
9. **CORS for Multi-Service Apps:** When frontend and backend are on different ports, configure CORS on the backend. Port differences count as different origins (e.g., `:3000` vs `:3001`).
|
||||
|
||||
10. **Environment Variable Flow:**
|
||||
```
|
||||
docker-compose.yml → Container env → process.env (SSR) → hooks.server.ts → window.__VAR__ (browser)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## References
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue