在節點上的clojurescript的Web框架。

以Django,Flask和Rails的傳統。專為快速發貨的獨立開發人員而設計。戰斗在真實站點進行了測試。
哲學|快速啟動|文檔| API |示例|社區
( ns webserver
( :require
[promesa.core :as p]
[sitefox.html :refer [render]]
[sitefox.web :as web]))
( defn root [_req res]
( ->> ( render [ :h1 " Hello world! " ])
( .send res)))
( p/let [[app host port] ( web/start )]
( .get app " / " root)
( print " Serving on " ( str " http:// " host " : " port)))PORT - 配置端口SiteFox Web服務器綁定到。BIND_ADDRESS配置IP地址SiteFox Web服務器綁定到。SMTP_SERVER配置即將傳出的SMTP服務器,例如SMTP_SERVER=smtps://username:[email protected]/?pool=true 。DATABASE_URL配置要連接到的數據庫。默認為sqlite://./database.sqlite 。 最快的啟動方法是使用一個create腳本之一,它將使用一個命令為您設置示例項目。如果您正在建立一個簡單的網站,而沒有太多的前端交互性超出表單提交,則NBB創建腳本就是這樣:
npm init sitefox-nbb mywebsite
這將創建一個名為mywebsite的文件夾,其中包含您的新項目。請注意,您可以使用Scittle運行CLJS客戶端。
如果您正在構建全堆棧clojurescript應用程序,則Shadow-Cljs創建腳本是:
npm init sitefox-shadow-fullstack myapp
這將創建一個名為myapp的文件夾,其中包含您的新項目。
將SiteFox添加到您的項目中,作為一種依賴性:
{:deps
{io.github.chr15m/sitefox {:git/tag "v0.0.26" :git/sha "e6ea2027b5d4277917732d43d550083c8e105da9"}}}
如果您使用的是npm則可以以這種方式安裝SiteFox作為依賴關係。如果這樣做,則需要以某種方式將node_modules/sitefox/src添加到您的類路徑。
npm i sitefox
注意:M1 Mac用戶可能需要在NPM中設置Python版本:
npm config set python python3
這是因為node-sqlite3構建有時在沒有設置的情況下失敗。有關更多詳細信息,請參閱此問題。
一個帶有兩個路由的示例服務器,其中一條將值寫入鍵值數據庫。
( ns my.server
( :require
[promesa.core :as p]
[sitefox.web :as web]
[sitefox.db :refer [kv]]))
( defn home-page [req res]
; send a basic hello world response
( .send res " Hello world! " ))
( defn hello [req res]
; write a value to the key-value database
( p/let [table ( kv " sometable " )
r ( .write table " key " 42 )]
( .json res true )))
( defn setup-routes [app]
; flush all routes from express
( web/reset-routes app)
; set up an express route for "/"
( .get app " / " home-page)
; set up an express route for "/hello"
( .post app " /hello " hello)
; statically serve files from the "public" dir on "/"
; (or from "build" dir in PROD mode)
( web/static-folder app " / " " public " ))
( defn main! []
; create an express server and start serving
; BIND_ADDRESS & PORT env vars set host & port.
( p/let [[app _host _port] ( web/start )]
; set up the routes for the first time
( setup-routes app)))這裡有更多SiteFox示例。
如果您需要SiteFox的支持,則可以:
SiteFox使用具有明智的會話和記錄的明智默認值。有關詳細信息,請參見快速路由文檔。
使用web/start創建新服務器,並設置一條以“ Hello World!”響應的路線。如下:
( -> ( web/start )
( .then ( fn [app host port]
( .get app " /myroute "
( fn [req res]
( .send res " Hello world! " )))) SiteFox帶有可選的系統,可在更改服務器時重新加載路由。每當您的服務器代碼刷新時(例如,由Shadow-CLJS構建),您的Express路線都會重新加載。在此示例中,當進行重建時,將調用函數setup-routes 。
( defn setup-routes [app]
; flush all routes from express
( web/reset-routes app)
; ask express to handle the route "/"
( .get app " / " ( fn [req res] ( .send res " Hello world! " ))))
; during the server setup hook up the reloader
( reloader ( partial #'setup-routes app))我推薦Promesa庫來管理承諾控制流。此示例假設需要[promesa.core :as p] :
( p/let [[app host port] ( web/start )]
; now use express `app` to set up routes and middleware
)另請參閱以下示例:
SiteFox而不是模板,而是為服務器端試劑渲染,合併的WTH HTML文檔提供快捷方式。
[sitefox.html :refer [render-into]]您可以將HTML文檔加載並渲染試劑表格為選定的元素:
( def index-html ( fs/readFileSync " index.html " ))
( defn component-main []
[ :div
[ :h1 " Hello world! " ]
[ :p " This is my content. " ]])
; this returns a new HTML string that can be returned
; e.g. with (.send res)
( render-into index-html " main " [component-main])SiteFox使用Node-HTML-Parser,並提供用於與HTML和試劑合作的快捷方式:
html/parse是node-html-parser/parse的速記。html/render是試劑render-to-static-markup速記。html/$是解析器的querySelector的速記。html/$$是解析器的querySelectorAll的速記。另請參閱模板示例項目。
SiteFox使得無需配置即可開始存儲鍵值數據。如果需要,您可以稍後過渡到更多結構化數據。它將KeyV捆綁在一起,這是一個數據庫備份的密鑰值商店。您可以通過db/kv訪問密鑰值商店,並通過db/client訪問基礎數據庫。
請參閱DB模塊的完整文檔。
默認情況下,使用本地SQLITE數據庫,您可以立即開始在服務器上持續數據,而無需任何配置。轉移到生產後,您可以使用環境變量DATABASE_URL配置另一個數據庫。例如,要使用稱為“ dbname”的Postgres數據庫,您可以按以下方式訪問它(取決於您的網絡/本地設置):
DATABASE_URL="postgres://%2Fvar%2Frun%2Fpostgresql/DBNAME"
DATABASE_URL=postgres://someuser:somepassword@somehost:5432/DBNAME
DATABASE_URL=postgres:///somedatabase
請注意,如果要使用Postgres後端,則還需要npm install @keyv/postgres 。
要使用數據庫和鍵值接口首先需要數據庫模塊:
[sitefox.db :as db]現在,您可以使用db/kv將鍵值寫入名稱為“表”:
( let [table ( db/kv " sometable " )]
( .set table " key " " 42 " ))再次檢索值:
( -> ( .get table " key " )
( .then ( fn [val] ( print val))))您可以使用db/client訪問基礎數據庫客戶端。例如,對配置的數據庫進行查詢:
( let [c ( db/client )]
( -> ( .query c " select * from sometable WHERE x = 1 " )
( .then ( fn [rows] ( print rows)))))同樣,建議使用Promesa來管理數據庫操作期間的控制流。
要探索命令行的鍵值數據,請使用SQLite和JQ來過濾數據:
sqlite3 database.sqlite "select * from keyv where key like 'SOMEPREFIX%';" | cut -f 2 -d "|" | jq '.'
默認情況下, node-sqlite3模塊不會為數據庫錯誤時提供完整的堆棧跟踪。可以以較小的性能罰款打開詳細的堆棧痕跡,如下所示:
( ns yourapp
( :require
[ " sqlite3 " :as sqlite3]))
( .verbose sqlite3)如果要在生產中運行sqlite3,則可能會遇到錯誤SQLITE_BUSY: database is locked 。可以通過在sqlite3中啟用寫入記錄模式來解決這些並發問題和鎖定問題,如下所示:
(ns yourapp
(:require
[sitefox.db :refer [client]]))
(p/let [c (client)
wal-mode-enabled (.query c "PRAGMA journal_mode=WAL;")]
(js/console.log wal-mode-enabled))
該代碼可以安全地放置在服務器代碼的主要功能中。
默認情況下啟用了會話,您的服務器的每個訪問者都會有自己的會話。會話數據在頁面加載之間持續存在,因此您可以使用它來存儲身份驗證狀態。會話備份到名稱kv表中(請參見上面的數據庫部分)。您可以使用req.session讀取和編寫任意JS數據結構。
為會話存儲(在路由處理程序功能內部)寫一個值:
( let [session ( aget req " session " )]
( aset session " myvalue " 42 ))從會話商店讀取值:
( aget req " session " " myvalue " )SiteFox包裝護照庫以實現身份驗證。您可以使用三個函數調用將簡單的電子郵件和基於密碼的身份驗證添加到您的應用程序:
( defn setup-routes [app]
( let [template ( fs/readFileSync " index.html " )]
( web/reset-routes app)
; three calls to set up email based authentication
( auth/setup-auth app)
( auth/setup-email-based-auth app template " main " )
( auth/setup-reset-password app template " main " )
; ... add your additional routes here ... ;
))傳遞的template字符串是一個HTML文檔, "main"是指定載體UI的選擇器。默認情況下,這將設置以下路線,您可以發送用戶註冊,登錄並重置其密碼:
/auth/sign-in/auth/sign-up/auth/reset-password還可以覆蓋默認的Auth UI試劑表單和重定向URL,以使用您自己的版本自定義它們。有關如何提供您自己的試劑表格的詳細信息,請參見驗證文檔。還要查看默認試劑驗證表格的源代碼,如果您想製作自己的源代碼。
當用戶簽署其數據時,將持續到SiteFox使用的默認KEYV數據庫中。您可以在請求對像上檢索當前身份驗證的用戶的數據架構:
( let [user ( aget req " user " )] ...)然後,您可以更新用戶的數據並將其數據保存回數據庫。 applied-science.js-interop庫很方便(在此要求為j ):
( p/let [user ( aget req " user " )]
( j/assoc! user :somekey 42 )
( auth/save-user user))如果要創建一個新表,則可以在用戶的UUID上鍵入(aget user "id")很有用。
有關更多詳細信息,請參見身份驗證示例。
要添加一個新的身份驗證方案,例如基於用戶名或第三方Oauth,請諮詢Passport Docs和Auth.Cljs。拉請最歡迎!
SiteFox將NodeMailer捆綁以發送電子郵件。配置您的傳出SMTP服務器:
SMTP_SERVER=smtps://username:[email protected]/?pool=true
然後,您可以使用以下方式使用send-email函數:
( -> ( mail/send-email
" [email protected] "
" [email protected] "
" This is my test email. "
:text " Hello, This is my first email from **Sitefox**. Thank you. " )
( .then js/console.log))默認情況下,發送的電子郵件以JSON-LINES格式記錄到./logs/mail.log 。
如果您沒有指定SMTP服務器,則電子郵件模塊將處於調試模式。不會發送任何電子郵件,即將發出的電子郵件寫信給/tmp進行檢查,並且send-email結果也將記錄到控制台。
如果設置SMTP_SERVER=ethereal則將使用Ethereal.Email服務。運行send-email後,您可以打印結果的url屬性。您可以使用打印的鏈接在開發模式下測試您的電子郵件。
另請參閱“發送電子郵件”示例項目。
請參閱使用節點輸入 - validator並檢查CSRF問題的表單驗證示例。
為了確保您可以在沒有CSRF警告的POST您應該創建這樣的隱藏元素(試劑語法):
[ :input { :name " _csrf " :type " hidden " :default-value ( .csrfToken req)}]如果您要從客戶端提出AJAX POST請求,則應將CSRF令牌作為標頭傳遞。有效的令牌可以在JSON Endpoint /_csrf-token上作為字符串獲得,您可以使用fetch-csrf-token獲取它,並將其添加到Fetch請求的標題中,如下所示:
( ns n ( :require [sitefox.ui :refer [fetch-csrf-token]]))
( -> ( fetch-csrf-token )
( .then ( fn [token]
( js/fetch " /api/endpoint "
#js { :method " POST "
:headers #js { :Content-Type " application/json "
:X-XSRF-TOKEN token} ; <- use token here
:body ( js/JSON.stringify ( clj->js some-data))}))))注意:如果設置環境變量SEND_CSRF_TOKEN ,則可以從客戶端cookie獲取CSRF令牌。這是以前的SiteFox版本中的默認值。設置後,SiteFox將在客戶端cookie XSRF-TOKEN中的每個GET請求上發送令牌,並且可以使用ui/csrf-token功能檢索。這是CSRF保護的有效但不太安全的形式。
在某些極少數情況下,您可能希望關閉CSRF檢查(例如,從非瀏覽器設備發佈到API)。如果您知道自己在做什麼,則可以使用pre-csrf-router添加繞過CSRF檢查的路由:
( defn setup-routes [app]
; flush all routes from express
( web/reset-routes app)
; set up an API route bypassing CSRF checks
( .post ( j/get app " pre-csrf-router " ) " /api/endpoint " endpoint-unprotected-by-csrf)
; set up an express route for "/hello" which is protected as normal
( .post app " /hello " hello))默認情況下,Web服務器將寫入./logs文件夾中的日誌文件。這些文件由服務器自動旋轉。日誌有兩種類型:
logs/access.log是標準的Web訪問日誌,“合併”格式。logs/error.log在其中使用tracebacks/install-traceback-handler編寫回溯。向錯誤日誌發送未知的例外:
(def admin-email (env-required "ADMIN_EMAIL"))
(def build-id (try (fs/readFileSync "build-id.txt") (catch :default _e "dev")))
(install-traceback-handler admin-email build-id)
基於當前的git commit創建build-id.txt如下:
git rev-parse HEAD | cut -b -8 > build-id.txt
如果要在追溯中獲得正確的clojurescript行號,則需要["source-maps-support" :as sourcemaps] ,然後:
(.install sourcemaps)
您可以使用web/setup-error-handler功能根據您定義的試劑組件為這些錯誤提供頁面:
( defn component-error-page [_req error-code error]
[ :section.error
[ :h2 error-code " Error " ]
( case error-code
404 [ :p " We couldn't find the page you're looking for. " ]
500 [ :<> [ :p " An error occurred: " ] [ :p ( .toString error)]]
[ :div " An unknown error occurred. " ])])
( web/setup-error-handler app my-html-template " main " component-error-page)您可以將它們結合起來,以捕獲500個內部服務器錯誤和未接收的例外,如下所示:
(let [traceback-handler (install-traceback-handler admin-email build-id)]
(web/setup-error-handler app template-app "main" component-error-page traceback-handler))
使用nbb和shadow-cljs支持實時重新加載。默認情況下,使用NPM創建腳本時啟用它。示例有更多詳細信息。
SiteFox由Chris McCormick(Twitter上的@MCCRMX和@[email protected]上的mastodon上)製作。我在為自己和客戶建立網站時迭代了它。