diff --git a/deploy/varnish/default.vcl b/deploy/varnish/default.vcl new file mode 100644 index 0000000..b3d734d --- /dev/null +++ b/deploy/varnish/default.vcl @@ -0,0 +1,53 @@ +vcl 4.1; + +import vsthrottle; + +backend app { + .host = "app"; + .port = "8080"; +} + +acl purge { "localhost"; "127.0.0.1"; } + +sub vcl_recv { + # RATE LIMIT + if (!vsthrottle.is_allowed(client.ip, 10, 10s)) { + return (synth(429, "Too Many Requests")); + } + + # PURGE tylko lokalnie + if (req.method == "PURGE") { + if (!client.ip ~ purge) { return (synth(405, "Not allowed")); } + return (purge); + } + + # omijamy cache dla healthchecków / wewn. nagłówka + if (req.url == "/healthcheck" || req.http.X-Internal-Check) { return (pass); } + + # metody inne niż GET/HEAD bez cache + if (req.method != "GET" && req.method != "HEAD") { return (pass); } + + # static – agresywnie cache’ujemy + if (req.url ~ "^/static/" || req.url ~ "\.(css|js|png|jpg|svg|ico|woff2?)$") { return (hash); } + + return (hash); +} + +sub vcl_backend_response { + if (bereq.url ~ "^/static/" || bereq.url ~ "\.(css|js|png|jpg|svg|ico|woff2?)$") { + set beresp.ttl = 24h; + } else { + if (beresp.http.Cache-Control ~ "no-cache|no-store|private") { + set beresp.uncacheable = true; + set beresp.ttl = 0s; + } else { + set beresp.ttl = 60s; # domyślny TTL dla HTML/API + } + } +} + +sub vcl_deliver { + set resp.http.X-Cache = obj.hits > 0 ? "HIT" : "MISS"; + set resp.http.X-RateLimit-Limit = "10"; + set resp.http.X-RateLimit-Window = "10s"; +} diff --git a/docker-compose.yml b/docker-compose.yml index 9d7e3b6..0de8cdd 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -2,10 +2,12 @@ services: app: build: . container_name: zbiorka-app - ports: - - "${APP_PORT:-8080}:${APP_PORT}" + #ports: + # - "${APP_PORT:-8080}:${APP_PORT}" + expose: + - "${APP_PORT}" healthcheck: - test: ["CMD", "python", "-c", "import urllib.request; import sys; req = urllib.request.Request('http://localhost:${APP_PORT}/healthcheck', headers={'X-Internal-Check': '${HEALTHCHECK_TOKEN}'}); sys.exit(0) if urllib.request.urlopen(req).read() == b'OK' else sys.exit(1)"] + test: [ "CMD", "python", "-c", "import urllib.request; import sys; req = urllib.request.Request('http://localhost:${APP_PORT}/healthcheck', headers={'X-Internal-Check': '${HEALTHCHECK_TOKEN}'}); sys.exit(0) if urllib.request.urlopen(req).read() == b'OK' else sys.exit(1)" ] interval: 30s timeout: 10s retries: 3 @@ -14,4 +16,25 @@ services: - .env volumes: - ./instance:/app/instance - restart: unless-stopped \ No newline at end of file + restart: unless-stopped + + varnish: + image: varnish:fresh + container_name: zbiorka-varnish + depends_on: + app: + condition: service_healthy + ports: + - "${APP_PORT:-8080}:80" + volumes: + - ./deploy/varnish/default.vcl:/etc/varnish/default.vcl:ro + environment: + - VARNISH_SIZE=256m + healthcheck: + test: [ "CMD-SHELL", "curl -fsS -H 'X-Internal-Check=${HEALTHCHECK_TOKEN}' http://localhost/healthcheck | grep -q OK" ] + interval: 30s + timeout: 5s + retries: 3 + env_file: + - .env + restart: unless-stopped