forked from DreamLab-AI/origin-logseq-AR
-
Notifications
You must be signed in to change notification settings - Fork 18
Expand file tree
/
Copy pathnginx.production.conf
More file actions
307 lines (258 loc) · 12 KB
/
nginx.production.conf
File metadata and controls
307 lines (258 loc) · 12 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
# VisionFlow Production Nginx Configuration
# Optimised for Cloudflare Tunnel with HTTPS termination
pid /var/run/nginx.pid;
error_log /var/log/nginx/error.log crit;
events {
worker_connections 2048;
multi_accept on;
use epoll;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
charset utf-8;
# Custom MIME types
types {
application/typescript ts;
application/javascript js mjs;
text/jsx jsx;
text/tsx tsx;
}
# Logging
log_format production '$remote_addr - $remote_user [$time_local] '
'"$request" $status $body_bytes_sent '
'"$http_referer" "$http_user_agent" '
'rt=$request_time uct="$upstream_connect_time" '
'CF-Ray=$http_cf_ray CF-Connecting-IP=$http_cf_connecting_ip';
access_log off;
# Performance optimisations
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 120;
keepalive_requests 100;
client_max_body_size 100M;
# Gzip compression
gzip on;
gzip_disable "msie6";
gzip_vary on;
gzip_proxied any;
gzip_comp_level 6;
gzip_types text/plain text/css application/json application/javascript
text/xml application/xml application/xml+rss text/javascript
application/typescript text/jsx text/tsx;
# WebSocket upgrade mapping
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
# CORS origin allowlist (prevents arbitrary origin reflection)
map $http_origin $cors_origin {
default "";
"~^https?://localhost(:\\d+)?$" $http_origin;
"~^https?://127\\.0\\.0\\.1(:\\d+)?$" $http_origin;
"https://www.visionflow.info" $http_origin;
"https://visionflow.info" $http_origin;
}
# Rust backend upstream
upstream rust_backend {
server 127.0.0.1:4001 max_fails=0;
keepalive 64;
keepalive_timeout 120s;
}
# JavaScript Solid Server (JSS) upstream
# Not used by nginx directly — Rust backend proxies to JSS internally
# Kept for reference; uncomment if direct nginx→JSS routes are needed
# upstream jss {
# server jss:3030;
# keepalive 16;
# }
# Main server - Production
server {
listen 3001 default_server;
server_name _;
root /app/client/dist;
index index.html;
# Security headers for production
add_header Cross-Origin-Opener-Policy "same-origin" always;
add_header Cross-Origin-Embedder-Policy "require-corp" 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;
# CSP for production - tighter than dev
# TODO: Replace 'unsafe-inline' in script-src with nonce-based CSP once IdP views are refactored
add_header Content-Security-Policy "default-src 'self' https:; script-src 'self' 'unsafe-inline' https://esm.sh https://javascriptsolidserver.github.io https://cdn.jsdelivr.net https://unpkg.com https://getalby.com https://goal.ruv.io; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; connect-src 'self' wss: https: https://esm.sh; frame-src 'self' https://getalby.com; font-src 'self' data:; base-uri 'self'; form-action 'self'; frame-ancestors 'self';" always;
# HSTS handled by Cloudflare, but include as backup
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
# API endpoints
location /api/ {
proxy_pass http://rust_backend;
proxy_http_version 1.1;
# Cloudflare headers
proxy_set_header CF-Connecting-IP $http_cf_connecting_ip;
proxy_set_header CF-Ray $http_cf_ray;
proxy_set_header CF-Visitor $http_cf_visitor;
proxy_set_header CF-IPCountry $http_cf_ipcountry;
# Standard headers
proxy_set_header Host $host;
proxy_set_header X-Real-IP $http_cf_connecting_ip;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https; # Always HTTPS from Cloudflare
# Timeouts
proxy_connect_timeout 60s;
proxy_send_timeout 300s;
proxy_read_timeout 300s;
# Performance
proxy_buffering on;
proxy_buffer_size 4k;
proxy_buffers 8 4k;
proxy_busy_buffers_size 8k;
}
# =====================================================================
# Solid Protocol endpoints → Rust backend (handles pod mgmt + proxies LDP to JSS)
# The Rust handler at /solid/* manages pod creation, init, check
# and proxies all other LDP requests to JSS internally.
# =====================================================================
# Solid WebSocket notifications (upgrade to Rust handler)
location ^~ /solid/.notifications {
proxy_pass http://rust_backend/api/solid/.notifications;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $http_cf_connecting_ip;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header Authorization $http_authorization;
proxy_read_timeout 3600s;
proxy_send_timeout 3600s;
proxy_connect_timeout 10s;
proxy_buffering off;
proxy_cache off;
}
# All /solid/* routes → Rust backend at /api/solid/* (pod management + LDP proxy to JSS)
location ^~ /solid/ {
proxy_pass http://rust_backend/api/solid/;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $http_cf_connecting_ip;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header Authorization $http_authorization;
# Pass original URI so NIP-98 auth verifies against the public-facing path (/solid/...)
# rather than the nginx-rewritten internal path (/api/solid/...)
proxy_set_header X-Forwarded-URI $request_uri;
# LDP/Solid specific headers
proxy_pass_header Accept;
proxy_pass_header Content-Type;
proxy_pass_header Link;
proxy_pass_header Slug;
proxy_pass_header If-Match;
proxy_pass_header If-None-Match;
proxy_read_timeout 60s;
proxy_send_timeout 60s;
proxy_connect_timeout 10s;
client_max_body_size 50m;
# CORS headers for Solid apps
add_header Access-Control-Allow-Origin $cors_origin always;
add_header Access-Control-Allow-Methods "GET, POST, PUT, PATCH, DELETE, OPTIONS" always;
add_header Access-Control-Allow-Headers "Authorization, Content-Type, Accept, Origin, Link, Slug, If-Match, If-None-Match" always;
add_header Access-Control-Allow-Credentials "true" always;
add_header Access-Control-Expose-Headers "Location, Link, WAC-Allow, Accept-Patch, Accept-Post" always;
if ($request_method = 'OPTIONS') {
add_header Access-Control-Allow-Origin $cors_origin always;
add_header Access-Control-Allow-Methods "GET, POST, PUT, PATCH, DELETE, OPTIONS" always;
add_header Access-Control-Allow-Headers "Authorization, Content-Type, Accept, Origin, Link, Slug, If-Match, If-None-Match" always;
add_header Access-Control-Max-Age 86400;
add_header Content-Length 0;
return 204;
}
}
# User pods shortcut → Rust backend at /api/solid/pods/
location /pods/ {
proxy_pass http://rust_backend/api/solid/pods/;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $http_cf_connecting_ip;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header Authorization $http_authorization;
proxy_set_header X-Forwarded-URI $request_uri;
proxy_pass_header Accept;
proxy_pass_header Content-Type;
proxy_pass_header Link;
proxy_pass_header Slug;
client_max_body_size 50m;
}
# WebSocket endpoints
location ~ ^/(wss|ws/speech|ws/mcp-relay|ws/hybrid-status)$ {
proxy_pass http://rust_backend;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
# Cloudflare headers
proxy_set_header CF-Connecting-IP $http_cf_connecting_ip;
proxy_set_header CF-Ray $http_cf_ray;
proxy_set_header CF-Visitor $http_cf_visitor;
# Standard headers
proxy_set_header Host $host;
proxy_set_header X-Real-IP $http_cf_connecting_ip;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;
# Pass the real Origin through — the Rust backend validates it
# against CORS_ALLOWED_ORIGINS (must include the production domain,
# e.g. CORS_ALLOWED_ORIGINS=https://www.visionflow.info,http://localhost:3000).
# Do NOT rewrite Origin here; spoofing it bypasses CORS entirely.
proxy_set_header Origin $http_origin;
# WebSocket timeouts (Cloudflare has 100s timeout)
proxy_read_timeout 3600s;
proxy_send_timeout 3600s;
proxy_connect_timeout 75s;
# Disable buffering for real-time
proxy_buffering off;
proxy_cache off;
tcp_nodelay on;
}
# Static assets with caching
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
expires 30d;
add_header Cache-Control "public, immutable";
# Security headers still apply
add_header Cross-Origin-Opener-Policy "same-origin" always;
add_header Cross-Origin-Embedder-Policy "require-corp" always;
add_header X-Content-Type-Options "nosniff" always;
try_files $uri =404;
}
# HTML files - no cache for updates
location ~* \.html$ {
expires -1;
add_header Cache-Control "no-cache, no-store, must-revalidate";
# Security headers
add_header Cross-Origin-Opener-Policy "same-origin" always;
add_header Cross-Origin-Embedder-Policy "require-corp" always;
add_header X-Content-Type-Options "nosniff" always;
try_files $uri /index.html;
}
# Root - serve React app
location / {
try_files $uri $uri/ /index.html;
# Cache control for root
expires -1;
add_header Cache-Control "no-cache, no-store, must-revalidate";
}
# Health check endpoint
location /health {
access_log off;
return 200 "healthy\n";
add_header Content-Type text/plain;
}
# Cloudflare verification endpoint
location /.well-known/cf-custom-hostname-challenge {
proxy_pass http://rust_backend;
}
}
}