mirror of
https://github.com/nolen777/eagle0.git
synced 2026-07-28 20:55:44 +00:00
Implements a user-facing account management portal at accounts.eagle0.net that allows non-admin users to view their account information and delete their account. The same admin server backend handles both admin.eagle0.net (admin-only) and accounts.eagle0.net (user self-service), distinguished by Host header. Changes: - nginx: Add server blocks for accounts.eagle0.net - admin_server: Add host detection (isAccountsHost/isAdminHost) - admin_server: Add requireUser auth wrapper (auth without admin check) - admin_server: Add /my-account, /my-account/delete, /goodbye routes - admin_server: Modify login flow to not require admin for accounts portal - templates: Add my_account_layout.html, my_account.html, goodbye.html - templates: Update login.html to show different text per portal Post-deploy manual steps required: 1. DNS: Add A record for accounts.eagle0.net -> droplet IP 2. SSL: Run certbot on droplet for accounts.eagle0.net certificate Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
313 lines
9.9 KiB
Nginx Configuration File
313 lines
9.9 KiB
Nginx Configuration File
events {
|
|
worker_connections 1024;
|
|
}
|
|
|
|
http {
|
|
# Allow large request bodies for game uploads (default is 1MB)
|
|
client_max_body_size 50M;
|
|
|
|
# Logging
|
|
log_format grpc_json escape=json '{'
|
|
'"time":"$time_iso8601",'
|
|
'"client":"$remote_addr",'
|
|
'"uri":"$uri",'
|
|
'"status":$status,'
|
|
'"grpc_status":"$sent_http_grpc_status",'
|
|
'"request_time":$request_time,'
|
|
'"upstream_time":"$upstream_response_time"'
|
|
'}';
|
|
|
|
access_log /var/log/nginx/access.log grpc_json;
|
|
error_log /var/log/nginx/error.log warn;
|
|
|
|
# Rate limiting zone
|
|
limit_req_zone $binary_remote_addr zone=grpc_limit:10m rate=100r/s;
|
|
|
|
# Docker DNS resolver - re-resolve hostnames every 10s
|
|
# This prevents stale IP caching when containers restart
|
|
resolver 127.0.0.11 valid=10s ipv6=off;
|
|
|
|
# Eagle backend - blue-green deployment with variable-based routing
|
|
# Uses a variable so nginx only resolves the configured backend (not all backends).
|
|
# This allows nginx to start/reload even when the inactive backend is stopped.
|
|
# The deploy script updates this map, then recreates nginx.
|
|
map $host $eagle_backend {
|
|
default "eagle-blue:40032";
|
|
}
|
|
|
|
# HTTP server for Let's Encrypt challenge and redirect
|
|
server {
|
|
listen 80;
|
|
listen [::]:80;
|
|
server_name prod.eagle0.net eagle0.net;
|
|
|
|
# Let's Encrypt challenge
|
|
location /.well-known/acme-challenge/ {
|
|
root /var/www/certbot;
|
|
}
|
|
|
|
# Redirect all other HTTP to HTTPS
|
|
location / {
|
|
return 301 https://$host$request_uri;
|
|
}
|
|
}
|
|
|
|
# HTTPS server for gRPC
|
|
server {
|
|
listen 443 ssl;
|
|
listen [::]:443 ssl;
|
|
http2 on;
|
|
server_name prod.eagle0.net eagle0.net;
|
|
|
|
# SSL certificates (managed by certbot)
|
|
ssl_certificate /etc/letsencrypt/live/prod.eagle0.net/fullchain.pem;
|
|
ssl_certificate_key /etc/letsencrypt/live/prod.eagle0.net/privkey.pem;
|
|
|
|
# SSL configuration
|
|
ssl_protocols TLSv1.2 TLSv1.3;
|
|
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;
|
|
ssl_prefer_server_ciphers off;
|
|
ssl_session_timeout 1d;
|
|
ssl_session_cache shared:SSL:10m;
|
|
ssl_session_tickets off;
|
|
|
|
# gRPC proxy for Eagle service
|
|
location /net.eagle0.eagle.api.Eagle {
|
|
# Rate limiting
|
|
limit_req zone=grpc_limit burst=50 nodelay;
|
|
|
|
# gRPC proxy - uses variable for blue-green deployment
|
|
grpc_pass grpc://$eagle_backend;
|
|
|
|
# Timeouts for long-running streams
|
|
grpc_read_timeout 1200s;
|
|
grpc_send_timeout 1200s;
|
|
grpc_socket_keepalive on;
|
|
|
|
# Error handling
|
|
error_page 502 = /error502grpc;
|
|
}
|
|
|
|
# gRPC proxy for Auth service (routes to Go auth service, not Eagle)
|
|
location /net.eagle0.eagle.api.auth.Auth {
|
|
# Rate limiting
|
|
limit_req zone=grpc_limit burst=50 nodelay;
|
|
|
|
# Route to auth service directly (not through Eagle)
|
|
set $auth_backend "auth:40033";
|
|
grpc_pass grpc://$auth_backend;
|
|
|
|
# Timeouts
|
|
grpc_read_timeout 30s;
|
|
grpc_send_timeout 30s;
|
|
|
|
# Error handling
|
|
error_page 502 = /error502grpc;
|
|
}
|
|
|
|
# OAuth callback endpoint (proxied to Go auth service)
|
|
location /oauth/callback {
|
|
proxy_pass http://auth:8080;
|
|
proxy_set_header Host $host;
|
|
proxy_set_header X-Real-IP $remote_addr;
|
|
}
|
|
|
|
# Apple OAuth callback (Apple uses POST with form_post response mode)
|
|
location /oauth/apple/callback {
|
|
proxy_pass http://auth:8080;
|
|
proxy_set_header Host $host;
|
|
proxy_set_header X-Real-IP $remote_addr;
|
|
}
|
|
|
|
# Steam OAuth callback (Steam uses OpenID 2.0)
|
|
location /oauth/steam/callback {
|
|
proxy_pass http://auth:8080;
|
|
proxy_set_header Host $host;
|
|
proxy_set_header X-Real-IP $remote_addr;
|
|
}
|
|
|
|
# Invitation landing page (proxied to Go auth service)
|
|
location /invite/ {
|
|
proxy_pass http://auth:8080;
|
|
proxy_set_header Host $host;
|
|
proxy_set_header X-Real-IP $remote_addr;
|
|
}
|
|
|
|
# Credits/attributions page (proxied to Go auth service)
|
|
location /credits {
|
|
proxy_pass http://auth:8080;
|
|
proxy_set_header Host $host;
|
|
proxy_set_header X-Real-IP $remote_addr;
|
|
}
|
|
|
|
# Health check endpoint
|
|
location /health {
|
|
access_log off;
|
|
return 200 "OK\n";
|
|
add_header Content-Type text/plain;
|
|
}
|
|
|
|
# gRPC error handling
|
|
location = /error502grpc {
|
|
internal;
|
|
default_type application/grpc;
|
|
add_header grpc-status 14;
|
|
add_header grpc-message "unavailable";
|
|
return 204;
|
|
}
|
|
}
|
|
|
|
# HTTPS server for Go Auth service (port 40033)
|
|
# Clients connect here directly for OAuth RPCs in Phase 2
|
|
server {
|
|
listen 40033 ssl;
|
|
listen [::]:40033 ssl;
|
|
http2 on;
|
|
server_name prod.eagle0.net;
|
|
|
|
# SSL certificates (same as main server)
|
|
ssl_certificate /etc/letsencrypt/live/prod.eagle0.net/fullchain.pem;
|
|
ssl_certificate_key /etc/letsencrypt/live/prod.eagle0.net/privkey.pem;
|
|
|
|
# SSL configuration
|
|
ssl_protocols TLSv1.2 TLSv1.3;
|
|
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;
|
|
ssl_prefer_server_ciphers off;
|
|
ssl_session_timeout 1d;
|
|
ssl_session_cache shared:SSL:10m;
|
|
ssl_session_tickets off;
|
|
|
|
# gRPC proxy for Auth service
|
|
# Uses variable-based resolution so nginx can start even if auth isn't ready yet
|
|
# DNS is cached by the resolver directive (valid=10s)
|
|
location /net.eagle0.eagle.api.auth.Auth {
|
|
# Rate limiting
|
|
limit_req zone=grpc_limit burst=50 nodelay;
|
|
|
|
# Dynamic upstream resolution (doesn't block nginx startup)
|
|
set $auth_backend "auth:40033";
|
|
grpc_pass grpc://$auth_backend;
|
|
|
|
# Timeouts
|
|
grpc_read_timeout 30s;
|
|
grpc_send_timeout 30s;
|
|
|
|
# Error handling
|
|
error_page 502 = /error502grpc;
|
|
}
|
|
|
|
# gRPC proxy for Admin service
|
|
location /net.eagle0.eagle.api.admin.Admin {
|
|
# Rate limiting
|
|
limit_req zone=grpc_limit burst=50 nodelay;
|
|
|
|
# Dynamic upstream resolution (doesn't block nginx startup)
|
|
set $auth_backend "auth:40033";
|
|
grpc_pass grpc://$auth_backend;
|
|
|
|
# Timeouts
|
|
grpc_read_timeout 30s;
|
|
grpc_send_timeout 30s;
|
|
|
|
# Error handling
|
|
error_page 502 = /error502grpc;
|
|
}
|
|
|
|
# Health check endpoint
|
|
location /health {
|
|
access_log off;
|
|
return 200 "OK\n";
|
|
add_header Content-Type text/plain;
|
|
}
|
|
|
|
# gRPC error handling
|
|
location = /error502grpc {
|
|
internal;
|
|
default_type application/grpc;
|
|
add_header grpc-status 14;
|
|
add_header grpc-message "unavailable";
|
|
return 204;
|
|
}
|
|
}
|
|
|
|
# HTTP server for Admin Console (Let's Encrypt + redirect)
|
|
server {
|
|
listen 80;
|
|
listen [::]:80;
|
|
server_name admin.prod.eagle0.net admin.eagle0.net;
|
|
|
|
location /.well-known/acme-challenge/ {
|
|
root /var/www/certbot;
|
|
}
|
|
|
|
location / {
|
|
return 301 https://$host$request_uri;
|
|
}
|
|
}
|
|
|
|
# HTTPS server for Admin Console
|
|
server {
|
|
listen 443 ssl;
|
|
listen [::]:443 ssl;
|
|
server_name admin.prod.eagle0.net admin.eagle0.net;
|
|
|
|
ssl_certificate /etc/letsencrypt/live/admin.eagle0.net/fullchain.pem;
|
|
ssl_certificate_key /etc/letsencrypt/live/admin.eagle0.net/privkey.pem;
|
|
|
|
ssl_protocols TLSv1.2 TLSv1.3;
|
|
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;
|
|
ssl_prefer_server_ciphers off;
|
|
ssl_session_timeout 1d;
|
|
ssl_session_cache shared:SSL:10m;
|
|
ssl_session_tickets off;
|
|
|
|
location / {
|
|
proxy_pass http://admin:8080;
|
|
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;
|
|
}
|
|
}
|
|
|
|
# HTTP server for Accounts Console (Let's Encrypt + redirect)
|
|
server {
|
|
listen 80;
|
|
listen [::]:80;
|
|
server_name accounts.prod.eagle0.net accounts.eagle0.net;
|
|
|
|
location /.well-known/acme-challenge/ {
|
|
root /var/www/certbot;
|
|
}
|
|
|
|
location / {
|
|
return 301 https://$host$request_uri;
|
|
}
|
|
}
|
|
|
|
# HTTPS server for Accounts Console (user self-service portal)
|
|
server {
|
|
listen 443 ssl;
|
|
listen [::]:443 ssl;
|
|
server_name accounts.prod.eagle0.net accounts.eagle0.net;
|
|
|
|
ssl_certificate /etc/letsencrypt/live/accounts.eagle0.net/fullchain.pem;
|
|
ssl_certificate_key /etc/letsencrypt/live/accounts.eagle0.net/privkey.pem;
|
|
|
|
ssl_protocols TLSv1.2 TLSv1.3;
|
|
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;
|
|
ssl_prefer_server_ciphers off;
|
|
ssl_session_timeout 1d;
|
|
ssl_session_cache shared:SSL:10m;
|
|
ssl_session_tickets off;
|
|
|
|
location / {
|
|
proxy_pass http://admin:8080;
|
|
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;
|
|
}
|
|
}
|
|
}
|