Improve routing, add helpers

This commit is contained in:
Akira Tempaku 2025-06-21 11:55:20 +09:00
parent 63d7932890
commit 96e7dadfdf
Signed by: paku
GPG key ID: 5B4E8402BCC50607
14 changed files with 135 additions and 123 deletions

View file

@ -21,7 +21,7 @@
("ningle-fbr" .
(:class qlot/source/git:source-git
:initargs (:remote-url "https://github.com/skyizwhite/ningle-fbr.git")
:version "git-3c83e74a84e57f8ee2a9e98c4045d9b3e7a937f5"))
:version "git-19aae06f7ff17f16f008133a3bf234b89ab7de1e"))
("lack-mw" .
(:class qlot/source/git:source-git
:initargs (:remote-url "https://github.com/skyizwhite/lack-mw.git")

9
src/api/not-found.lisp Normal file
View file

@ -0,0 +1,9 @@
(defpackage #:website/api/not-found
(:use #:cl
#:jingle)
(:export #:handle-not-found))
(in-package #:website/api/not-found)
(defun handle-not-found ()
(set-response-status :not-found)
'(:|message| "Not found"))

View file

@ -1,17 +1,17 @@
(defpackage #:website/routes/api/revalidate
(defpackage #:website/api/revalidate
(:use #:cl
#:jingle
#:access)
(:import-from #:website/lib/env
#:microcms-webhook-key)
(:import-from #:website/helper
#:get-request-body-plist)
#:request-body-json->plist)
(:import-from #:website/lib/cms
#:clear-about-cache
#:clear-works-cache
#:clear-blog-cache)
(:export #:handle-post))
(in-package #:website/routes/api/revalidate)
(in-package #:website/api/revalidate)
(defun handle-post (params)
(declare (ignore params))
@ -19,7 +19,7 @@
(microcms-webhook-key))
(set-response-status :unauthorized)
(return-from handle-post '(:|message| "Invalid token")))
(let* ((body (get-request-body-plist))
(let* ((body (request-body-json->plist))
(api (getf body :|api|))
(id (getf body :|id|))
(old-draft-key (accesses body :|contents| :|old| :|draftKey|))

View file

@ -1,27 +1,71 @@
(defpackage #:website/app
(:use #:cl
#:jingle)
#:jingle
#:hsx)
(:import-from #:jonathan
#:to-json)
(:import-from #:ningle-fbr
#:set-routes)
(:import-from #:lack/middleware/mount
#:*lack-middleware-mount*)
(:import-from #:lack-mw
#:*trim-trailing-slash*)
(:import-from #:clack-errors
#:*clack-error-middleware*)
(:import-from #:website/components/metadata
#:~metadata)
(:import-from #:website/components/scripts
#:~scripts)
(:import-from #:website/components/layout
#:~layout)
(:import-from #:website/lib/env
#:dev-mode-p)
(:import-from #:website/renderer)
(:export #:*app*))
(in-package #:website/app)
(defparameter *page-app* (make-app))
(set-routes *page-app* :system :website :target-dir-path "pages")
(defmethod jingle:process-response :around ((app (eql *page-app*)) result)
(set-response-header :content-type "text/html; charset=utf-8")
(when (eq (request-method *request*) :get)
(let ((strategy (context :cache)))
(cond ((dev-mode-p)
(set-response-header :cache-control "private, no-store, must-revalidate"))
((eq strategy :ssr)
(set-response-header :cache-control "public, max-age=0, must-revalidate"))
((eq strategy :isr)
(set-response-header :cache-control "public, max-age=0, s-maxage=60, stale-while-revalidate=60"))
((eq strategy :sg)
(set-response-header :cache-control "public, max-age=0, s-maxage=31536000, must-revalidate")))))
(call-next-method app
(render-to-string
(hsx (html :lang "en"
(head
(~metadata :metadata (context :metadata))
(~scripts))
(body
(~layout result)))))))
(defparameter *api-app* (make-app))
(set-routes *api-app* :system :website :target-dir-path "api")
(defmethod jingle:process-response :around ((app (eql *api-app*)) result)
(set-response-header :content-type "application/json; charset=utf-8")
(call-next-method app (to-json result)))
(defun with-args (middleware &rest args)
(lambda (app)
(apply middleware app args)))
(defparameter *app*
(let ((app (make-app)))
(set-routes app :system :website :target-dir-path "routes")
(install-middleware app (lambda (app)
(funcall *clack-error-middleware*
app
:debug (dev-mode-p))))
(install-middleware app *trim-trailing-slash*)
(static-path app "/assets/" "assets/")
(configure app)))
(progn
(install-middleware *page-app*
(with-args *lack-middleware-mount* "/api" *api-app*))
(install-middleware *page-app*
(with-args *clack-error-middleware* :debug (dev-mode-p)))
(install-middleware *page-app* *trim-trailing-slash*)
(static-path *page-app* "/assets/" "assets/")
(configure *page-app*)))
*app*

View file

@ -5,18 +5,12 @@
#:make-flexi-stream)
(:import-from #:jonathan
#:parse)
(:export #:api-request-p
#:get-request-body-plist))
(:export #:request-body-json->plist
#:set-metadata
#:set-cache))
(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 ()
(defun request-body-json->plist ()
(parse
(let ((text-stream (make-flexi-stream (request-raw-body *request*)
:external-format :utf-8)))
@ -24,3 +18,9 @@
(loop :for char := (read-char text-stream nil)
:while char
:do (write-char char out))))))
(defun set-metadata (metadata)
(setf (context :metadata) metadata))
(defun set-cache (strategy)
(setf (context :cache) strategy))

View file

@ -1,21 +1,22 @@
(defpackage #:website/routes/about
(defpackage #:website/pages/about
(:use #:cl
#:hsx
#:jingle)
#:jingle
#:website/helper)
(:import-from #:website/lib/cms
#:fetch-about)
(:import-from #:website/components/article
#:~article)
(:export #:handle-get))
(in-package #:website/routes/about)
(in-package #:website/pages/about)
(defparameter *metadata*
(list :title "about"))
(defun handle-get (params)
(setf (context :metadata) *metadata*)
(set-metadata *metadata*)
(with-request-params ((draft-key "draft-key" nil)) params
(setf (context :cache) (if draft-key :ssr :isr))
(set-cache (if draft-key :ssr :isr))
(let ((about (fetch-about :draft-key draft-key)))
(~article
:title "About"

View file

@ -1,15 +1,16 @@
(defpackage #:website/routes/blog/<id>
(defpackage #:website/pages/blog/<id>
(:use #:cl
#:hsx
#:jingle)
#:jingle
#:website/helper)
(:import-from #:website/lib/cms
#:fetch-blog-detail)
(:import-from #:website/routes/not-found
(:import-from #:website/pages/not-found
#:handle-not-found)
(:import-from #:website/components/article
#:~article)
(:export #:handle-get))
(in-package #:website/routes/blog/<id>)
(in-package #:website/pages/blog/<id>)
(defun handle-get (params)
(with-request-params ((id :id nil)
@ -17,10 +18,10 @@
(let ((blog (fetch-blog-detail id :draft-key draft-key)))
(unless blog
(return-from handle-get (handle-not-found)))
(setf (context :cache) (if draft-key :ssr :isr))
(setf (context :metadata) (list :title (getf blog :title)
:description (getf blog :description)
:type "article"))
(set-cache (if draft-key :ssr :isr))
(set-metadata (list :title (getf blog :title)
:description (getf blog :description)
:type "article"))
(hsx
(~article
:title (getf blog :title)

View file

@ -1,21 +1,22 @@
(defpackage #:website/routes/blog/index
(defpackage #:website/pages/blog/index
(:use #:cl
#:hsx
#:jingle)
#:jingle
#:website/helper)
(:import-from #:website/lib/cms
#:fetch-blog-list)
(:import-from #:website/lib/time
#:asctime)
(:export #:handle-get))
(in-package #:website/routes/blog/index)
(in-package #:website/pages/blog/index)
(defparameter *metadata*
(list :title "blog"))
(defun handle-get (params)
(declare (ignore params))
(setf (context :cache) :isr)
(setf (context :metadata) *metadata*)
(set-cache :isr)
(set-metadata *metadata*)
(let ((blogs (fetch-blog-list :page 1)))
(hsx
(section

View file

@ -1,13 +1,14 @@
(defpackage #:website/routes/index
(defpackage #:website/pages/index
(:use #:cl
#:hsx
#:access
#:jingle)
#:jingle
#:website/helper)
(:import-from #:website/lib/cms
#:get-about)
(:export #:handle-get
#:handle-head))
(in-package #:website/routes/index)
(in-package #:website/pages/index)
(defparameter *links*
'(("Keyoxide"
@ -28,7 +29,7 @@
(defun handle-get (params)
(declare (ignore params))
(setf (context :cache) :sg)
(set-cache :sg)
(hsx
(div :class "flex flex-col items-center justify-center h-full"
(img

23
src/pages/not-found.lisp Normal file
View file

@ -0,0 +1,23 @@
(defpackage #:website/pages/not-found
(:use #:cl
#:hsx
#:jingle
#:website/helper)
(:export #:handle-not-found))
(in-package #:website/pages/not-found)
(defparameter *metadata*
'(:title "404 Not Found"
:description "The page you are looking for may have been deleted or the URL might be incorrect."
:error t))
(defun handle-not-found ()
(set-response-status :not-found)
(set-cache :ssr)
(set-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"))))

View file

@ -1,21 +1,22 @@
(defpackage #:website/routes/works
(defpackage #:website/pages/works
(:use #:cl
#:hsx
#:jingle)
#:jingle
#:website/helper)
(:import-from #:website/lib/cms
#:fetch-works)
(:import-from #:website/components/article
#:~article)
(:export #:handle-get))
(in-package #:website/routes/works)
(in-package #:website/pages/works)
(defparameter *metadata*
(list :title "works"))
(defun handle-get (params)
(setf (context :metadata) *metadata*)
(set-metadata *metadata*)
(with-request-params ((draft-key "draft-key" nil)) params
(setf (context :cache) (if draft-key :ssr :isr))
(set-cache (if draft-key :ssr :isr))
(let ((works (fetch-works :draft-key draft-key)))
(~article
:title "Works"

View file

@ -1,42 +0,0 @@
(defpackage #:website/renderer
(:use #:cl
#:hsx
#:jingle)
(:import-from #:jonathan
#:to-json)
(:import-from #:website/lib/env
#:dev-mode-p)
(:import-from #:website/helper
#:api-request-p)
(:import-from #:website/components/metadata
#:~metadata)
(:import-from #:website/components/scripts
#:~scripts)
(:import-from #:website/components/layout
#:~layout))
(in-package #:website/renderer)
(defmethod jingle:process-response :around ((app jingle:app) result)
(when (eq (request-method *request*) :get)
(let ((strategy (context :cache)))
(cond ((dev-mode-p)
(set-response-header :cache-control "private, no-store, must-revalidate"))
((eq strategy :ssr)
(set-response-header :cache-control "public, max-age=0, must-revalidate"))
((eq strategy :isr)
(set-response-header :cache-control "public, max-age=0, s-maxage=60, stale-while-revalidate=60"))
((eq strategy :sg)
(set-response-header :cache-control "public, max-age=0, s-maxage=31536000, must-revalidate")))))
(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 "en"
(head
(~metadata :metadata (context :metadata))
(~scripts))
(body
(~layout result)))))))))

View file

@ -1,26 +0,0 @@
(defpackage #:website/routes/not-found
(:use #:cl
#:hsx
#:jingle)
(:import-from #:website/helper
#:api-request-p)
(:export #:handle-not-found))
(in-package #:website/routes/not-found)
(defparameter *metadata*
'(:title "404 Not Found"
:description "The page you are looking for may have been deleted or the URL might be incorrect."
:error t))
(defun handle-not-found ()
(set-response-status :not-found)
(setf (context :cache) :ssr)
(setf (context :metadata) *metadata*)
(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")))))

View file

@ -1,8 +1,7 @@
/** @type {import('tailwindcss').Config} */
module.exports = {
content: [
"./src/renderer.lisp",
"./src/routes/**/*.lisp",
"./src/pages/**/*.lisp",
"./src/components/**/*.lisp",
],
theme: {