Web -Framework für ClojureScript auf dem Knoten.

In der Tradition von Django, Flask und Schienen. Entworfen für Indie -Devs, die schnell versenden. Schlacht auf echten Websites getestet.
Philosophie | Schneller Start | Dokumentation | API | Beispiele | Gemeinschaft
( 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 - Konfigurieren Sie den Port SiteFox -Webserver bindend an.BIND_ADDRESS - Konfigurieren Sie den IP -Adress -SiteFox -Webserver bindend an.SMTP_SERVER - Konfigurieren Sie den ausgehenden SMTP -Server EG SMTP_SERVER=smtps://username:[email protected]/?pool=true .DATABASE_URL - Konfigurieren Sie die Datenbank, um eine Verbindung herzustellen. Standardeinstellungen zu sqlite://./database.sqlite . Der schnellste Weg, um zu beginnen, ist die Verwendung eines der create -Skripts, das ein Beispielprojekt für Sie mit einem Befehl eingerichtet wird. Wenn Sie eine einfache Website ohne viel Front-End-Interaktivität über die Einreichung von Formularen erstellen, ist das NBB Create-Skript der Weg:
npm init sitefox-nbb mywebsite
Dadurch wird ein Ordner namens mywebsite erstellt, der Ihr neues Projekt enthält. Beachten Sie, dass Sie ScittleTle verwenden können, um die ClJS-Client-Seite auszuführen.
Wenn Sie eine ClojureScript-Anwendung in Full-Stack erstellen, ist das Shadow-ClJS-Skript der Weg:
npm init sitefox-shadow-fullstack myapp
Dadurch wird ein Ordner namens myapp erstellt, der Ihr neues Projekt enthält.
Fügen Sie Ihrem Projekt als Abhängigkeit SiteFox hinzu:
{:deps
{io.github.chr15m/sitefox {:git/tag "v0.0.26" :git/sha "e6ea2027b5d4277917732d43d550083c8e105da9"}}}
Wenn Sie npm verwenden, können Sie SiteFox auf diese Weise als Abhängigkeit installieren. Wenn Sie dies tun, müssen Sie Ihrem Klassenpfad node_modules/sitefox/src irgendwie hinzufügen.
npm i sitefox
Hinweis : M1 -Mac -Benutzer müssen möglicherweise die Python -Version in NPM so festlegen:
npm config set python python3
Dies liegt daran, dass der node-sqlite3 -Build ohne die Einstellung manchmal fehlschlägt. Weitere Informationen finden Sie in dieser Ausgabe.
Ein Beispielserver mit zwei Routen, von denen einer Werte in die Schlüsselwertdatenbank schreibt.
( 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)))Weitere Beispiele für SiteFox hier.
Wenn Sie Unterstützung bei SiteFox benötigen, können Sie:
SiteFox verwendet den Express -Webserver mit vernünftigen Standardeinstellungen für Sitzungen und Protokollierung. Einzelheiten finden Sie in der Dokumentation zur Express -Routing.
Erstellen Sie einen neuen Server mit web/start und richten Sie eine Route ein, die mit "Hello World!" Reagiert. wie folgt:
( -> ( web/start )
( .then ( fn [app host port]
( .get app " /myroute "
( fn [req res]
( .send res " Hello world! " )))) SiteFox verfügt über ein optionales System zum Nachladen von Routen, wenn der Server geändert wird. Ihre Expressrouten werden jedes Mal neu geladen, wenn Ihr Servercode aktualisiert wird (z. B. durch einen Schatten-ClJs-Build). In diesem Beispiel werden die Funktions- setup-routes aufgerufen, wenn ein Wiederaufbau auftritt.
( 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)) Ich empfehle die Promesa -Bibliothek für die Verwaltung von Promise Control Flow. In diesem Beispiel wird davon ausgegangen, dass [promesa.core :as p] erforderlich ist:
( p/let [[app host port] ( web/start )]
; now use express `app` to set up routes and middleware
)Siehe auch folgende Beispiele:
Anstelle von Vorlagen bietet SiteFox Verknüpfungen für das Rendering von Server -Seitenreagenzern an und fusionierte mit HTML -Dokumenten.
[sitefox.html :refer [render-into]]Sie können ein HTML -Dokument laden und Reagenzien formen in ein ausgewähltes Element:
( 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 verwendet Node-HTML-Parser und bietet Verknüpfungen für die Arbeit mit HTML & Reagenz: Reagenz:
html/parse ist eine Abkürzung für node-html-parser/parse .html/render ist eine Abkürzung für Reagenzs render-to-static-markup .html/$ ist eine Kurzform für den querySelector des Parsers.html/$$ ist eine Abkürzung für den querySelectorAll des Parsers.Siehe auch das Beispielprojekt für Vorlagen.
SITEFOX erleichtert es einfach, mit der Speicherung von Schlüsselwertdaten ohne Konfiguration zu beginnen. Sie können später zu strukturierteren Daten übergehen, wenn Sie diese benötigen. Es bündelt Keyv, ein Datenbank-Backed-Schlüsselwert. Sie können über db/kv und die zugrunde liegende Datenbank über db/client auf den Schlüsselwertspeicher zugreifen.
Siehe die vollständige Dokumentation für das DB -Modul.
Standardmäßig wird eine lokale SQLite -Datenbank verwendet, und Sie können ohne Konfiguration sofort Daten auf dem Server anhalten. Sobald Sie zur Produktion übergehen, können Sie eine andere Datenbank mithilfe der DATABASE_URL konfigurieren. Um beispielsweise eine Postgres -Datenbank mit dem Namen "DBNAME" zu verwenden, können Sie wie folgt darauf zugreifen (abhängig von Ihrem Netzwerk/lokalen Setup):
DATABASE_URL="postgres://%2Fvar%2Frun%2Fpostgresql/DBNAME"
DATABASE_URL=postgres://someuser:somepassword@somehost:5432/DBNAME
DATABASE_URL=postgres:///somedatabase
Beachten Sie, dass Sie auch npm install @keyv/postgres müssen, wenn Sie das Postgres -Backend verwenden möchten.
Um die Datenbank- und Schlüsselwertschnittstelle zu verwenden, benötigen Sie zunächst das Datenbankmodul:
[sitefox.db :as db] Jetzt können Sie db/kv verwenden, um einen Schlüsselwert in eine "Tabelle" zu schreiben:
( let [table ( db/kv " sometable " )]
( .set table " key " " 42 " ))Rufen Sie den Wert erneut ab:
( -> ( .get table " key " )
( .then ( fn [val] ( print val)))) Sie können db/client verwenden, um auf den zugrunde liegenden Datenbank -Client zugreifen zu können. Zum Beispiel um eine Abfrage gegen die konfigurierte Datenbank zu machen:
( let [c ( db/client )]
( -> ( .query c " select * from sometable WHERE x = 1 " )
( .then ( fn [rows] ( print rows)))))Auch hier wird Promesa für die Verwaltung des Steuerflusses während des Datenbankvorgangs empfohlen.
Um Schlüsselwertdaten aus der Befehlszeile zu untersuchen, verwenden Sie SQLite und JQ, um solche Daten zu filtern:
sqlite3 database.sqlite "select * from keyv where key like 'SOMEPREFIX%';" | cut -f 2 -d "|" | jq '.'
Standardmäßig liefert das node-sqlite3 Modul keine vollständigen Stapelspuren mit Zeilennummern usw. Wenn ein Datenbankfehler auftritt. Es ist möglich, ausführliche Stapelspuren mit einer kleinen Leistungsstrafe wie folgt einzuschalten:
( ns yourapp
( :require
[ " sqlite3 " :as sqlite3]))
( .verbose sqlite3) Wenn Sie SQLite3 in der Produktion ausführen möchten, können Sie den Fehler SQLITE_BUSY: database is locked wenn Sie gleichzeitige Datenbankvorgänge von verschiedenen Clients ausführen. Es ist möglich, diese Probleme mit der Parallelität zu beheben und Probleme zu sperren, indem der Schreibmodus in SQLite3 wie folgt aktiviert wird:
(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))
Dieser Code kann sicher in die Hauptfunktion Ihres Servercode platziert werden.
Die Sitzungen sind standardmäßig aktiviert und jeder Besucher Ihres Servers hat eine eigene Sitzung. Die Sitzungsdaten werden über Seite geladen, sodass Sie beispielsweise den Authentifizierungsstatus speichern können. Die Sitzungen werden in eine namespaierte kv -Tabelle unterbrochen (siehe Abschnitt Datenbank oben). Sie können beliebige JS -Datenstrukturen mit req.session in die Sitzung lesen und schreiben.
Um einen Wert in den Session Store zu schreiben (innerhalb einer Routenhandlerfunktion):
( let [session ( aget req " session " )]
( aset session " myvalue " 42 ))Um einen Wert aus dem Session Store zu lesen:
( aget req " session " " myvalue " )SiteFox wickelt die Passbibliothek um die Authentifizierung. Mit drei Funktionsaufrufen können Sie Ihrer App einfache E -Mail und passwortbasierte Authentifizierung hinzufügen:
( 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 ... ;
)) Die in der template übergebene Vorlagenzeichenfolge ist ein HTML -Dokument und "main" ist der Selektor, der angibt, wo die Auth -Benutzeroberfläche montiert werden soll. Auf diese Weise werden die folgenden Routen standardmäßig eingerichtet, auf denen Sie Benutzer anmelden, sich anmelden und ihr Passwort zurücksetzen können:
/auth/sign-in/auth/sign-up/auth/reset-passwordEs ist auch möglich, die Standard -UI -Reagenz -Formulare und die Umleitungs -URLs zu überschreiben, um sie mit Ihren eigenen Versionen anzupassen. In der Auth -Dokumentation finden Sie Details zur Lieferung Ihrer eigenen Reagenzien. Sehen Sie sich auch den Quellcode für die Standard -Reagenzien -Auth -Formulare an, wenn Sie Ihre eigenen erstellen möchten.
Wenn sich ein Benutzer anmeldet, wird seine Daten in die von SiteFox verwendete Standard -KeyV -Datenbank bestehen. Sie können die aktuell authentifizierte Benutzerdatenstruktur im Anforderungsobjekt abrufen:
( let [user ( aget req " user " )] ...) Sie können dann die Daten des Benutzers aktualisieren und ihre Daten wieder in der Datenbank speichern. Die Bibliothek j applied-science.js-interop .
( p/let [user ( aget req " user " )]
( j/assoc! user :somekey 42 )
( auth/save-user user)) Wenn Sie eine neue Tabelle erstellen möchten, ist es nützlich, sie auf der UUID des Benutzers zu Schlüssel zu bringen, mit der Sie erhalten können (aget user "id") .
Weitere Details finden Sie im Authentifizierungsbeispiel.
Um ein neues Authentifizierungsschema wie den Benutzernamen oder die Drittanbieter OAuth hinzuzufügen, wenden Sie sich an die Passport -Dokumente und Auth.ClJS. Anfragen am meisten willkommen!
SiteFox Bündel NodeMailer zum Senden von E -Mails. Konfigurieren Sie Ihren ausgehenden SMTP -Server:
SMTP_SERVER=smtps://username:[email protected]/?pool=true
Dann können Sie die send-email Funktion wie folgt verwenden:
( -> ( 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)) Standardmäßig werden gesendete E-Mails im JSON-LINES-Format an ./logs/mail.log angemeldet.
Wenn Sie keinen SMTP -Server angeben, befindet sich das E -Mail -Modul im Debug -Modus. Es werden keine E-Mails gesendet, ausgehende E-Mails werden zur Inspektion an /tmp geschrieben, und send-email Ergebnisse werden ebenfalls an der Konsole angemeldet.
Wenn Sie SMTP_SERVER=ethereal festlegen, wird der Ehereal.Email -Dienst verwendet. Nach dem Ausführen send-email können Sie die url Eigenschaft des Ergebniss drucken. Sie können die Links verwenden, die zum Testen Ihrer E -Mails im Dev -Modus gedruckt werden.
Sehen Sie sich auch das Beispielprojekt zum Senden-E-Mail an.
Siehe das Beispiel für Formularvalidierungsanlagen, das den Node-Input-Validator verwendet und über CSRF-Probleme überprüft wird.
Um sicherzustellen, dass Sie ohne CSRF -Warnungen POST können, sollten Sie ein verstecktes Element wie dieses (Reagenzsyntax) erstellen:
[ :input { :name " _csrf " :type " hidden " :default-value ( .csrfToken req)}] Wenn Sie eine AJAX POST von der Client -Seite stellen, sollten Sie das CSRF -Token als Kopfzeile übergeben. Ein gültiges Token ist als Zeichenfolge am JSON-Endpunkt /_csrf-token erhältlich fetch-csrf-token
( 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))})))) Hinweis : Sie können das CSRF -Token stattdessen von einem Client -Side -Cookie abrufen, wenn Sie die Umgebungsvariable SEND_CSRF_TOKEN festlegen. Dies war der Standard in früheren SiteFox -Versionen. Wenn SiteFox das Token für jede Get-Anfrage im clientseitigen Cookie XSRF-TOKEN sendet, kann dies mit der ui/csrf-token Funktion abgerufen werden. Dies ist eine gültige, aber weniger sichere Form des CSRF -Schutzes.
In einigen seltenen Umständen möchten Sie möglicherweise CSRF-Überprüfungen ausschalten (z. B. eine API von einem Nicht-Browser-Gerät abgeben). Wenn Sie wissen, was Sie tun, können Sie den pre-csrf-router verwenden, um Routen hinzuzufügen, die die CSRF-Überprüfung umgehen:
( 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)) Standardmäßig schreibt der Webserver in den Ordner ./logs an Protokolldateien. Diese Dateien werden vom Server automatisch gedreht. Es gibt zwei Arten von Protokollen:
logs/access.log , die Standard -Webzugriffsprotokolle im "kombinierten" Format sind.logs/error.log tracebacks/install-traceback-handlerSenden Sie nicht erfasste Ausnahmen an das Fehlerprotokoll:
(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)
Erstellen Sie build-id.txt basierend auf dem aktuellen GIT-Commit wie folgt:
git rev-parse HEAD | cut -b -8 > build-id.txt
Wenn Sie korrekte ClojureScript-Zeilennummern in Tracebacks erhalten möchten, erfordern Sie ["source-maps-support" :as sourcemaps] und dann:
(.install sourcemaps)
Sie können die Funktion web/setup-error-handler verwenden, um eine Seite für diese Fehler zu erstellen, die auf einer von Ihnen definierten Reagenzkomponente basiert:
( 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)Sie können diese kombinieren, um sowohl 500 interne Serverfehler als auch unbefolgte Ausnahmen zu fangen:
(let [traceback-handler (install-traceback-handler admin-email build-id)]
(web/setup-error-handler app template-app "main" component-error-page traceback-handler))
Das Live-Nachladen wird sowohl mit nbb als auch mit shadow-cljs unterstützt. Es ist standardmäßig aktiviert, wenn die Skripts npm erstellen. Beispiele haben mehr Details.
SiteFox wurde von Chris McCormick (@mccrmx auf Twitter und@[email protected] auf Mastodon) hergestellt. Ich habe es iteriert, während ich Websites für mich und für Kunden baute.