Backend: GET /api/random/ (aliases /random and /random/) returns a random library image with: - rating=s,q,e filtering (comma separated, default any); - fastfetch mode (?fastfetch=1 or any User-Agent containing "fastfetch") that only considers png/jpg/gif - what terminal viewers can show; - JSON with j_id, filename, extension, rating, size, e621 id plus absolute url/download_url/thumbnail_url. Authenticated callers get signed URLs so fastfetch and image viewers can load them without headers; guests get unsigned URLs and never receive hidden_from_guests items. Tests: apps/library/tests/test_random.py (8 tests) covering the response contract, guest signatures, image-only default, the fastfetch format restriction (flag and User-Agent), rating filters, guest visibility and the short alias. Frontend: /random page with rating pills, R to roll, Open/Download and a library link, plus navigation and command palette entries; needs a backend, hidden in local mode. nginx: /random negotiates on Accept so browsers keep getting the SPA while scripts get the JSON (verified with the proxy and frontend containers). Also fixes a regression from the SSRF change: the guest download proxy still referenced the removed 'parsed' variable on its success path, so every proxied download would have 500'd. Redirect hops are now covered by tests with a mocked requests.get.
100 lines
3.3 KiB
Plaintext
100 lines
3.3 KiB
Plaintext
# J621 public entry point (the "nginx" service in the compose files).
|
|
#
|
|
# Routes:
|
|
# /api, /admin, /static, /health -> backend:8000 (Django / gunicorn)
|
|
# everything else -> frontend:80 (SPA static files)
|
|
#
|
|
# Both upstreams are variables so the same file works in all three compose
|
|
# variants: with no frontend on the network "/" answers a small JSON hint,
|
|
# with no backend the API paths answer 502 until one is configured via /setup.
|
|
# Nothing is published to the host; the Tailscale sidecar shares this
|
|
# container's network namespace and funnels to 127.0.0.1:80.
|
|
|
|
resolver 127.0.0.11 valid=10s ipv6=off;
|
|
|
|
map $http_x_forwarded_proto $j621_forwarded_proto {
|
|
default $http_x_forwarded_proto;
|
|
"" $scheme;
|
|
}
|
|
|
|
# /random is both the SPA route and the shell-greeting shortcut: browsers
|
|
# (Accept: text/html) get the SPA, scripts (curl/wget/fetch) get the API JSON.
|
|
map $http_accept $j621_random_target {
|
|
default @j621_random_api;
|
|
~*text/html @j621_random_spa;
|
|
}
|
|
|
|
server {
|
|
listen 80;
|
|
server_name _;
|
|
|
|
location = /nginx-health {
|
|
access_log off;
|
|
return 200 "ok\n";
|
|
}
|
|
|
|
location ~ ^/random/?$ {
|
|
error_page 418 = $j621_random_target;
|
|
return 418;
|
|
}
|
|
|
|
location @j621_random_api {
|
|
set $j621_backend http://backend:8000;
|
|
proxy_pass $j621_backend$request_uri;
|
|
|
|
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-Host $host;
|
|
proxy_set_header X-Forwarded-Proto $j621_forwarded_proto;
|
|
}
|
|
|
|
location @j621_random_spa {
|
|
set $j621_frontend http://frontend:80;
|
|
proxy_pass $j621_frontend$request_uri;
|
|
|
|
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-Host $host;
|
|
proxy_set_header X-Forwarded-Proto $j621_forwarded_proto;
|
|
}
|
|
|
|
location ~ ^/(api|admin|static|health)(/|$) {
|
|
set $j621_backend http://backend:8000;
|
|
proxy_pass $j621_backend$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-Host $host;
|
|
proxy_set_header X-Forwarded-Proto $j621_forwarded_proto;
|
|
|
|
# Uploads and client-processed files can be large; stream them.
|
|
client_max_body_size 2048m;
|
|
proxy_request_buffering off;
|
|
proxy_read_timeout 600s;
|
|
proxy_send_timeout 600s;
|
|
}
|
|
|
|
location / {
|
|
set $j621_frontend http://frontend:80;
|
|
proxy_pass $j621_frontend$request_uri;
|
|
|
|
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-Host $host;
|
|
proxy_set_header X-Forwarded-Proto $j621_forwarded_proto;
|
|
|
|
# Backend-only deployments have no frontend: say so instead of 502.
|
|
error_page 502 503 504 = @j621_no_frontend;
|
|
}
|
|
|
|
location @j621_no_frontend {
|
|
default_type application/json;
|
|
return 200 '{"detail":"J621 API - no frontend is attached to this deployment."}';
|
|
}
|
|
}
|