From 8f0a4e8333ca21e999e3b1e65b9be0017e31fb99 Mon Sep 17 00:00:00 2001 From: Andrey Antukh Date: Thu, 22 Jun 2023 15:14:16 +0200 Subject: [PATCH] :tada: Add local caching of gfonts styles --- CHANGES.md | 4 +++ docker/devenv/files/nginx.conf | 52 ++++++++++++++++++++++++++- docker/images/files/nginx.conf | 52 +++++++++++++++++++++++++++ frontend/src/app/main/fonts.cljs | 62 ++++++++++++++------------------ 4 files changed, 133 insertions(+), 37 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 29ac5024f..f923a9262 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -6,6 +6,10 @@ ### :sparkles: New features +- Add support for local caching of google fonts (this avoids exposing + the final user IP to goolge and reduces the amount of request sent + to google) + ### :bug: Bugs fixed ### :arrow_up: Deps updates diff --git a/docker/devenv/files/nginx.conf b/docker/devenv/files/nginx.conf index 0086018b3..169706e23 100644 --- a/docker/devenv/files/nginx.conf +++ b/docker/devenv/files/nginx.conf @@ -40,7 +40,10 @@ http { '' close; } - # include /etc/nginx/sites-enabled/*; + proxy_cache_path /tmp/cache/ levels=2:2 keys_zone=penpot:20m; + proxy_cache_methods GET HEAD; + proxy_cache_valid any 48h; + proxy_cache_key "$host$request_uri"; server { listen 3449 default_server; @@ -99,6 +102,53 @@ http { proxy_buffering off; } + location /internal/gfonts/css { + proxy_pass https://fonts.googleapis.com/css?$args; + proxy_hide_header Access-Control-Allow-Origin; + proxy_hide_header Cross-Origin-Resource-Policy; + proxy_hide_header Link; + proxy_hide_header Alt-Svc; + proxy_hide_header Cache-Control; + proxy_hide_header Expires; + + proxy_ignore_headers Set-Cookie Vary Cache-Control Expires; + + proxy_set_header User-Agent "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Safari/537.36"; + proxy_set_header Host "fonts.googleapis.com"; + proxy_set_header Accept "*/*"; + + proxy_cache penpot; + + add_header Access-Control-Allow-Origin $http_origin; + add_header Cache-Control max-age=86400; + add_header X-Cache-Status $upstream_cache_status; + } + + location ~ ^/internal/gfonts/font/(?.+) { + proxy_pass https://fonts.gstatic.com/s/$font_file; + + proxy_hide_header Access-Control-Allow-Origin; + proxy_hide_header Cross-Origin-Resource-Policy; + proxy_hide_header Link; + proxy_hide_header Alt-Svc; + proxy_hide_header Cache-Control; + proxy_hide_header Expires; + proxy_hide_header Cross-Origin-Opener-Policy; + proxy_hide_header Report-To; + + proxy_ignore_headers Set-Cookie Vary Cache-Control Expires; + + proxy_set_header User-Agent "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Safari/537.36"; + proxy_set_header Host "fonts.gstatic.com"; + proxy_set_header Accept "*/*"; + + proxy_cache penpot; + + add_header Access-Control-Allow-Origin $http_origin; + add_header Cache-Control max-age=86400; + add_header X-Cache-Status $upstream_cache_status; + } + location /internal/assets { internal; alias /home/penpot/penpot/backend/assets; diff --git a/docker/images/files/nginx.conf b/docker/images/files/nginx.conf index 35b06e731..b79a07022 100644 --- a/docker/images/files/nginx.conf +++ b/docker/images/files/nginx.conf @@ -45,6 +45,11 @@ http { '' close; } + proxy_cache_path /tmp/cache/ levels=2:2 keys_zone=penpot:20m; + proxy_cache_methods GET HEAD; + proxy_cache_valid any 48h; + proxy_cache_key "$host$request_uri"; + server { listen 80 default_server; server_name _; @@ -88,6 +93,53 @@ http { error_page 301 302 307 = @handle_redirect; } + location /internal/gfonts/css { + proxy_pass https://fonts.googleapis.com/css?$args; + proxy_hide_header Access-Control-Allow-Origin; + proxy_hide_header Cross-Origin-Resource-Policy; + proxy_hide_header Link; + proxy_hide_header Alt-Svc; + proxy_hide_header Cache-Control; + proxy_hide_header Expires; + + proxy_ignore_headers Set-Cookie Vary Cache-Control Expires; + + proxy_set_header User-Agent "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Safari/537.36"; + proxy_set_header Host "fonts.googleapis.com"; + proxy_set_header Accept "*/*"; + + proxy_cache penpot; + + add_header Access-Control-Allow-Origin $http_origin; + add_header Cache-Control max-age=86400; + add_header X-Cache-Status $upstream_cache_status; + } + + location ~ ^/internal/gfonts/font/(?.+) { + proxy_pass https://fonts.gstatic.com/s/$font_file; + + proxy_hide_header Access-Control-Allow-Origin; + proxy_hide_header Cross-Origin-Resource-Policy; + proxy_hide_header Link; + proxy_hide_header Alt-Svc; + proxy_hide_header Cache-Control; + proxy_hide_header Expires; + proxy_hide_header Cross-Origin-Opener-Policy; + proxy_hide_header Report-To; + + proxy_ignore_headers Set-Cookie Vary Cache-Control Expires; + + proxy_set_header User-Agent "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Safari/537.36"; + proxy_set_header Host "fonts.gstatic.com"; + proxy_set_header Accept "*/*"; + + proxy_cache penpot; + + add_header Access-Control-Allow-Origin $http_origin; + add_header Cache-Control max-age=86400; + add_header X-Cache-Status $upstream_cache_status; + } + location /internal/assets { internal; alias /opt/data/assets; diff --git a/frontend/src/app/main/fonts.cljs b/frontend/src/app/main/fonts.cljs index b04190463..0094c6be4 100644 --- a/frontend/src/app/main/fonts.cljs +++ b/frontend/src/app/main/fonts.cljs @@ -14,12 +14,12 @@ [app.common.text :as txt] [app.config :as cf] [app.util.dom :as dom] + [app.util.globals :as globals] [app.util.http :as http] [app.util.object :as obj] [beicon.core :as rx] [clojure.set :as set] [cuerdas.core :as str] - [goog.events :as gev] [lambdaisland.uri :as u] [okulary.core :as l] [promesa.core :as p])) @@ -91,37 +91,14 @@ ;; only know if the font is needed or not (defonce ^:dynamic loaded-hints (l/atom #{})) -(defn- create-link-element - [uri] - (let [node (.createElement js/document "link")] - (unchecked-set node "href" uri) - (unchecked-set node "rel" "stylesheet") - (unchecked-set node "type" "text/css") - node)) - -(defn- create-style-element - [css] - (let [node (.createElement js/document "style")] - (unchecked-set node "innerHTML" css) - node)) - -(defn- load-font-css! - "Creates a link element and attaches it to the dom for correctly - load external css resource." - [url on-loaded] - (let [node (create-link-element url) - head (.-head ^js js/document)] - (gev/listenOnce node "load" (fn [_] - (when (fn? on-loaded) - (on-loaded)))) - (dom/append-child! head node))) - (defn- add-font-css! "Creates a style element and attaches it to the dom." - [css] - (let [head (.-head ^js js/document)] - (->> (create-style-element css) - (dom/append-child! head)))) + [id css] + (let [node (dom/create-element "style")] + (dom/set-attribute! node "id" id) + (dom/set-html! node css) + (when-let [head (unchecked-get globals/document "head")] + (dom/append-child! head node)))) ;; --- LOADER: BUILTIN @@ -139,18 +116,31 @@ ;; --- LOADER: GOOGLE -(defn generate-gfonts-url +(defn- generate-gfonts-url [{:keys [family variants]}] - (let [base (str "https://fonts.googleapis.com/css?family=" family) - variants (str/join "," (map :id variants))] - (str base ":" variants "&display=block"))) + (let [query (dm/str "family=" family ":" + (str/join "," (map :id variants)) + "&display=block")] + (dm/str + (-> cf/public-uri + (assoc :path "/internal/gfonts/css") + (assoc :query query))))) + +(defn- fetch-and-process-gfont-css + [url] + (let [base (dm/str (assoc cf/public-uri :path "/internal/gfonts/font"))] + (->> (http/send! {:method :get :uri url :mode :cors :response-type :text}) + (rx/map :body) + (rx/map #(str/replace % "https://fonts.gstatic.com/s" base))))) (defmethod load-font :google [{:keys [id ::on-loaded] :as font}] (when (exists? js/window) (log/info :hint "load-font" :font-id id :backend "google") (let [url (generate-gfonts-url font)] - (load-font-css! url (partial on-loaded id)) + (->> (fetch-and-process-gfont-css url) + (rx/tap #(on-loaded id)) + (rx/subs (partial add-font-css! id))) nil))) ;; --- LOADER: CUSTOM @@ -187,7 +177,7 @@ (when (exists? js/window) (log/info :hint "load-font" :font-id id :backend "custom") (let [css (generate-custom-font-css font)] - (add-font-css! css) + (add-font-css! id css) (when (fn? on-loaded) (on-loaded)))))