- Added WEBUI_NAME variable to .env.example and Docker Compose for consistent branding. - Enhanced Nginx configuration with sub_filters to replace 'Open WebUI' with 'UltiAI' in responses. - Introduced custom CSS for UltiAI branding and added logo and favicon assets. - Implemented routing adjustments in Nginx to support SPA navigation without prefix issues.
1065 lines
47 KiB
Plaintext
1065 lines
47 KiB
Plaintext
# Edge reverse proxy — single entry point (replaces Caddy).
|
||
# Optional upstreams use Docker DNS resolver so nginx starts even if a module is disabled.
|
||
|
||
map $http_upgrade $connection_upgrade {
|
||
default upgrade;
|
||
'' close;
|
||
}
|
||
|
||
# Reflect browser Origin for cross-origin API calls (web app on :3004, API on :80).
|
||
map $http_origin $cors_allow_origin {
|
||
default $http_origin;
|
||
'' '*';
|
||
}
|
||
|
||
server {
|
||
listen 80;
|
||
server_name ${DOMAIN};
|
||
|
||
client_max_body_size 10G;
|
||
|
||
# --- ultid /api/v1/* (before OpenWebUI catch-alls; ^~ beats prefix /api/) ---
|
||
location ^~ /api/v1/mail/ {
|
||
resolver 127.0.0.11 valid=10s ipv6=off;
|
||
set $ultid_upstream ultid:8080;
|
||
proxy_hide_header Access-Control-Allow-Origin;
|
||
proxy_hide_header Access-Control-Allow-Methods;
|
||
proxy_hide_header Access-Control-Allow-Headers;
|
||
proxy_hide_header Access-Control-Expose-Headers;
|
||
proxy_hide_header Access-Control-Max-Age;
|
||
proxy_hide_header Access-Control-Allow-Credentials;
|
||
proxy_hide_header Vary;
|
||
add_header Access-Control-Allow-Origin $cors_allow_origin always;
|
||
add_header Access-Control-Allow-Methods "GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS" always;
|
||
add_header Access-Control-Allow-Headers "Accept, Authorization, Content-Type, Idempotency-Key, Origin, X-Requested-With, X-Trace-Id" always;
|
||
add_header Access-Control-Expose-Headers "X-Trace-Id" always;
|
||
add_header Access-Control-Max-Age 300 always;
|
||
add_header Vary Origin always;
|
||
proxy_pass http://$ultid_upstream;
|
||
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;
|
||
}
|
||
location ^~ /api/v1/migration/ {
|
||
resolver 127.0.0.11 valid=10s ipv6=off;
|
||
set $ultid_upstream ultid:8080;
|
||
proxy_hide_header Access-Control-Allow-Origin;
|
||
proxy_hide_header Access-Control-Allow-Methods;
|
||
proxy_hide_header Access-Control-Allow-Headers;
|
||
proxy_hide_header Access-Control-Expose-Headers;
|
||
proxy_hide_header Access-Control-Max-Age;
|
||
proxy_hide_header Access-Control-Allow-Credentials;
|
||
proxy_hide_header Vary;
|
||
add_header Access-Control-Allow-Origin $cors_allow_origin always;
|
||
add_header Access-Control-Allow-Methods "GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS" always;
|
||
add_header Access-Control-Allow-Headers "Accept, Authorization, Content-Type, Idempotency-Key, Origin, X-Requested-With, X-Trace-Id" always;
|
||
add_header Access-Control-Expose-Headers "X-Trace-Id" always;
|
||
add_header Access-Control-Max-Age 300 always;
|
||
add_header Vary Origin always;
|
||
proxy_pass http://$ultid_upstream;
|
||
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;
|
||
}
|
||
location ^~ /api/v1/admin/ {
|
||
resolver 127.0.0.11 valid=10s ipv6=off;
|
||
set $ultid_upstream ultid:8080;
|
||
proxy_hide_header Access-Control-Allow-Origin;
|
||
proxy_hide_header Access-Control-Allow-Methods;
|
||
proxy_hide_header Access-Control-Allow-Headers;
|
||
proxy_hide_header Access-Control-Expose-Headers;
|
||
proxy_hide_header Access-Control-Max-Age;
|
||
proxy_hide_header Access-Control-Allow-Credentials;
|
||
proxy_hide_header Vary;
|
||
add_header Access-Control-Allow-Origin $cors_allow_origin always;
|
||
add_header Access-Control-Allow-Methods "GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS" always;
|
||
add_header Access-Control-Allow-Headers "Accept, Authorization, Content-Type, Idempotency-Key, Origin, X-Requested-With, X-Trace-Id" always;
|
||
add_header Access-Control-Expose-Headers "X-Trace-Id" always;
|
||
add_header Access-Control-Max-Age 300 always;
|
||
add_header Vary Origin always;
|
||
proxy_pass http://$ultid_upstream;
|
||
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;
|
||
}
|
||
location ^~ /api/v1/drive/ {
|
||
resolver 127.0.0.11 valid=10s ipv6=off;
|
||
set $ultid_upstream ultid:8080;
|
||
proxy_hide_header Access-Control-Allow-Origin;
|
||
proxy_hide_header Access-Control-Allow-Methods;
|
||
proxy_hide_header Access-Control-Allow-Headers;
|
||
proxy_hide_header Access-Control-Expose-Headers;
|
||
proxy_hide_header Access-Control-Max-Age;
|
||
proxy_hide_header Access-Control-Allow-Credentials;
|
||
proxy_hide_header Vary;
|
||
add_header Access-Control-Allow-Origin $cors_allow_origin always;
|
||
add_header Access-Control-Allow-Methods "GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS" always;
|
||
add_header Access-Control-Allow-Headers "Accept, Authorization, Content-Type, Idempotency-Key, Origin, X-Requested-With, X-Trace-Id" always;
|
||
add_header Access-Control-Expose-Headers "X-Trace-Id" always;
|
||
add_header Access-Control-Max-Age 300 always;
|
||
add_header Vary Origin always;
|
||
proxy_pass http://$ultid_upstream;
|
||
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;
|
||
}
|
||
location ^~ /api/v1/office/ {
|
||
resolver 127.0.0.11 valid=10s ipv6=off;
|
||
set $ultid_upstream ultid:8080;
|
||
proxy_hide_header Access-Control-Allow-Origin;
|
||
proxy_hide_header Access-Control-Allow-Methods;
|
||
proxy_hide_header Access-Control-Allow-Headers;
|
||
proxy_hide_header Access-Control-Expose-Headers;
|
||
proxy_hide_header Access-Control-Max-Age;
|
||
proxy_hide_header Access-Control-Allow-Credentials;
|
||
proxy_hide_header Vary;
|
||
add_header Access-Control-Allow-Origin $cors_allow_origin always;
|
||
add_header Access-Control-Allow-Methods "GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS" always;
|
||
add_header Access-Control-Allow-Headers "Accept, Authorization, Content-Type, Idempotency-Key, Origin, X-Requested-With, X-Trace-Id" always;
|
||
add_header Access-Control-Expose-Headers "X-Trace-Id" always;
|
||
add_header Access-Control-Max-Age 300 always;
|
||
add_header Vary Origin always;
|
||
proxy_pass http://$ultid_upstream;
|
||
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;
|
||
}
|
||
location ^~ /api/v1/richtext/ {
|
||
resolver 127.0.0.11 valid=10s ipv6=off;
|
||
set $ultid_upstream ultid:8080;
|
||
proxy_hide_header Access-Control-Allow-Origin;
|
||
proxy_hide_header Access-Control-Allow-Methods;
|
||
proxy_hide_header Access-Control-Allow-Headers;
|
||
proxy_hide_header Access-Control-Expose-Headers;
|
||
proxy_hide_header Access-Control-Max-Age;
|
||
proxy_hide_header Access-Control-Allow-Credentials;
|
||
proxy_hide_header Vary;
|
||
add_header Access-Control-Allow-Origin $cors_allow_origin always;
|
||
add_header Access-Control-Allow-Methods "GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS" always;
|
||
add_header Access-Control-Allow-Headers "Accept, Authorization, Content-Type, Idempotency-Key, Origin, X-Requested-With, X-Trace-Id" always;
|
||
add_header Access-Control-Expose-Headers "X-Trace-Id" always;
|
||
add_header Access-Control-Max-Age 300 always;
|
||
add_header Vary Origin always;
|
||
proxy_pass http://$ultid_upstream;
|
||
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;
|
||
}
|
||
location ^~ /api/v1/ultidraw/ {
|
||
resolver 127.0.0.11 valid=10s ipv6=off;
|
||
set $ultid_upstream ultid:8080;
|
||
proxy_hide_header Access-Control-Allow-Origin;
|
||
proxy_hide_header Access-Control-Allow-Methods;
|
||
proxy_hide_header Access-Control-Allow-Headers;
|
||
proxy_hide_header Access-Control-Expose-Headers;
|
||
proxy_hide_header Access-Control-Max-Age;
|
||
proxy_hide_header Access-Control-Allow-Credentials;
|
||
proxy_hide_header Vary;
|
||
add_header Access-Control-Allow-Origin $cors_allow_origin always;
|
||
add_header Access-Control-Allow-Methods "GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS" always;
|
||
add_header Access-Control-Allow-Headers "Accept, Authorization, Content-Type, Idempotency-Key, Origin, X-Requested-With, X-Trace-Id" always;
|
||
add_header Access-Control-Expose-Headers "X-Trace-Id" always;
|
||
add_header Access-Control-Max-Age 300 always;
|
||
add_header Vary Origin always;
|
||
proxy_pass http://$ultid_upstream;
|
||
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;
|
||
}
|
||
location ^~ /api/v1/ai/ {
|
||
resolver 127.0.0.11 valid=10s ipv6=off;
|
||
set $ultid_upstream ultid:8080;
|
||
proxy_hide_header Access-Control-Allow-Origin;
|
||
proxy_hide_header Access-Control-Allow-Methods;
|
||
proxy_hide_header Access-Control-Allow-Headers;
|
||
proxy_hide_header Access-Control-Expose-Headers;
|
||
proxy_hide_header Access-Control-Max-Age;
|
||
proxy_hide_header Access-Control-Allow-Credentials;
|
||
proxy_hide_header Vary;
|
||
add_header Access-Control-Allow-Origin $cors_allow_origin always;
|
||
add_header Access-Control-Allow-Methods "GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS" always;
|
||
add_header Access-Control-Allow-Headers "Accept, Authorization, Content-Type, Idempotency-Key, Origin, X-Requested-With, X-Trace-Id" always;
|
||
add_header Access-Control-Expose-Headers "X-Trace-Id" always;
|
||
add_header Access-Control-Max-Age 300 always;
|
||
add_header Vary Origin always;
|
||
proxy_pass http://$ultid_upstream;
|
||
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;
|
||
}
|
||
location ^~ /api/v1/users/me {
|
||
resolver 127.0.0.11 valid=10s ipv6=off;
|
||
set $ultid_upstream ultid:8080;
|
||
proxy_hide_header Access-Control-Allow-Origin;
|
||
proxy_hide_header Access-Control-Allow-Methods;
|
||
proxy_hide_header Access-Control-Allow-Headers;
|
||
proxy_hide_header Access-Control-Expose-Headers;
|
||
proxy_hide_header Access-Control-Max-Age;
|
||
proxy_hide_header Access-Control-Allow-Credentials;
|
||
proxy_hide_header Vary;
|
||
add_header Access-Control-Allow-Origin $cors_allow_origin always;
|
||
add_header Access-Control-Allow-Methods "GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS" always;
|
||
add_header Access-Control-Allow-Headers "Accept, Authorization, Content-Type, Idempotency-Key, Origin, X-Requested-With, X-Trace-Id" always;
|
||
add_header Access-Control-Expose-Headers "X-Trace-Id" always;
|
||
add_header Access-Control-Max-Age 300 always;
|
||
add_header Vary Origin always;
|
||
proxy_pass http://$ultid_upstream;
|
||
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;
|
||
}
|
||
location ^~ /api/v1/calendar/ {
|
||
resolver 127.0.0.11 valid=10s ipv6=off;
|
||
set $ultid_upstream ultid:8080;
|
||
proxy_hide_header Access-Control-Allow-Origin;
|
||
proxy_hide_header Access-Control-Allow-Methods;
|
||
proxy_hide_header Access-Control-Allow-Headers;
|
||
proxy_hide_header Access-Control-Expose-Headers;
|
||
proxy_hide_header Access-Control-Max-Age;
|
||
proxy_hide_header Access-Control-Allow-Credentials;
|
||
proxy_hide_header Vary;
|
||
add_header Access-Control-Allow-Origin $cors_allow_origin always;
|
||
add_header Access-Control-Allow-Methods "GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS" always;
|
||
add_header Access-Control-Allow-Headers "Accept, Authorization, Content-Type, Idempotency-Key, Origin, X-Requested-With, X-Trace-Id" always;
|
||
add_header Access-Control-Expose-Headers "X-Trace-Id" always;
|
||
add_header Access-Control-Max-Age 300 always;
|
||
add_header Vary Origin always;
|
||
proxy_pass http://$ultid_upstream;
|
||
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;
|
||
}
|
||
location ^~ /api/v1/contacts/ {
|
||
resolver 127.0.0.11 valid=10s ipv6=off;
|
||
set $ultid_upstream ultid:8080;
|
||
proxy_hide_header Access-Control-Allow-Origin;
|
||
proxy_hide_header Access-Control-Allow-Methods;
|
||
proxy_hide_header Access-Control-Allow-Headers;
|
||
proxy_hide_header Access-Control-Expose-Headers;
|
||
proxy_hide_header Access-Control-Max-Age;
|
||
proxy_hide_header Access-Control-Allow-Credentials;
|
||
proxy_hide_header Vary;
|
||
add_header Access-Control-Allow-Origin $cors_allow_origin always;
|
||
add_header Access-Control-Allow-Methods "GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS" always;
|
||
add_header Access-Control-Allow-Headers "Accept, Authorization, Content-Type, Idempotency-Key, Origin, X-Requested-With, X-Trace-Id" always;
|
||
add_header Access-Control-Expose-Headers "X-Trace-Id" always;
|
||
add_header Access-Control-Max-Age 300 always;
|
||
add_header Vary Origin always;
|
||
proxy_pass http://$ultid_upstream;
|
||
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;
|
||
}
|
||
location ^~ /api/v1/meet/ {
|
||
resolver 127.0.0.11 valid=10s ipv6=off;
|
||
set $ultid_upstream ultid:8080;
|
||
proxy_hide_header Access-Control-Allow-Origin;
|
||
proxy_hide_header Access-Control-Allow-Methods;
|
||
proxy_hide_header Access-Control-Allow-Headers;
|
||
proxy_hide_header Access-Control-Expose-Headers;
|
||
proxy_hide_header Access-Control-Max-Age;
|
||
proxy_hide_header Access-Control-Allow-Credentials;
|
||
proxy_hide_header Vary;
|
||
add_header Access-Control-Allow-Origin $cors_allow_origin always;
|
||
add_header Access-Control-Allow-Methods "GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS" always;
|
||
add_header Access-Control-Allow-Headers "Accept, Authorization, Content-Type, Idempotency-Key, Origin, X-Requested-With, X-Trace-Id" always;
|
||
add_header Access-Control-Expose-Headers "X-Trace-Id" always;
|
||
add_header Access-Control-Max-Age 300 always;
|
||
add_header Vary Origin always;
|
||
proxy_pass http://$ultid_upstream;
|
||
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;
|
||
}
|
||
location ^~ /api/v1/photos/ {
|
||
resolver 127.0.0.11 valid=10s ipv6=off;
|
||
set $ultid_upstream ultid:8080;
|
||
proxy_hide_header Access-Control-Allow-Origin;
|
||
proxy_hide_header Access-Control-Allow-Methods;
|
||
proxy_hide_header Access-Control-Allow-Headers;
|
||
proxy_hide_header Access-Control-Expose-Headers;
|
||
proxy_hide_header Access-Control-Max-Age;
|
||
proxy_hide_header Access-Control-Allow-Credentials;
|
||
proxy_hide_header Vary;
|
||
add_header Access-Control-Allow-Origin $cors_allow_origin always;
|
||
add_header Access-Control-Allow-Methods "GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS" always;
|
||
add_header Access-Control-Allow-Headers "Accept, Authorization, Content-Type, Idempotency-Key, Origin, X-Requested-With, X-Trace-Id" always;
|
||
add_header Access-Control-Expose-Headers "X-Trace-Id" always;
|
||
add_header Access-Control-Max-Age 300 always;
|
||
add_header Vary Origin always;
|
||
proxy_pass http://$ultid_upstream;
|
||
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;
|
||
}
|
||
location ^~ /api/v1/search {
|
||
resolver 127.0.0.11 valid=10s ipv6=off;
|
||
set $ultid_upstream ultid:8080;
|
||
proxy_hide_header Access-Control-Allow-Origin;
|
||
proxy_hide_header Access-Control-Allow-Methods;
|
||
proxy_hide_header Access-Control-Allow-Headers;
|
||
proxy_hide_header Access-Control-Expose-Headers;
|
||
proxy_hide_header Access-Control-Max-Age;
|
||
proxy_hide_header Access-Control-Allow-Credentials;
|
||
proxy_hide_header Vary;
|
||
add_header Access-Control-Allow-Origin $cors_allow_origin always;
|
||
add_header Access-Control-Allow-Methods "GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS" always;
|
||
add_header Access-Control-Allow-Headers "Accept, Authorization, Content-Type, Idempotency-Key, Origin, X-Requested-With, X-Trace-Id" always;
|
||
add_header Access-Control-Expose-Headers "X-Trace-Id" always;
|
||
add_header Access-Control-Max-Age 300 always;
|
||
add_header Vary Origin always;
|
||
proxy_pass http://$ultid_upstream;
|
||
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;
|
||
}
|
||
|
||
# OpenWebUI trusted-header signin — ultid injects X-Ulti-User-* (auth_request + POST body deadlocks)
|
||
location = /api/v1/auths/signin {
|
||
resolver 127.0.0.11 valid=10s ipv6=off;
|
||
set $ultid_upstream ultid:8080;
|
||
proxy_hide_header Access-Control-Allow-Origin;
|
||
proxy_hide_header Access-Control-Allow-Methods;
|
||
proxy_hide_header Access-Control-Allow-Headers;
|
||
proxy_hide_header Access-Control-Expose-Headers;
|
||
proxy_hide_header Access-Control-Max-Age;
|
||
proxy_hide_header Access-Control-Allow-Credentials;
|
||
proxy_hide_header Vary;
|
||
add_header Access-Control-Allow-Origin $cors_allow_origin always;
|
||
add_header Access-Control-Allow-Methods "GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS" always;
|
||
add_header Access-Control-Allow-Headers "Accept, Authorization, Content-Type, Idempotency-Key, Origin, X-Requested-With, X-Trace-Id" always;
|
||
add_header Access-Control-Expose-Headers "X-Trace-Id" always;
|
||
add_header Access-Control-Max-Age 300 always;
|
||
add_header Vary Origin always;
|
||
proxy_pass http://$ultid_upstream/api/v1/ai/embed-signin;
|
||
proxy_http_version 1.1;
|
||
proxy_set_header Host $host;
|
||
proxy_set_header Cookie $http_cookie;
|
||
proxy_set_header Authorization $http_authorization;
|
||
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;
|
||
}
|
||
|
||
# --- OpenWebUI API at site root (prod SPA: WEBUI_BASE_URL="" → fetch("/api/…")) ---
|
||
# No auth_request here: POST + auth_request deadlocks (embed-auth timeout → 500).
|
||
# Ultimail gate is /ai/ HTML; OpenWebUI API auth uses its own JWT (signin via embed-signin).
|
||
location ~ ^/api/(config|changelog|version|webhook|usage|models|embeddings|message|chat|tasks)(/|$) {
|
||
resolver 127.0.0.11 valid=10s ipv6=off;
|
||
set $openwebui_upstream openwebui:8080;
|
||
proxy_pass http://$openwebui_upstream;
|
||
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_set_header Upgrade $http_upgrade;
|
||
proxy_set_header Connection $connection_upgrade;
|
||
proxy_set_header Accept-Encoding "";
|
||
sub_filter_once off;
|
||
sub_filter_types application/json text/plain;
|
||
sub_filter 'UltiAI (Open WebUI)' 'UltiAI';
|
||
sub_filter 'Open WebUI' 'UltiAI';
|
||
proxy_read_timeout 86400s;
|
||
proxy_send_timeout 86400s;
|
||
}
|
||
location ~ ^/api/v1/(auths|channels|chats|notes|models|knowledge|prompts|tools|skills|memories|folders|groups|files|functions|evaluations|analytics|utils|terminals|automations|calendars|scim|pipelines|tasks|images|audio|retrieval|configs|users|chat|embeddings|messages)(/|$) {
|
||
resolver 127.0.0.11 valid=10s ipv6=off;
|
||
set $openwebui_upstream openwebui:8080;
|
||
proxy_pass http://$openwebui_upstream;
|
||
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_set_header Upgrade $http_upgrade;
|
||
proxy_set_header Connection $connection_upgrade;
|
||
proxy_read_timeout 86400s;
|
||
proxy_send_timeout 86400s;
|
||
}
|
||
|
||
# ultid API fallback (must stay after ^~ /api/auth/ — mail OIDC routes)
|
||
location /api/ {
|
||
resolver 127.0.0.11 valid=10s ipv6=off;
|
||
set $ultid_upstream ultid:8080;
|
||
|
||
proxy_hide_header Access-Control-Allow-Origin;
|
||
proxy_hide_header Access-Control-Allow-Methods;
|
||
proxy_hide_header Access-Control-Allow-Headers;
|
||
proxy_hide_header Access-Control-Expose-Headers;
|
||
proxy_hide_header Access-Control-Max-Age;
|
||
proxy_hide_header Access-Control-Allow-Credentials;
|
||
proxy_hide_header Vary;
|
||
|
||
add_header Access-Control-Allow-Origin $cors_allow_origin always;
|
||
add_header Access-Control-Allow-Methods "GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS" always;
|
||
add_header Access-Control-Allow-Headers "Accept, Authorization, Content-Type, Idempotency-Key, Origin, X-Requested-With, X-Trace-Id" always;
|
||
add_header Access-Control-Expose-Headers "X-Trace-Id" always;
|
||
add_header Access-Control-Max-Age 300 always;
|
||
add_header Vary Origin always;
|
||
|
||
proxy_pass http://$ultid_upstream;
|
||
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;
|
||
}
|
||
|
||
# OpenWebUI socket.io (ultid uses /ws without /socket.io)
|
||
location ^~ /ws/socket.io {
|
||
resolver 127.0.0.11 valid=10s ipv6=off;
|
||
set $openwebui_upstream openwebui:8080;
|
||
proxy_pass http://$openwebui_upstream;
|
||
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 $remote_addr;
|
||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||
proxy_set_header X-Forwarded-Proto $scheme;
|
||
proxy_read_timeout 86400s;
|
||
proxy_send_timeout 86400s;
|
||
}
|
||
|
||
location /ws {
|
||
resolver 127.0.0.11 valid=10s ipv6=off;
|
||
set $ultid_ws_upstream ultid:8080;
|
||
|
||
proxy_hide_header Access-Control-Allow-Origin;
|
||
|
||
add_header Access-Control-Allow-Origin $cors_allow_origin always;
|
||
add_header Vary Origin always;
|
||
|
||
proxy_pass http://$ultid_ws_upstream;
|
||
proxy_http_version 1.1;
|
||
proxy_set_header Upgrade $http_upgrade;
|
||
proxy_set_header Connection "upgrade";
|
||
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;
|
||
}
|
||
|
||
# OpenWebUI Ollama/OpenAI proxy mounts (SPA uses /ollama, /openai at root)
|
||
location ^~ /ollama/ {
|
||
resolver 127.0.0.11 valid=10s ipv6=off;
|
||
set $openwebui_upstream openwebui:8080;
|
||
proxy_pass http://$openwebui_upstream;
|
||
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;
|
||
}
|
||
location ^~ /openai/ {
|
||
resolver 127.0.0.11 valid=10s ipv6=off;
|
||
set $openwebui_upstream openwebui:8080;
|
||
proxy_pass http://$openwebui_upstream;
|
||
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;
|
||
}
|
||
|
||
# TipTap / Hocuspocus — proxy WS without redirect (301 breaks upgrade)
|
||
location /collab {
|
||
resolver 127.0.0.11 valid=10s ipv6=off;
|
||
set $hocuspocus_upstream host.docker.internal:1234;
|
||
|
||
rewrite ^/collab/?(.*)$ /$1 break;
|
||
proxy_pass http://$hocuspocus_upstream;
|
||
proxy_http_version 1.1;
|
||
proxy_set_header Upgrade $http_upgrade;
|
||
proxy_set_header Connection "upgrade";
|
||
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 86400s;
|
||
proxy_send_timeout 86400s;
|
||
}
|
||
|
||
# Ultimail OIDC post-login — before Authentik /auth/ (path collision)
|
||
location ^~ /auth/complete {
|
||
resolver 127.0.0.11 valid=10s ipv6=off;
|
||
set $mail_upstream ${MAIL_FRONTEND_UPSTREAM};
|
||
proxy_pass http://$mail_upstream;
|
||
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_set_header Upgrade $http_upgrade;
|
||
proxy_set_header Connection $connection_upgrade;
|
||
}
|
||
|
||
location /auth/ {
|
||
resolver 127.0.0.11 valid=10s ipv6=off;
|
||
set $authentik_upstream authentik-server:9000;
|
||
proxy_pass http://$authentik_upstream;
|
||
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_set_header Upgrade $http_upgrade;
|
||
proxy_set_header Connection $connection_upgrade;
|
||
proxy_read_timeout 86400;
|
||
proxy_hide_header X-Frame-Options;
|
||
proxy_hide_header Content-Security-Policy;
|
||
# Permet l’embed du portail Authentik dans la suite (même host + dev Next :3004).
|
||
add_header Content-Security-Policy "frame-ancestors 'self' http://localhost:3004 http://127.0.0.1:3004" always;
|
||
}
|
||
|
||
location /meet/ {
|
||
resolver 127.0.0.11 valid=10s ipv6=off;
|
||
set $jitsi_upstream jitsi-web;
|
||
rewrite ^/meet/(.*)$ /$1 break;
|
||
proxy_pass http://$jitsi_upstream;
|
||
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;
|
||
}
|
||
|
||
# Public Nextcloud share links → UltiDrive viewer
|
||
location ~ ^/cloud/index\.php/s/([^/]+)/?(.*)$ {
|
||
return 301 /drive/s/$1$is_args$args;
|
||
}
|
||
|
||
location /cloud/ {
|
||
resolver 127.0.0.11 valid=10s ipv6=off;
|
||
set $nc_upstream nextcloud;
|
||
rewrite ^/cloud/(.*)$ /$1 break;
|
||
proxy_pass http://$nc_upstream;
|
||
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;
|
||
}
|
||
|
||
location = /cloud {
|
||
return 301 /cloud/;
|
||
}
|
||
|
||
# OnlyOffice fetches Nextcloud after StorageUrl host rewrite (URLs lack /cloud prefix).
|
||
location ^~ /index.php {
|
||
resolver 127.0.0.11 valid=10s ipv6=off;
|
||
set $nc_upstream nextcloud;
|
||
proxy_pass http://$nc_upstream;
|
||
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;
|
||
}
|
||
|
||
# OpenWebUI — same-origin proxy with trusted-header SSO
|
||
location = /api/v1/ai/embed-auth {
|
||
resolver 127.0.0.11 valid=10s ipv6=off;
|
||
set $ultid_upstream ultid:8080;
|
||
proxy_pass http://$ultid_upstream;
|
||
proxy_http_version 1.1;
|
||
proxy_set_header Host $host;
|
||
proxy_set_header Cookie $http_cookie;
|
||
proxy_set_header Authorization $http_authorization;
|
||
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;
|
||
}
|
||
|
||
location /ai/ {
|
||
resolver 127.0.0.11 valid=10s ipv6=off;
|
||
set $openwebui_upstream openwebui:8080;
|
||
|
||
auth_request /api/v1/ai/embed-auth;
|
||
auth_request_set $ulti_user_email $upstream_http_x_ulti_user_email;
|
||
auth_request_set $ulti_user_name $upstream_http_x_ulti_user_name;
|
||
auth_request_set $ulti_user_role $upstream_http_x_ulti_user_role;
|
||
|
||
proxy_hide_header X-Frame-Options;
|
||
add_header Content-Security-Policy "frame-ancestors 'self'" always;
|
||
|
||
rewrite ^/ai/?(.*)$ /$1 break;
|
||
proxy_pass http://$openwebui_upstream;
|
||
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_set_header X-Forwarded-Prefix /ai;
|
||
proxy_set_header Upgrade $http_upgrade;
|
||
proxy_set_header Connection $connection_upgrade;
|
||
proxy_set_header X-Ulti-User-Email $ulti_user_email;
|
||
proxy_set_header X-Ulti-User-Name $ulti_user_name;
|
||
proxy_set_header X-Ulti-User-Role $ulti_user_role;
|
||
proxy_read_timeout 86400s;
|
||
proxy_send_timeout 86400s;
|
||
|
||
# OpenWebUI serves root-relative /static, /_app, /api — prefix for subpath /ai/
|
||
proxy_set_header Accept-Encoding "";
|
||
sub_filter_once off;
|
||
sub_filter_types text/html text/css application/javascript application/json text/javascript;
|
||
sub_filter 'href="/static/' 'href="/ai/static/';
|
||
sub_filter 'src="/static/' 'src="/ai/static/';
|
||
sub_filter 'href="/_app/' 'href="/ai/_app/';
|
||
sub_filter 'src="/_app/' 'src="/ai/_app/';
|
||
sub_filter '"/_app/' '"/ai/_app/';
|
||
sub_filter "'/_app/" "'/ai/_app/";
|
||
sub_filter '"/static/' '"/ai/static/';
|
||
sub_filter "'/static/" "'/ai/static/";
|
||
sub_filter '"/api/' '"/ai/api/';
|
||
sub_filter "'/api/" "'/ai/api/";
|
||
sub_filter 'href="/manifest.json' 'href="/ai/manifest.json';
|
||
sub_filter '"/manifest.json' '"/ai/manifest.json';
|
||
# SvelteKit base path — without this, /ai/ routes 404 (base "" expects site root)
|
||
sub_filter 'base: ""' 'base: "/ai"';
|
||
sub_filter "base: ''" 'base: "/ai"';
|
||
# In-app links that escape to site root (e.g. "Nouvelle conversation" → /)
|
||
sub_filter 'href="/"' 'href="/ai/"';
|
||
sub_filter "href='/'" "href='/ai/'";
|
||
sub_filter 'href="/notes' 'href="/ai/notes';
|
||
sub_filter 'href="/workspace' 'href="/ai/workspace';
|
||
sub_filter 'href="/home' 'href="/ai/home';
|
||
sub_filter 'href="/c/' 'href="/ai/c/';
|
||
sub_filter 'href="/automations' 'href="/ai/automations';
|
||
sub_filter 'href="/calendar' 'href="/ai/calendar';
|
||
sub_filter 'href="/channels' 'href="/ai/channels';
|
||
sub_filter 'href="/playground' 'href="/ai/playground';
|
||
# UltiAI branding (WEBUI_NAME env still appends " (Open WebUI)" in backend)
|
||
sub_filter 'UltiAI (Open WebUI)' 'UltiAI';
|
||
sub_filter 'Open WebUI' 'UltiAI';
|
||
}
|
||
|
||
location = /ai {
|
||
return 301 /ai/;
|
||
}
|
||
|
||
# OpenWebUI API (prefix /ai/api — évite collision avec ultid /api/v1/)
|
||
location /ai/api/ {
|
||
resolver 127.0.0.11 valid=10s ipv6=off;
|
||
set $openwebui_upstream openwebui:8080;
|
||
|
||
rewrite ^/ai/api/?(.*)$ /api/$1 break;
|
||
proxy_pass http://$openwebui_upstream;
|
||
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_set_header Upgrade $http_upgrade;
|
||
proxy_set_header Connection $connection_upgrade;
|
||
proxy_read_timeout 86400s;
|
||
proxy_send_timeout 86400s;
|
||
}
|
||
|
||
# OpenWebUI assets (SPA uses root-relative /static, /_app — not /ai/…)
|
||
location ^~ /static/ {
|
||
resolver 127.0.0.11 valid=10s ipv6=off;
|
||
set $openwebui_upstream openwebui:8080;
|
||
|
||
auth_request /api/v1/ai/embed-auth;
|
||
auth_request_set $ulti_user_email $upstream_http_x_ulti_user_email;
|
||
auth_request_set $ulti_user_name $upstream_http_x_ulti_user_name;
|
||
auth_request_set $ulti_user_role $upstream_http_x_ulti_user_role;
|
||
|
||
proxy_pass http://$openwebui_upstream;
|
||
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_set_header X-Ulti-User-Email $ulti_user_email;
|
||
proxy_set_header X-Ulti-User-Name $ulti_user_name;
|
||
proxy_set_header X-Ulti-User-Role $ulti_user_role;
|
||
|
||
proxy_set_header Accept-Encoding "";
|
||
sub_filter_once off;
|
||
sub_filter_types application/javascript text/javascript;
|
||
sub_filter '"/api/' '"/ai/api/';
|
||
sub_filter "'/api/" "'/ai/api/";
|
||
sub_filter 'href="/"' 'href="/ai/"';
|
||
sub_filter "href='/'" "href='/ai/'";
|
||
sub_filter 'Open WebUI' 'UltiAI';
|
||
sub_filter 'UltiAI (Open WebUI)' 'UltiAI';
|
||
}
|
||
|
||
location ^~ /_app/ {
|
||
resolver 127.0.0.11 valid=10s ipv6=off;
|
||
set $openwebui_upstream openwebui:8080;
|
||
|
||
auth_request /api/v1/ai/embed-auth;
|
||
auth_request_set $ulti_user_email $upstream_http_x_ulti_user_email;
|
||
auth_request_set $ulti_user_name $upstream_http_x_ulti_user_name;
|
||
auth_request_set $ulti_user_role $upstream_http_x_ulti_user_role;
|
||
|
||
proxy_pass http://$openwebui_upstream;
|
||
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_set_header X-Ulti-User-Email $ulti_user_email;
|
||
proxy_set_header X-Ulti-User-Name $ulti_user_name;
|
||
proxy_set_header X-Ulti-User-Role $ulti_user_role;
|
||
|
||
proxy_set_header Accept-Encoding "";
|
||
sub_filter_once off;
|
||
sub_filter_types application/javascript text/javascript application/json;
|
||
sub_filter '"/api/' '"/ai/api/';
|
||
sub_filter "'/api/" "'/ai/api/";
|
||
sub_filter 'href="/"' 'href="/ai/"';
|
||
sub_filter "href='/'" "href='/ai/'";
|
||
sub_filter 'Open WebUI' 'UltiAI';
|
||
sub_filter 'UltiAI (Open WebUI)' 'UltiAI';
|
||
}
|
||
|
||
location = /manifest.json {
|
||
resolver 127.0.0.11 valid=10s ipv6=off;
|
||
set $openwebui_upstream openwebui:8080;
|
||
|
||
auth_request /api/v1/ai/embed-auth;
|
||
auth_request_set $ulti_user_email $upstream_http_x_ulti_user_email;
|
||
auth_request_set $ulti_user_name $upstream_http_x_ulti_user_name;
|
||
auth_request_set $ulti_user_role $upstream_http_x_ulti_user_role;
|
||
|
||
proxy_pass http://$openwebui_upstream/manifest.json;
|
||
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_set_header X-Ulti-User-Email $ulti_user_email;
|
||
proxy_set_header X-Ulti-User-Name $ulti_user_name;
|
||
proxy_set_header X-Ulti-User-Role $ulti_user_role;
|
||
}
|
||
|
||
location /office/ {
|
||
resolver 127.0.0.11 valid=10s ipv6=off;
|
||
set $oo_upstream onlyoffice;
|
||
rewrite ^/office/(.*)$ /$1 break;
|
||
proxy_pass http://$oo_upstream;
|
||
proxy_http_version 1.1;
|
||
proxy_set_header Host $host;
|
||
proxy_set_header X-Forwarded-Host $host/office;
|
||
proxy_set_header X-Forwarded-Proto $scheme;
|
||
proxy_set_header X-Real-IP $remote_addr;
|
||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||
proxy_set_header Upgrade $http_upgrade;
|
||
proxy_set_header Connection $connection_upgrade;
|
||
proxy_read_timeout 3600s;
|
||
}
|
||
|
||
location = /office {
|
||
return 301 /office/;
|
||
}
|
||
|
||
# UltiDrive — same suite frontend as mail (unified Next.js app)
|
||
location /drive/ {
|
||
resolver 127.0.0.11 valid=10s ipv6=off;
|
||
set $mail_upstream ${MAIL_FRONTEND_UPSTREAM};
|
||
proxy_pass http://$mail_upstream;
|
||
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_set_header Upgrade $http_upgrade;
|
||
proxy_set_header Connection $connection_upgrade;
|
||
}
|
||
|
||
location = /drive {
|
||
resolver 127.0.0.11 valid=10s ipv6=off;
|
||
set $mail_upstream ${MAIL_FRONTEND_UPSTREAM};
|
||
proxy_pass http://$mail_upstream;
|
||
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_set_header Upgrade $http_upgrade;
|
||
proxy_set_header Connection $connection_upgrade;
|
||
}
|
||
|
||
# Ulti Suite frontend (mail + drive + contacts + agenda + compte) — dev: pnpm dev on host (MAIL_FRONTEND_UPSTREAM=host.docker.internal:3004)
|
||
# Prod: set MAIL_FRONTEND_UPSTREAM=suite-frontend:3000
|
||
# Démos publiques de la landing (zéro rétention) — frontend Next.
|
||
location ^~ /demo {
|
||
resolver 127.0.0.11 valid=10s ipv6=off;
|
||
set $mail_upstream ${MAIL_FRONTEND_UPSTREAM};
|
||
proxy_pass http://$mail_upstream;
|
||
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_set_header Upgrade $http_upgrade;
|
||
proxy_set_header Connection $connection_upgrade;
|
||
}
|
||
|
||
# Sauvegarde no-op des démos — route API du frontend Next, pas ultid.
|
||
location ^~ /api/demo/ {
|
||
resolver 127.0.0.11 valid=10s ipv6=off;
|
||
set $mail_upstream ${MAIL_FRONTEND_UPSTREAM};
|
||
proxy_pass http://$mail_upstream;
|
||
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;
|
||
}
|
||
|
||
location ^~ /api/auth/ {
|
||
resolver 127.0.0.11 valid=10s ipv6=off;
|
||
set $mail_upstream ${MAIL_FRONTEND_UPSTREAM};
|
||
proxy_pass http://$mail_upstream;
|
||
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;
|
||
}
|
||
|
||
location ^~ /mail/ {
|
||
resolver 127.0.0.11 valid=10s ipv6=off;
|
||
set $mail_upstream ${MAIL_FRONTEND_UPSTREAM};
|
||
proxy_pass http://$mail_upstream;
|
||
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_set_header Upgrade $http_upgrade;
|
||
proxy_set_header Connection $connection_upgrade;
|
||
}
|
||
|
||
# Do not 301 /mail → /mail/ (Next 308 /mail/ → /mail causes a redirect loop).
|
||
location = /mail {
|
||
resolver 127.0.0.11 valid=10s ipv6=off;
|
||
set $mail_upstream ${MAIL_FRONTEND_UPSTREAM};
|
||
proxy_pass http://$mail_upstream;
|
||
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_set_header Upgrade $http_upgrade;
|
||
proxy_set_header Connection $connection_upgrade;
|
||
}
|
||
|
||
location = /mail/ {
|
||
return 302 /mail/inbox;
|
||
}
|
||
|
||
location ^~ /login {
|
||
resolver 127.0.0.11 valid=10s ipv6=off;
|
||
set $mail_upstream ${MAIL_FRONTEND_UPSTREAM};
|
||
proxy_pass http://$mail_upstream;
|
||
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_set_header Upgrade $http_upgrade;
|
||
proxy_set_header Connection $connection_upgrade;
|
||
}
|
||
|
||
location ^~ /chat {
|
||
resolver 127.0.0.11 valid=10s ipv6=off;
|
||
set $mail_upstream ${MAIL_FRONTEND_UPSTREAM};
|
||
proxy_pass http://$mail_upstream;
|
||
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_set_header Upgrade $http_upgrade;
|
||
proxy_set_header Connection $connection_upgrade;
|
||
}
|
||
|
||
location ^~ /contacts {
|
||
resolver 127.0.0.11 valid=10s ipv6=off;
|
||
set $mail_upstream ${MAIL_FRONTEND_UPSTREAM};
|
||
proxy_pass http://$mail_upstream;
|
||
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_set_header Upgrade $http_upgrade;
|
||
proxy_set_header Connection $connection_upgrade;
|
||
}
|
||
|
||
location ^~ /agenda {
|
||
resolver 127.0.0.11 valid=10s ipv6=off;
|
||
set $mail_upstream ${MAIL_FRONTEND_UPSTREAM};
|
||
proxy_pass http://$mail_upstream;
|
||
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_set_header Upgrade $http_upgrade;
|
||
proxy_set_header Connection $connection_upgrade;
|
||
}
|
||
|
||
# Réglages du compte Ulti
|
||
location ^~ /compte {
|
||
resolver 127.0.0.11 valid=10s ipv6=off;
|
||
set $mail_upstream ${MAIL_FRONTEND_UPSTREAM};
|
||
proxy_pass http://$mail_upstream;
|
||
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_set_header Upgrade $http_upgrade;
|
||
proxy_set_header Connection $connection_upgrade;
|
||
}
|
||
|
||
# Console d'administration suite
|
||
location ^~ /admin {
|
||
resolver 127.0.0.11 valid=10s ipv6=off;
|
||
set $mail_upstream ${MAIL_FRONTEND_UPSTREAM};
|
||
proxy_pass http://$mail_upstream;
|
||
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_set_header Upgrade $http_upgrade;
|
||
proxy_set_header Connection $connection_upgrade;
|
||
}
|
||
|
||
location ^~ /_next/ {
|
||
resolver 127.0.0.11 valid=10s ipv6=off;
|
||
set $mail_upstream ${MAIL_FRONTEND_UPSTREAM};
|
||
proxy_pass http://$mail_upstream;
|
||
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_set_header Upgrade $http_upgrade;
|
||
proxy_set_header Connection $connection_upgrade;
|
||
}
|
||
|
||
# next/font (Geist, etc.) — separate from /_next/ static chunks
|
||
location ^~ /__nextjs_font/ {
|
||
resolver 127.0.0.11 valid=10s ipv6=off;
|
||
set $mail_upstream ${MAIL_FRONTEND_UPSTREAM};
|
||
proxy_pass http://$mail_upstream;
|
||
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;
|
||
}
|
||
|
||
location ^~ /brand/ {
|
||
resolver 127.0.0.11 valid=10s ipv6=off;
|
||
set $mail_upstream ${MAIL_FRONTEND_UPSTREAM};
|
||
proxy_pass http://$mail_upstream;
|
||
proxy_http_version 1.1;
|
||
proxy_set_header Host $host;
|
||
proxy_set_header X-Forwarded-Proto $scheme;
|
||
}
|
||
|
||
location ^~ /mail-backgrounds/ {
|
||
resolver 127.0.0.11 valid=10s ipv6=off;
|
||
set $mail_upstream ${MAIL_FRONTEND_UPSTREAM};
|
||
proxy_pass http://$mail_upstream;
|
||
proxy_http_version 1.1;
|
||
proxy_set_header Host $host;
|
||
proxy_set_header X-Forwarded-Proto $scheme;
|
||
}
|
||
|
||
# Public assets at repo root (launcher icons, etc.)
|
||
location ~* ^/[^/]+\.(svg|png|ico|webp)$ {
|
||
resolver 127.0.0.11 valid=10s ipv6=off;
|
||
set $mail_upstream ${MAIL_FRONTEND_UPSTREAM};
|
||
proxy_pass http://$mail_upstream$request_uri;
|
||
proxy_http_version 1.1;
|
||
proxy_set_header Host $host;
|
||
proxy_set_header X-Forwarded-Proto $scheme;
|
||
}
|
||
|
||
# Landing page de la suite (servie par le frontend Next).
|
||
location = / {
|
||
resolver 127.0.0.11 valid=10s ipv6=off;
|
||
set $mail_upstream ${MAIL_FRONTEND_UPSTREAM};
|
||
proxy_pass http://$mail_upstream;
|
||
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;
|
||
}
|
||
|
||
# OpenWebUI SPA routes without /ai prefix (client nav + hard reload in UltiAI iframe).
|
||
# Do not include "auth" — Ultimail OIDC uses /auth/application/o/… (Authentik).
|
||
location ~ ^/(notes|workspace|c|home|automations|calendar|channels|playground|watch|s|error)(/|$) {
|
||
return 302 /ai$uri$is_args$args;
|
||
}
|
||
|
||
location / {
|
||
default_type text/plain;
|
||
return 404 "Not found\n";
|
||
}
|
||
}
|
||
|
||
# Stalwart webadmin + JMAP (mail.${DOMAIN})
|
||
server {
|
||
listen 80;
|
||
server_name mail.${DOMAIN};
|
||
|
||
client_max_body_size 100M;
|
||
|
||
location / {
|
||
resolver 127.0.0.11 valid=10s ipv6=off;
|
||
set $stalwart_upstream stalwart:8080;
|
||
|
||
proxy_pass http://$stalwart_upstream;
|
||
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_set_header Upgrade $http_upgrade;
|
||
proxy_set_header Connection $connection_upgrade;
|
||
}
|
||
}
|