# nginx config for drive-frontend. # # /media/ requests are validated via auth_request to the Drive backend before # being proxied to SeaweedFS. This avoids exposing S3 credentials to the browser # while still serving private files through the CDN path. apiVersion: v1 kind: ConfigMap metadata: name: drive-frontend-nginx-conf namespace: lasuite data: default.conf: | server { listen 8080; server_name localhost; server_tokens off; root /usr/share/nginx/html; # Rewrite hardcoded upstream La Gaufre widget and services API URLs to our # integration service. sub_filter doesn't work on gzip-compressed responses. gzip off; sub_filter_once off; sub_filter_types text/html application/javascript; sub_filter 'https://static.suite.anct.gouv.fr/widgets/lagaufre.js' 'https://integration.DOMAIN_SUFFIX/api/v2/lagaufre.js'; sub_filter 'https://lasuite.numerique.gouv.fr/api/services' 'https://integration.DOMAIN_SUFFIX/api/v2/services.json'; sub_filter 'https://operateurs.suite.anct.gouv.fr/api/v1.0/lagaufre/services/?operator=9f5624fc-ef99-4d10-ae3f-403a81eb16ef&siret=21870030000013' 'https://integration.DOMAIN_SUFFIX/api/v2/services.json'; sub_filter '' ''; # Public file viewer — Next.js static export generates a literal [id].html # template for this dynamic route. Serve it for any file UUID so the # client-side router hydrates the correct FilePage component without auth. location ~ ^/explorer/items/files/[^/]+$ { try_files $uri $uri.html /explorer/items/files/[id].html; } # Item detail routes (folders, workspaces, shared items). location ~ ^/explorer/items/[^/]+$ { try_files $uri $uri.html /explorer/items/[id].html; } location / { # Try the exact path, then path + .html (Next.js static export generates # e.g. explorer/items/my-files.html), then fall back to index.html for # client-side routes that have no pre-rendered file. try_files $uri $uri.html /index.html; } # Protected media: auth via Drive backend, then proxy to S3 with signed headers. # media-auth returns SigV4 Authorization/X-Amz-Date/X-Amz-Content-SHA256 # headers signed for the S3 key (item/UUID/file). nginx captures them and # forwards to SeaweedFS. The regex strips /media/ and optional /preview/ # so the proxy path matches the signed S3 key exactly. location ~ ^/media/(preview/)?(.*) { set $original_uri $request_uri; set $s3_key $2; resolver kube-dns.kube-system.svc.cluster.local valid=30s; set $s3_backend http://seaweedfs-filer.storage.svc.cluster.local:8333; auth_request /internal/media-auth; auth_request_set $auth_header $upstream_http_authorization; auth_request_set $amz_date $upstream_http_x_amz_date; auth_request_set $amz_content $upstream_http_x_amz_content_sha256; proxy_set_header Authorization $auth_header; proxy_set_header X-Amz-Date $amz_date; proxy_set_header X-Amz-Content-Sha256 $amz_content; proxy_pass $s3_backend/sunbeam-drive/$s3_key; } # Internal subrequest: Django checks session and item access, returns S3 auth headers. location = /internal/media-auth { internal; proxy_pass http://drive-backend.lasuite.svc.cluster.local:80/api/v1.0/items/media-auth/; proxy_pass_request_body off; proxy_set_header Content-Length ""; proxy_set_header Cookie $http_cookie; proxy_set_header Host drive.DOMAIN_SUFFIX; proxy_set_header X-Original-URL https://drive.DOMAIN_SUFFIX$original_uri; } error_page 500 502 503 504 @blank_error; location @blank_error { return 200 ''; add_header Content-Type text/html; } }