Implement on-demand cache revalidation

This commit is contained in:
Akira Tempaku 2025-05-17 23:48:30 +09:00
parent ea91a6043e
commit 4532ab3afe
Signed by: paku
GPG key ID: 5B4E8402BCC50607
9 changed files with 98 additions and 26 deletions

View file

@ -2,5 +2,6 @@ WEBSITE_ENV=
WEBSITE_URL=
MICROCMS_SERVICE_DOMAIN=
MICROCMS_API_KEY=
MICROCMS_WEBHOOK_KEY=
CLOUDFLARE_ZONE_ID=
CLOUDFLARE_API_KEY=

2
qlfile
View file

@ -9,3 +9,5 @@ ql clack-errors
git microcms https://github.com/skyizwhite/microcms-lisp-sdk
ql local-time
ql function-cache
ql jonathan
ql flexi-streams

View file

@ -46,3 +46,11 @@
(:class qlot/source/ql:source-ql
:initargs (:%version :latest)
:version "ql-2023-10-21"))
("jonathan" .
(:class qlot/source/ql:source-ql
:initargs (:%version :latest)
:version "ql-2020-09-25"))
("flexi-streams" .
(:class qlot/source/ql:source-ql
:initargs (:%version :latest)
:version "ql-2024-10-12"))

26
src/helper.lisp Normal file
View file

@ -0,0 +1,26 @@
(uiop:define-package #:website/helper
(:use #:cl
#:jingle)
(:import-from #:flexi-streams
#:make-flexi-stream)
(:import-from #:jonathan
#:parse)
(:export #:api-request-p
#:get-request-body-plist))
(in-package #:website/helper)
(defun starts-with-p (prefix string)
(let ((pos (search prefix string :start1 0 :end1 (length prefix) :start2 0)))
(and pos (= pos 0))))
(defun api-request-p ()
(starts-with-p "/api/" (request-uri *request*)))
(defun get-request-body-plist ()
(parse
(let ((text-stream (make-flexi-stream (request-raw-body *request*)
:external-format :utf-8)))
(with-output-to-string (out)
(loop :for char := (read-char text-stream nil)
:while char
:do (write-char char out))))))

View file

@ -17,19 +17,19 @@
(setf microcms:*service-domain* (microcms-service-domain))
(setf microcms:*api-key* (microcms-api-key))
(defmacro memorize (name timeout)
(defmacro memorize (name)
(let ((origin (gensym)))
`(progn
(setf (fdefinition ',origin) (fdefinition ',name))
(defcached (,name :timeout ,timeout) (&key query)
(defcached ,name (&key query)
(,origin :query query)))))
(define-object-client about)
(memorize get-about 60)
(memorize get-about)
(define-object-client work)
(memorize get-work 60)
(memorize get-work)
(define-list-client blog)
(memorize get-blog-list 60)
(memorize get-blog-detail 60)
(memorize get-blog-list)
(memorize get-blog-detail)

View file

@ -18,3 +18,4 @@
(env-var website-url "WEBSITE_URL")
(env-var microcms-service-domain "MICROCMS_SERVICE_DOMAIN")
(env-var microcms-api-key "MICROCMS_API_KEY")
(env-var microcms-webhook-key "MICROCMS_WEBHOOK_KEY")

View file

@ -2,8 +2,10 @@
(:use #:cl
#:hsx
#:jingle)
(:import-from #:hsx/element
#:element)
(:import-from #:jonathan
#:to-json)
(:import-from #:website/helper
#:api-request-p)
(:import-from #:website/components/metadata
#:~metadata)
(:import-from #:website/components/scripts
@ -13,17 +15,21 @@
(in-package #:website/renderer)
(defmethod jingle:process-response :around ((app jingle:app) result)
(set-response-header :content-type "text/html; charset=utf-8")
(when (eq (request-method *request*) :get)
(set-response-header :cache-control "public, max-age=60"))
(call-next-method app
(render-to-string
(hsx (html :lang "ja"
(head
(~metadata :metadata (context :metadata))
(~scripts))
(body
:hx-ext "head-support, response-targets, preload"
:hx-boost "true" :hx-swap "transition:true"
:hx-target-404 "body" :hx-target-5* "body"
(~layout result)))))))
(cond ((api-request-p)
(set-response-header :content-type "application/json; charset=utf-8")
(call-next-method app (to-json result)))
(t
(set-response-header :content-type "text/html; charset=utf-8")
(call-next-method app
(render-to-string
(hsx (html :lang "ja"
(head
(~metadata :metadata (context :metadata))
(~scripts))
(body
:hx-ext "head-support, response-targets, preload"
:hx-boost "true" :hx-swap "transition:true"
:hx-target-404 "body" :hx-target-5* "body"
(~layout result)))))))))

View file

@ -0,0 +1,24 @@
(defpackage #:website/routes/api/revalidate
(:use #:cl
#:jingle)
(:import-from #:function-cache
#:clear-cache)
(:import-from #:website/lib/env
#:microcms-webhook-key)
(:import-from #:website/helper
#:get-request-body-plist)
(:import-from #:website/lib/cms
#:get-about)
(:export #:handle-post))
(in-package #:website/routes/api/revalidate)
(defun handle-post (params)
(declare (ignore params))
(unless (string= (car (get-request-header "X-MICROCMS-WEBHOOK-KEY"))
(microcms-webhook-key))
(set-response-status :unauthorized)
(return-from handle-post '(:|message| "Invalid token")))
(let* ((body (get-request-body-plist))
(api (getf body :|api|)))
(cond ((string= api "about") (clear-cache 'get-about)))
'(:|message| "ok")))

View file

@ -2,6 +2,8 @@
(:use #:cl
#:hsx
#:jingle)
(:import-from #:website/helper
#:api-request-p)
(:export #:handle-not-found))
(in-package #:website/routes/not-found)
@ -12,9 +14,11 @@
(defun handle-not-found ()
(setf (context :metadata) *metadata*)
(hsx
(div :class "flex flex-col h-full items-center justify-center gap-y-6"
(h1 :class "font-bold text-2xl"
"404 Not Found")
(a :href "/" :class "text-lg text-pink-500 hover:underline"
"Back to TOP"))))
(if (api-request-p)
'(:|message| "404 Not Found")
(hsx
(div :class "flex flex-col h-full items-center justify-center gap-y-6"
(h1 :class "font-bold text-2xl"
"404 Not Found")
(a :href "/" :class "text-lg text-pink-500 hover:underline"
"Back to TOP")))))