-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathdefault.conf
More file actions
144 lines (130 loc) · 6.17 KB
/
Copy pathdefault.conf
File metadata and controls
144 lines (130 loc) · 6.17 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
# Ad-hoc SQL execution is the most expensive thing an authenticated user can do:
# each request can occupy a database connection for minutes. Cap the sustained
# rate per client, with a burst so a normal person clicking Run repeatedly is
# never throttled.
limit_req_zone $binary_remote_addr zone=sqlexec:10m rate=30r/m;
server {
listen 80;
server_name _;
root /usr/share/nginx/html;
index index.html;
# Compression
gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_types
text/plain
text/css
text/javascript
application/javascript
application/json
image/svg+xml;
# SPA routing — all non-file requests serve index.html (React Router handles the rest)
# index.html must never be cached so browsers always load the latest JS bundle references after deploys
location / {
try_files $uri $uri/ /index.html;
add_header Cache-Control "no-cache, no-store, must-revalidate";
add_header Pragma "no-cache";
add_header Expires "0";
}
# Ad-hoc SQL execution, rate-limited. Declared before the general /api/ rule
# so it wins for this path; everything else is identical to it.
# `nodelay` lets the burst through immediately rather than queueing it, so
# normal interactive use is unaffected and only sustained abuse is rejected.
location ~ ^/api/connections/[^/]+/query$ {
limit_req zone=sqlexec burst=20 nodelay;
limit_req_status 429;
proxy_pass http://backend:8080$request_uri;
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;
proxy_read_timeout 300s;
proxy_send_timeout 300s;
proxy_connect_timeout 10s;
proxy_buffering off;
proxy_cache off;
}
# API proxy to the backend service
# Allows the frontend to call /api/... without CORS issues
location /api/ {
proxy_pass http://backend:8080/api/;
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;
proxy_read_timeout 300s;
proxy_send_timeout 300s;
proxy_connect_timeout 10s;
# Disable response buffering — required for SSE (Server-Sent Events) streaming.
# Without this, nginx buffers the entire response before forwarding, breaking
# real-time token/progress streaming in the agentic chat flow.
proxy_buffering off;
proxy_cache off;
# Support large file uploads (slow query log upload endpoint)
client_max_body_size 2048m;
proxy_request_buffering off;
}
# Internal auth gate for the agent service. auth_request hits the backend's
# cheap authenticated probe with the caller's cookies; 2xx → allow, 401 → deny.
# Without this, /agent-api/* (profiles, sessions, chat) is reachable with NO
# authentication — anyone could start a session as another user's provisioned
# profile and use their MCP token. Keep this in front of every /agent-api hop.
location = /__agent_auth {
internal;
proxy_pass http://backend:8080/api/auth/me;
proxy_http_version 1.1;
proxy_pass_request_body off;
proxy_set_header Content-Length "";
proxy_set_header Host $host;
proxy_set_header Cookie $http_cookie;
# auth_request inherits the ORIGINAL request's headers. The chat stream is
# an EventSource, which sends `Accept: text/event-stream` — that would make
# Spring's JSON-only /api/auth/me return 406, and auth_request turns any
# non-2xx/401/403 into a 500 ("The agent run ended early"). Force JSON so
# the probe negotiates correctly.
proxy_set_header Accept "application/json";
}
# Agent chat proxy → the DeepSQL Agent service (:8787).
# Mirrors the dev Vite proxy: strip the /agent-api prefix so
# /agent-api/api/chat/stream reaches the agent's /api/chat/stream.
# SSE buffering MUST be off for token streaming.
location /agent-api/ {
# Require a valid DeepSQL session before reaching the agent.
auth_request /__agent_auth;
auth_request_set $deepsql_user $upstream_http_x_remote_user;
# Compose service on the internal network. Literal hostname resolves
# via Docker DNS. If the agent container is down this route returns
# 502 and the rest of the UI keeps working.
proxy_pass http://deepsql-agent:8787/;
proxy_http_version 1.1;
# The agent API CSRF compares Origin host:port to Host. `$host` drops
# the port (localhost vs localhost:3000) and profile/switch returns 403
# "Cross-origin mismatch". `$http_host` preserves the browser Host.
proxy_set_header Host $http_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;
# Identity for trusted-proxy mode inside the agent container.
# DeepSQL's auth_request already verified the session; stamp the
# *effective* user from /api/auth/me (View as overlays the target).
# Never hardcode admin — that ran every Agent tab as the admin MCP token.
proxy_set_header X-Remote-User $deepsql_user;
proxy_read_timeout 300s;
proxy_send_timeout 300s;
proxy_connect_timeout 10s;
proxy_buffering off;
proxy_cache off;
}
# Cache static assets aggressively
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
# Security headers
add_header X-Frame-Options SAMEORIGIN;
add_header X-Content-Type-Options nosniff;
add_header Referrer-Policy strict-origin-when-cross-origin;
}