From aa0a383b9cbb0222eea5293b76e1d3f68d1ca1dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A5l-Kristian=20Hamre?= Date: Thu, 31 Jul 2025 15:13:19 +0200 Subject: [PATCH] Added docker compose dev env, improved varnish VCL, and added favicons. --- config/nginx/dev/localhost.conf | 17 +++++ config/varnish/default.vcl | 97 ++++++++++++++++--------- config/varnish/dev.vcl | 105 ++++++++++++++++++++++++++++ docker-compose-dev.yml | 31 ++++++++ static/apple-touch-icon.png | Bin 0 -> 3181 bytes static/favicon-96x96.png | Bin 0 -> 1667 bytes static/favicon.ico | Bin 0 -> 15086 bytes static/favicon.svg | 3 + static/site.webmanifest | 21 ++++++ static/web-app-manifest-192x192.png | Bin 0 -> 3496 bytes static/web-app-manifest-512x512.png | Bin 0 -> 15969 bytes templates/dashboard_base.html | 7 ++ 12 files changed, 247 insertions(+), 34 deletions(-) create mode 100644 config/nginx/dev/localhost.conf create mode 100644 config/varnish/dev.vcl create mode 100644 docker-compose-dev.yml create mode 100644 static/apple-touch-icon.png create mode 100644 static/favicon-96x96.png create mode 100644 static/favicon.ico create mode 100644 static/favicon.svg create mode 100644 static/site.webmanifest create mode 100644 static/web-app-manifest-192x192.png create mode 100644 static/web-app-manifest-512x512.png diff --git a/config/nginx/dev/localhost.conf b/config/nginx/dev/localhost.conf new file mode 100644 index 0000000..7a32915 --- /dev/null +++ b/config/nginx/dev/localhost.conf @@ -0,0 +1,17 @@ +server { + listen 80 default_server; + listen [::]:80 default_server; + + server_name _; + server_tokens off; + + location / { + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header Host $http_host; + + proxy_pass http://varnish:80; + proxy_redirect off; + } +} \ No newline at end of file diff --git a/config/varnish/default.vcl b/config/varnish/default.vcl index 3b077ca..68924e3 100644 --- a/config/varnish/default.vcl +++ b/config/varnish/default.vcl @@ -9,52 +9,53 @@ backend default { .port = "5000"; } -acl purge { - "localhost"; - "nginx"; - "127.0.0.1"; -} - sub vcl_recv { - set req.http.Host = regsub(req.http.Host, ":[0-9]+", ""); - unset req.http.proxy; - set req.url = std.querysort(req.url); - set req.url = regsub(req.url, "\?$", ""); - set req.http.Surrogate-Capability = "key=ESI/1.0"; - - if (req.url ~ "/purge-cache") { - if (!client.ip ~ purge) { - return(synth(403, "Not allowed.")); - } - ban("req.http.host == rstat.net"); - return(synth(200, "Cache cleared")); + if (req.method != "GET" && + req.method != "HEAD" && + req.method != "PUT" && + req.method != "POST" && + req.method != "TRACE" && + req.method != "OPTIONS" && + req.method != "DELETE") { + /* Non-RFC2616 or CONNECT which is weird. */ + return (pipe); } - if (!req.http.X-Forwarded-Proto) { - if(std.port(server.ip) == 443 || std.port(server.ip) == 8443) { - set req.http.X-Forwarded-Proto = "https"; - } else { - set req.http.X-Forwarded-Proto = "https"; - } + # We only deal with GET and HEAD by default + if (req.method != "GET" && req.method != "HEAD") { + return (pass); } - - # Cache static files - if (req.url ~ "^[^?]*\.(7z|avi|bmp|bz2|css|csv|doc|docx|eot|flac|flv|gif|gz|ico|jpeg|jpg|js|less|mka|mkv|mov|mp3|mp4|mpeg|mpg|odt|ogg|ogm|opus|otf|pdf|png|ppt|pptx|rar|rtf|svg|svgz|swf|tar|tbz|tgz|ttf|txt|txz|wav|webm|webp|woff|woff2|xls|xlsx|xml|xz|zip)(\?.*)?$") { + + set req.url = regsub(req.url, "^http[s]?://", ""); + + # static files are always cacheable. remove SSL flag and cookie + if (req.url ~ "^/(pub/)?(media|static)/.*\.(ico|jpg|jpeg|png|gif|tiff|bmp|mp3|ogg|svg|swf|woff|woff2|eot|ttf|otf)$") { + unset req.http.Https; + unset req.http.X-Forwarded-Proto; unset req.http.Cookie; - return(hash); + unset req.http.css; + unset req.http.js; } - - # Remove all cookies for other requests - unset req.http.Cookie; - return(hash); + + return (hash); } sub vcl_hash { + if (req.http.host) { + hash_data(req.http.host); + } else { + hash_data(server.ip); + } + + # To make sure http users don't see ssl warning + if (req.http.X-Forwarded-Proto) { hash_data(req.http.X-Forwarded-Proto); + } } sub vcl_backend_response { - # Cache static files and other content in Varnish for 1 min + set beresp.http.X-Host = bereq.http.host; + set beresp.ttl = 1m; # Enable stale content serving set beresp.grace = 24h; @@ -62,6 +63,30 @@ sub vcl_backend_response { if (beresp.http.Cache-Control) { set beresp.http.X-Orig-Cache-Control = beresp.http.Cache-Control; } + + # validate if we need to cache it and prevent from setting cookie + # images, css and js are cacheable by default so we have to remove cookie also + if (beresp.ttl > 0s && (bereq.method == "GET" || bereq.method == "HEAD")) { + unset beresp.http.set-cookie; + unset beresp.http.set-css; + unset beresp.http.set-js; + if (bereq.url !~ "\.(ico|jpg|jpeg|png|gif|tiff|bmp|gz|tgz|bz2|tbz|mp3|ogg|svg|swf|woff|woff2|eot|ttf|otf)(\?|$)") { + set beresp.http.Pragma = "no-cache"; + set beresp.http.Expires = "-1"; + set beresp.http.Cache-Control = "no-store, no-cache, must-revalidate, max-age=0"; + set beresp.grace = 1m; + } + } + + # If page is not cacheable then bypass varnish for 2 minutes as Hit-For-Pass + if (beresp.ttl <= 0s || + beresp.http.Surrogate-control ~ "no-store" || + (!beresp.http.Surrogate-Control && beresp.http.Vary == "*")) { + # Mark as Hit-For-Pass for the next 2 minutes + set beresp.ttl = 120s; + set beresp.uncacheable = true; + } + return (deliver); } sub vcl_deliver { @@ -73,4 +98,8 @@ sub vcl_deliver { # If no Cache-Control was set by the origin, we'll set a default set resp.http.Cache-Control = "no-cache, must-revalidate"; } -} + + unset resp.http.Server; + unset resp.http.Via; + unset resp.http.Link; +} \ No newline at end of file diff --git a/config/varnish/dev.vcl b/config/varnish/dev.vcl new file mode 100644 index 0000000..68924e3 --- /dev/null +++ b/config/varnish/dev.vcl @@ -0,0 +1,105 @@ +vcl 4.1; + +# https://github.com/varnish/toolbox/tree/master/vcls/hit-miss +include "hit-miss.vcl"; +import std; + +backend default { + .host = "rstat-dashboard"; + .port = "5000"; +} + +sub vcl_recv { + if (req.method != "GET" && + req.method != "HEAD" && + req.method != "PUT" && + req.method != "POST" && + req.method != "TRACE" && + req.method != "OPTIONS" && + req.method != "DELETE") { + /* Non-RFC2616 or CONNECT which is weird. */ + return (pipe); + } + + # We only deal with GET and HEAD by default + if (req.method != "GET" && req.method != "HEAD") { + return (pass); + } + + set req.url = regsub(req.url, "^http[s]?://", ""); + + # static files are always cacheable. remove SSL flag and cookie + if (req.url ~ "^/(pub/)?(media|static)/.*\.(ico|jpg|jpeg|png|gif|tiff|bmp|mp3|ogg|svg|swf|woff|woff2|eot|ttf|otf)$") { + unset req.http.Https; + unset req.http.X-Forwarded-Proto; + unset req.http.Cookie; + unset req.http.css; + unset req.http.js; + } + + return (hash); +} + +sub vcl_hash { + if (req.http.host) { + hash_data(req.http.host); + } else { + hash_data(server.ip); + } + + # To make sure http users don't see ssl warning + if (req.http.X-Forwarded-Proto) { + hash_data(req.http.X-Forwarded-Proto); + } +} + +sub vcl_backend_response { + set beresp.http.X-Host = bereq.http.host; + + set beresp.ttl = 1m; + # Enable stale content serving + set beresp.grace = 24h; + # Preserve the origin's Cache-Control header for client-side caching + if (beresp.http.Cache-Control) { + set beresp.http.X-Orig-Cache-Control = beresp.http.Cache-Control; + } + + # validate if we need to cache it and prevent from setting cookie + # images, css and js are cacheable by default so we have to remove cookie also + if (beresp.ttl > 0s && (bereq.method == "GET" || bereq.method == "HEAD")) { + unset beresp.http.set-cookie; + unset beresp.http.set-css; + unset beresp.http.set-js; + if (bereq.url !~ "\.(ico|jpg|jpeg|png|gif|tiff|bmp|gz|tgz|bz2|tbz|mp3|ogg|svg|swf|woff|woff2|eot|ttf|otf)(\?|$)") { + set beresp.http.Pragma = "no-cache"; + set beresp.http.Expires = "-1"; + set beresp.http.Cache-Control = "no-store, no-cache, must-revalidate, max-age=0"; + set beresp.grace = 1m; + } + } + + # If page is not cacheable then bypass varnish for 2 minutes as Hit-For-Pass + if (beresp.ttl <= 0s || + beresp.http.Surrogate-control ~ "no-store" || + (!beresp.http.Surrogate-Control && beresp.http.Vary == "*")) { + # Mark as Hit-For-Pass for the next 2 minutes + set beresp.ttl = 120s; + set beresp.uncacheable = true; + } + return (deliver); +} + +sub vcl_deliver { + # Restore the origin's Cache-Control header for the browser + if (resp.http.X-Orig-Cache-Control) { + set resp.http.Cache-Control = resp.http.X-Orig-Cache-Control; + unset resp.http.X-Orig-Cache-Control; + } else { + # If no Cache-Control was set by the origin, we'll set a default + set resp.http.Cache-Control = "no-cache, must-revalidate"; + } + + unset resp.http.Server; + unset resp.http.Via; + unset resp.http.Link; +} \ No newline at end of file diff --git a/docker-compose-dev.yml b/docker-compose-dev.yml new file mode 100644 index 0000000..87baf0e --- /dev/null +++ b/docker-compose-dev.yml @@ -0,0 +1,31 @@ +name: rstat + +services: + + rstat-dashboard: + build: + context: . + dockerfile: Dockerfile + restart: always + volumes: + - ./reddit_stocks.db:/usr/src/app/reddit_stocks.db:ro + ports: + - "5000:5000" + + nginx: + image: nginx:1.29.0 + restart: always + volumes: + - ./config/nginx/dev:/etc/nginx/conf.d:ro + - ./public:/usr/share/nginx:ro + ports: + - "80:80" + + varnish: + image: varnish:7.7.1 + restart: always + volumes: + - ./config/varnish/dev.vcl:/etc/varnish/default.vcl:ro" + - ./config/varnish/hit-miss.vcl:/etc/varnish/hit-miss.vcl:ro" + tmpfs: + - /var/lib/varnish/varnishd:exec diff --git a/static/apple-touch-icon.png b/static/apple-touch-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..6871469f5b61ec84d39d240a7a6f3e377c67c2a0 GIT binary patch literal 3181 zcmXw+dpy(oAIHCQo7C($L^&>9TtYbIR&yJ(V+g|#vUBKS!`yGfYU9kxHO>!37+M*o z)DS{-#E~`&rRJ7$8FI@dCj8d#^2g`%em_2s_viC|zF&{er9`tON9wf5fTG>)ZGk$j$}9jJ&~~u1!6aZ8N;<+-kP6*FhK_e7d}M6V z%Lol;cOLpKCC$!QYTqtM=A6CK{ToQChfjH=K%gSCOF1HEcA}i3B?&=}Orj+ur6qMz zQ1p?Crgq3oFLC_!*z2d?u5{@6+=zbs*eKj;Yb<_@%k`24A48)hs#An1bw|Wx;o>P3 z#{hbUvP+B`_)pgQBU@@BfWCg_vC_A z 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 O1K@ykv8%KFBjw+4Z08OD literal 0 HcmV?d00001 diff --git a/static/favicon-96x96.png b/static/favicon-96x96.png new file mode 100644 index 0000000000000000000000000000000000000000..658c9faeb5f2fcb512ae50b984e74315185358d5 GIT binary patch literal 1667 zcmV-}27LL6P)Px*JxN4CRCr$PoiT46MG(hlUqqw?SqcJD01-Upt42dAdxSSB2Gt#)5J?SWtpa?=lvPh&L?$F%5dphr%eRq2^d$+f5uX$0t z?{05?_n)1eoxP_Z9-Uf1b$a!9afkpqeh91pEZ_U?TLtM#$9+C|c?CoK4+Z&WlB(40mte-1n-#T4$^Pe0hI!D^0;K0M*vb0V&u|{!4%M4azJ4pb!m5Kz`_2A+rjg z3}u*t+J+=S7`gju@n8Y?;N7=j;ljBVyGvi{_|+RYe*N$GkH@d_fB*5jaL7eKkBG)Zsl-oCjl6(L0cQ!HtKAQ~M-02Pli1wkp; z{^{204G9%Q05zDR_AMceQJ}=0T4H4bx(6= zPQ%>Ux3cfJ%uS&JPzmKjLIs@z`2OKzD}z@xMi>Y}8V0HW0h~ziDcIk+wYpT*r)hr= z4nX;4FJF2eE-#$t*yqG|2^Fy2P|yI-dZ>u2pUscm6o&>i7I58=cK~p<2}y!WhjosN zS);+=;X?jv&-P?Eyfp5J6XDa6|J=*A0RMfCe8ycv4mZMn+dZA2X?RH>L)9^XNDI)+21?YPkB*M@*6Se?w z<=!TyBgXj$?nKP6G8S_|6cZ{HfgxzIglWNX+>`>av$ksG%FEZa`l2pp34ban=kkqF z$vBDrO3neWzd-`XjDJdPl~WLSv)rd2&p|vejU%Ao0Avd?gTZw@1By2)G5AXRe=h;x zw0^wRw5F3*DqVy$8)(U7zeMbhUC}3C5UV1p2}v!=8=iia`|(l%)h4aqLc;A7SDSeH z0a$Cn#d*COgzKA^Zi!F)!bj&|;lewtaI<`Nsz8^s2;)^4icc2jK!eYn8I>l!msi9| z9#{!S^^p_%dj&w!SkmXhb>9cSKAG6E#90H@``Eb)k~W&u`N&#O0N|{CQGJU52&oWM(*l*=_`2u)tKq=P!8@ z+l*&|FtZA%*qP(}9TDElgr&kp+w%~qh3pj4VP;joW>x`InRy#7Cq_Xf3s4ERHr^5B zRVo4p!Y0Su+0*PQXCGA1xeIE#O%;=HSmD4;z7f0NoyGiKrXrdGz?+d+Wbax9y;T4L z2tft@0Z_1CyoQ*PtiBc1l?q(XfW4Uq*wzK{~sG#=%Iv5Ms713U;2XI|b-&GKV0$i$qD%6syjP7kJ zf|}a9iiq%9uJ#551)#4g2*w4u91C2kASeKBt3aZRB{%?m8w-L1&^HwX37~BiaEiHt z1mIEy(q$~c0_ckhf(6hQ6$A~yr3$1<8iNMV2NkdYNc>KViyy`*=ZZ`!;1>1bHSm{M zwV%YbF6?CiuqY|2-PA=jn!ajQyE6?M+U~HK2>_G|8rZhhS0Vs|nc7l;Qvjp45U$jE z6dE>iwIRCnTLKMxR_|-JzejK7g6ef4wz9E61Ylz=2W=w&2S&9y<^L}}oyy`{#U20v N002ovPDHLkV1ic})tUeR literal 0 HcmV?d00001 diff --git a/static/favicon.ico b/static/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..de36b00dc2b787f1d6dcb242e221bdd7a6a0e9cd GIT binary patch literal 15086 zcmd^`&x;*J5XU=gaF59!AlbPH9&_@#@0dhAh+z>VVMGKG1Wz6W+0}p{OJK9>Ge0(o zQ4_@wL_HW04=&{3LBX3I1ouG>Ie19GLj;9qefmx9_BK7;)AQ!NU3HS8X1crT`>mSl zo}S5zG5gGnIegfNF3hKo8S{iOrYNfGgU0+OZ3jiu|MGrg-jM;1%NQ{+r3mf+r(@;x zkwT=*k(~U9-(HSy#^(Azcjo9YM+Bp2g2A|R%SXS<@y%GV>oxJu7{+4r-n$FK%U52E zit@|Y|8IZ(au^fGe?jcA!_EDzZX{>pVppo@9y7JiadVFkKbLK;+wj+YkgM50ruI2q*1g7>!#}paZjyW5 zBXi#re_WlzKb7BD|0Hrx?9<}!)>>D!KTZD3-48x_P1k*NtNo^5Z_~=Z))xC^od?_# z@O@4>*VE>&acn)q-O=216y;oys z#h-6FA(2{e<08MQ`>-{qm)q@y+VB^fLgNo<)<=ya^;~n_f0@fszagwi@CQfy+C3d{ zr0#*PN7#fo#@Z&uA57tH>xnv##h^9R=D9Xe{!-KAKUG5?fBxps)?14?Fb)3ji{+pA z_9p+)x93#;xB2IN`=mxEm4ESDX#J<21>5zrr}#I1r&RuXVg8c$ob{i1q5Ftw@ju79 z5|o|)*3#tpJMJE$P5s-oLe|AP&GRU==IDB~HFsO?Lv#4UsZQX5q~@yfMw5H39pO>3 zp5z`m{9WuW&UN0hPUZ5}@w>%exvwridADuuiDBCOsTt+Y{Asi1Xzr)U-{;*0_et}I zGjm$!sIy*m^6kOpk8g0s71mDO&pUi|t=$~AUfkO8`3LY6x2@jfJrmB%GatLFYvzB4 z(u1o=(G|b{il3o9jQP{d%U|@|12eZn52|+68)nh=t6nlgd#&2+8(mlWkeRX9hs>QS7gif=nEjbkX||E5{A=AGYZ6(T*6f6MQ*m`ka3B{tWj&%TGmd?f5GKHnJpy`O$} zsI#XXT-J2omTT7OwX;v%liN{yh!2Y&+E?n{H-caGSD$^zbIjlG(Pnd!_~y_SUt=dn zJ~r31r(!25W8jY+dt3ajVjlxP8M|;S@t-uNL^7#SVuY{8a4g z>*d3o*qh?>4x^Hq@4~UfZ_^_dKWmb|uCXSC<~y;&u_>;Oi{XcGLT&IIt#$3ckA>FX z5LR8Aa^U_Glg5|QUBs`PSo8H=rYVOQJ~6S5`$A2`@&$*c^B{(=&p)kw)%vXG`Kqz| znla+L94G&ZFMbN&4{cx&tB=jKy$^|JFFu>ZAHuI}OE`p{|H|Fx?O%_Bk1y~;Yl7m} zaaSJW;Nu(oP%Y|woG^Z`F8_+Jan|X!@e>#Gp|1XzqfO>u6a2NaPk-+UxWwIGJ^ojy z_NafJ2fW+Y#jWizd~)coJ$fCgIh}`^d+}W=PMwb7tG^sgKFAB-@9Qw#_Ac=ioBQKp z#Lm0G2bX;xH32q0W0-EA;&Wg9y++-&)_|=s!E|s-*LRb1#m7ezVNTilx4(}#c1`R{ zqzTqu8N>I2!7P6^4vb`JZZM0Oc`J{Y#R0P43 \ No newline at end of file diff --git a/static/site.webmanifest b/static/site.webmanifest new file mode 100644 index 0000000..ccf313a --- /dev/null +++ b/static/site.webmanifest @@ -0,0 +1,21 @@ +{ + "name": "MyWebSite", + "short_name": "MySite", + "icons": [ + { + "src": "/web-app-manifest-192x192.png", + "sizes": "192x192", + "type": "image/png", + "purpose": "maskable" + }, + { + "src": "/web-app-manifest-512x512.png", + "sizes": "512x512", + "type": "image/png", + "purpose": "maskable" + } + ], + "theme_color": "#ffffff", + "background_color": "#ffffff", + "display": "standalone" +} \ No newline at end of file diff --git a/static/web-app-manifest-192x192.png b/static/web-app-manifest-192x192.png new file mode 100644 index 0000000000000000000000000000000000000000..420c5c35fed19af2cb188c02d3e285cb23dd5bcf GIT binary patch literal 3496 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 literal 0 HcmV?d00001 diff --git a/static/web-app-manifest-512x512.png b/static/web-app-manifest-512x512.png new file mode 100644 index 0000000000000000000000000000000000000000..9748561ccb51f2bc5e9ddbd9f8f9d667841ef3e2 GIT binary patch literal 15969 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 OS(@3H-Zyp%{Qm&Bg5_KQ literal 0 HcmV?d00001 diff --git a/templates/dashboard_base.html b/templates/dashboard_base.html index acb0cb4..ed3bad1 100644 --- a/templates/dashboard_base.html +++ b/templates/dashboard_base.html @@ -5,6 +5,13 @@ {% block title %}RSTAT Dashboard{% endblock %} + + + + + + +