diff --git a/backend/main.py b/backend/main.py
index 252291c..cb8b7e0 100644
--- a/backend/main.py
+++ b/backend/main.py
@@ -112,11 +112,13 @@ async def lifespan(app: FastAPI):
app = FastAPI(title="Yolkbook API", lifespan=lifespan)
+_cors_origins = [o.strip() for o in os.environ.get("ALLOWED_ORIGINS", "").split(",") if o.strip()]
app.add_middleware(
CORSMiddleware,
- allow_origins=["*"],
- allow_methods=["*"],
- allow_headers=["*"],
+ allow_origins=_cors_origins,
+ allow_credentials=True,
+ allow_methods=["GET", "POST", "PUT", "DELETE"],
+ allow_headers=["Authorization", "Content-Type"],
)
app.include_router(auth_router.router)
diff --git a/nginx/html/index.html b/nginx/html/index.html
index ef720d2..66005dd 100644
--- a/nginx/html/index.html
+++ b/nginx/html/index.html
@@ -70,7 +70,7 @@
-
+
diff --git a/nginx/html/js/admin.js b/nginx/html/js/admin.js
index 6475f9f..8e2bbb5 100644
--- a/nginx/html/js/admin.js
+++ b/nginx/html/js/admin.js
@@ -47,20 +47,20 @@ function renderUsers(users) {
: ``;
const impersonateBtn = !isSelf
- ? ``
+ ? ``
: '';
const deleteBtn = !isSelf
- ? ``
+ ? ``
: '';
return `
- | ${u.username} |
+ ${escHtml(u.username)} |
${roleLabel} |
${statusLabel} |
${created} |
-
+
${toggleBtn}
${impersonateBtn}
${deleteBtn}
@@ -115,7 +115,7 @@ async function toggleUser(id, disable) {
}
}
-async function impersonateUser(id, username) {
+async function impersonateUser(id) {
try {
const data = await API.post(`/api/admin/users/${id}/impersonate`, {});
// Save admin token so user can return
diff --git a/nginx/nginx.conf b/nginx/nginx.conf
index 4351a1a..4fcb467 100644
--- a/nginx/nginx.conf
+++ b/nginx/nginx.conf
@@ -13,8 +13,11 @@ http {
gzip_types text/plain text/css application/javascript application/json;
gzip_min_length 1000;
- # ── Rate limiting — login endpoint ────────────────────────────────────────
- limit_req_zone $binary_remote_addr zone=login:10m rate=5r/m;
+ # ── Rate limiting ─────────────────────────────────────────────────────────
+ limit_req_zone $binary_remote_addr zone=login:10m rate=5r/m;
+ limit_req_zone $binary_remote_addr zone=register:10m rate=3r/m;
+ limit_req_zone $binary_remote_addr zone=admin:10m rate=10r/m;
+ limit_req_status 429;
server {
listen 80;
@@ -24,10 +27,11 @@ http {
index index.html;
# ── Security headers ──────────────────────────────────────────────────
- add_header X-Content-Type-Options "nosniff" always;
- add_header X-Frame-Options "SAMEORIGIN" always;
- add_header X-XSS-Protection "1; mode=block" always;
+ add_header X-Content-Type-Options "nosniff" always;
+ add_header X-Frame-Options "SAMEORIGIN" always;
+ add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
+ add_header Content-Security-Policy "default-src 'self'; script-src 'self' https://cdn.jsdelivr.net; style-src 'self' 'unsafe-inline'; img-src 'self' data:; connect-src 'self'; font-src 'self'; frame-ancestors 'none'" always;
# ── Static files ──────────────────────────────────────────────────────
location / {
@@ -45,7 +49,7 @@ http {
access_log off;
}
- # ── Login rate limiting ───────────────────────────────────────────────
+ # ── Auth / admin rate limiting ────────────────────────────────────────
location = /api/auth/login {
limit_req zone=login burst=3 nodelay;
@@ -64,6 +68,42 @@ http {
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
}
+ location = /api/auth/register {
+ limit_req zone=register burst=2 nodelay;
+
+ proxy_pass http://api:8000;
+ proxy_http_version 1.1;
+
+ proxy_set_header Host $host;
+ proxy_set_header X-Real-IP $remote_addr;
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+ proxy_set_header X-Forwarded-Proto $scheme;
+
+ add_header Cache-Control "no-store" always;
+ add_header X-Content-Type-Options "nosniff" always;
+ add_header X-Frame-Options "SAMEORIGIN" always;
+ add_header X-XSS-Protection "1; mode=block" always;
+ add_header Referrer-Policy "strict-origin-when-cross-origin" always;
+ }
+
+ location /api/admin/ {
+ limit_req zone=admin burst=5 nodelay;
+
+ proxy_pass http://api:8000;
+ proxy_http_version 1.1;
+
+ proxy_set_header Host $host;
+ proxy_set_header X-Real-IP $remote_addr;
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+ proxy_set_header X-Forwarded-Proto $scheme;
+
+ add_header Cache-Control "no-store" always;
+ add_header X-Content-Type-Options "nosniff" always;
+ add_header X-Frame-Options "SAMEORIGIN" always;
+ add_header X-XSS-Protection "1; mode=block" always;
+ add_header Referrer-Policy "strict-origin-when-cross-origin" always;
+ }
+
# ── API reverse proxy ─────────────────────────────────────────────────
# All /api/* requests are forwarded to the FastAPI container.
# The container is reachable by its service name on the Docker network.
|