access_log /var/log/nginx/access.log combined_with_host_and_time;
error_log /var/log/nginx/error.log;

include /etc/nginx/zulip-include/headers;

# Serve a custom error page when the app is down
error_page 502 503 504 /static/webpack-bundles/5xx.html;

# Serve static files directly
location /local-static {
    alias /home/zulip/local-static;
}
location /static/ {
    alias /home/zulip/prod-static/;
    gzip_static on;
    include /etc/nginx/zulip-include/headers;
    add_header Access-Control-Allow-Origin *;
    add_header Timing-Allow-Origin *;

    # Set a nonexistent path, so we just serve the nice Django 404 page.
    error_page 404 /django_static_404.html;

    # These files are hashed and thus immutable; cache them aggressively.
    # Django adds 12 hex digits; Webpack adds 20.
    location ~ '\.[0-9a-f]{12}\.|[./][0-9a-f]{20}\.' {
        include /etc/nginx/zulip-include/immutable_headers;
    }
}

# Canonical URL for the root is /help/.
location = /help {
    return 301 /help/;
}

# Redirect to links without the trailing slash except for root /help/.
location ~ ^(/help(?:/[^/]+)+)/+$ {
    return 301 $1$is_args$args;
}

# Serve help center generated by astro/starlight.
location /help {
    alias $help_alias;

    gzip_static on;
    include /etc/nginx/zulip-include/headers;
    add_header Access-Control-Allow-Origin *;
    add_header Timing-Allow-Origin *;

    error_page 404 /404.html;
    index /index.html;
    try_files $uri $uri/index.html =404;

    # These files are hashed and thus immutable; cache them aggressively.
    # https://github.com/Pagefind/pagefind/issues/747#issuecomment-2510564644
    # https://github.com/withastro/docs/blob/53603ad048e8aedbca1aed77bac8eb00dcada79d/src/content/docs/en/guides/integrations-guide/node.mdx?plain=1#L304
    location ~ ^/help/(pagefind/.*\.(?:pf_fragment|pf_index|pf_meta)|_astro/.*)$ {
        include /etc/nginx/zulip-include/immutable_headers;
    }
}

# Send longpoll requests to Tornado
location = /json/events {
    if ($request_method = 'OPTIONS') {
        # add_header does not propagate into/out of blocks, so this
        # include cannot be factored out
        include /etc/nginx/zulip-include/headers;
        add_header Allow 'OPTIONS, GET, DELETE' always;
        return 204;
    }

    if ($request_method !~ ^(GET|DELETE)$ ) {
        # add_header does not propagate into/out of blocks, so this
        # include cannot be factored out
        include /etc/nginx/zulip-include/headers;
        add_header Allow 'OPTIONS, GET, DELETE' always;
        return 405;
    }

    proxy_pass $tornado_server;
    include /etc/nginx/zulip-include/proxy_longpolling;
}

# Send longpoll requests to Tornado
location = /api/v1/events {

    if ($request_method = 'OPTIONS') {
        include /etc/nginx/zulip-include/tornado_cors_headers;
        add_header Allow 'OPTIONS, GET, DELETE' always;
        return 204;
    }

    if ($request_method !~ ^(GET|DELETE)$ ) {
        include /etc/nginx/zulip-include/headers;
        add_header Allow 'OPTIONS, GET, DELETE' always;
        return 405;
    }

    include /etc/nginx/zulip-include/tornado_cors_headers;
    proxy_pass $tornado_server;
    include /etc/nginx/zulip-include/proxy_longpolling;
}

# Handle X-Accel-Redirect from Tornado to Tornado
location ~ ^/internal/tornado/(\d+)(/.*)$ {
    internal;
    proxy_pass http://tornado$1$2$is_args$args;
    include /etc/nginx/zulip-include/proxy_longpolling;
}

location /api/v1/tus {
    include /etc/nginx/zulip-include/api_headers;
    include /etc/nginx/zulip-include/proxy;
    # https://github.com/tus/tusd/blob/main/examples/nginx.conf
    # Disable request body size limits, and stream the request and
    # response from tusd directly.
    client_max_body_size    0;
    proxy_request_buffering off;
    proxy_buffering         off;
    proxy_pass              http://tusd;
}

# Send everything else to Django via uWSGI
location / {
    include uwsgi_params;
}

# These Django routes not under /api are shared between mobile and
# web, and thus need API headers added.  We can't easily collapse
# these blocks with the /api block, because regular expressions take
# priority over paths in nginx's order-of-operations, and we don't
# want to override the tornado configuration for /api/v1/events.
location /thumbnail {
    include /etc/nginx/zulip-include/api_headers;

    include uwsgi_params;
}
location /avatar {
    include /etc/nginx/zulip-include/api_headers;

    include uwsgi_params;
}
location /user_uploads {
    include /etc/nginx/zulip-include/api_headers;

    include uwsgi_params;
}

location /api/internal/ {
    # These only need be accessed from localhost
    allow 127.0.0.1;
    allow ::1;
    deny all;

    include /etc/nginx/zulip-include/api_headers;
    include uwsgi_params;
}

# Send all API routes not covered above to Django via uWSGI
location /api/ {
    include /etc/nginx/zulip-include/api_headers;

    include uwsgi_params;
}

include /etc/nginx/zulip-include/app.d/*.conf;
