109 lines
3.4 KiB
Markdown
109 lines
3.4 KiB
Markdown
# Plan: Fix CORS Configuration
|
|
|
|
**Scope**: Trivial — 3 files, ~20 lines
|
|
**Goal**: Ensure server-core CORS properly accepts requests from frontend origins, configurable via `CORS_ALLOWED_ORIGINS` env var, documented in `.env.example`.
|
|
|
|
---
|
|
|
|
## Context
|
|
|
|
Current CORS config in `main.go:114-130`:
|
|
- Uses `CORS_ALLOWED_ORIGINS` env var (defaults to `*`)
|
|
- Has BOTH `AllowOrigins` AND `AllowOriginFunc` — redundant
|
|
- `strings.Contains(corsOrigins, origin)` is weak (substring match: `"example.com"` matches `"notexample.com"`)
|
|
- `AllowCredentials: true` is needed for JWT `Authorization` header
|
|
- `CORS_ALLOWED_ORIGINS` is NOT documented in any `.env.example` file
|
|
- `SHARE_LINK_TTL` (from Phase 5.0) also missing from `.env.example`
|
|
|
|
---
|
|
|
|
## Task 1: Fix CORS config in `main.go`
|
|
- [x] **File**: `apps/server-core/main.go` lines 114-130
|
|
|
|
**Replace** the current CORS block with:
|
|
|
|
```go
|
|
// CORS Configuration
|
|
corsOrigins := os.Getenv("CORS_ALLOWED_ORIGINS")
|
|
if corsOrigins == "" {
|
|
corsOrigins = "*"
|
|
}
|
|
|
|
corsConfig := cors.Config{
|
|
AllowMethods: []string{"GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"},
|
|
AllowHeaders: []string{"Origin", "Content-Length", "Content-Type", "Authorization", "X-Admin-Key", "X-Device-Token"},
|
|
ExposeHeaders: []string{"Content-Length"},
|
|
AllowCredentials: true,
|
|
}
|
|
|
|
if corsOrigins == "*" {
|
|
// Wildcard: allow all origins but use AllowOriginFunc so the response
|
|
// header echoes the actual origin (required when AllowCredentials=true).
|
|
corsConfig.AllowOriginFunc = func(origin string) bool {
|
|
return true
|
|
}
|
|
} else {
|
|
// Explicit list: split by comma, trim spaces, exact match only.
|
|
origins := strings.Split(corsOrigins, ",")
|
|
for i := range origins {
|
|
origins[i] = strings.TrimSpace(origins[i])
|
|
}
|
|
corsConfig.AllowOrigins = origins
|
|
}
|
|
|
|
r.Use(cors.New(corsConfig))
|
|
```
|
|
|
|
**Key changes**:
|
|
1. Remove simultaneous `AllowOrigins` + `AllowOriginFunc` (pick one based on `*` vs explicit)
|
|
2. When `*`: use `AllowOriginFunc` returning `true` → gin-contrib/cors echoes the requesting origin (compatible with `AllowCredentials: true`)
|
|
3. When explicit: use `AllowOrigins` with trimmed values, exact match (no more `strings.Contains` substring bug)
|
|
|
|
**Verify**: `go build ./...` passes
|
|
|
|
---
|
|
|
|
## Task 2: Add `CORS_ALLOWED_ORIGINS` to root `.env.example`
|
|
- [x] **File**: `.env.example`
|
|
|
|
**Add** after `VITE_API_BASE_URL` line (section 5):
|
|
|
|
```env
|
|
# 7. CORS Configuration
|
|
# Comma-separated list of allowed origins for the API.
|
|
# Use '*' to allow all origins (NOT recommended for production).
|
|
# Example: https://dash.yourdomain.com,https://admin.yourdomain.com
|
|
CORS_ALLOWED_ORIGINS=http://localhost:5173
|
|
|
|
# 8. Share Link Configuration
|
|
# TTL for peer config share links (Go duration format)
|
|
SHARE_LINK_TTL=24h
|
|
```
|
|
|
|
---
|
|
|
|
## Task 3: Add `CORS_ALLOWED_ORIGINS` to server-core `.env.example`
|
|
- [x] **File**: `apps/server-core/.env.example`
|
|
|
|
**Add** at the end:
|
|
|
|
```env
|
|
# CORS
|
|
# Comma-separated allowed origins. '*' = allow all (not recommended for production)
|
|
CORS_ALLOWED_ORIGINS=http://localhost:5173
|
|
|
|
# Share Link TTL (Go duration format)
|
|
SHARE_LINK_TTL=24h
|
|
```
|
|
|
|
**Verify**: all `.env.example` files contain `CORS_ALLOWED_ORIGINS`
|
|
|
|
---
|
|
|
|
## Final Verification Wave
|
|
|
|
- [x] `go build ./...` passes in `apps/server-core/`
|
|
- [x] `CORS_ALLOWED_ORIGINS` present in root `.env.example`
|
|
- [x] `CORS_ALLOWED_ORIGINS` present in `apps/server-core/.env.example`
|
|
- [x] No other files reference `CORS_ALLOWED_ORIGINS` that need updating
|