Kerangka kerja web untuk clojurescript di node.

Dalam tradisi Django, Flask, dan Rails. Dirancang untuk pengembang indie yang mengirim cepat. Pertempuran diuji di situs sungguhan.
Filsafat | Mulai Cepat | Dokumentasi | API | Contoh | Masyarakat
( 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 - Konfigurasikan server web port Sitefox mengikat ke.BIND_ADDRESS - Konfigurasikan Alamat IP Sitefox Web Server mengikat ke.SMTP_SERVER - Konfigurasikan server SMTP keluar misalnya SMTP_SERVER=smtps://username:[email protected]/?pool=true .DATABASE_URL - Konfigurasikan database untuk terhubung ke. Default ke sqlite://./database.sqlite . Cara tercepat untuk memulai adalah menggunakan salah satu skrip create yang akan mengatur proyek contoh untuk Anda dengan satu perintah. Jika Anda membangun situs web sederhana tanpa banyak interaktivitas front-end di luar pengiriman formulir, skrip NBB Buat adalah caranya:
npm init sitefox-nbb mywebsite
Ini akan membuat folder bernama mywebsite yang berisi proyek baru Anda. Catatan Anda dapat menggunakan Scittle untuk menjalankan sisi klien CLJS.
Jika Anda membangun aplikasi clojurescript-stack full-stack, Shadow-Cljs, buat skrip adalah caranya:
npm init sitefox-shadow-fullstack myapp
Itu akan membuat folder bernama myapp yang berisi proyek baru Anda.
Tambahkan Sitefox ke proyek Anda sebagai ketergantungan:
{:deps
{io.github.chr15m/sitefox {:git/tag "v0.0.26" :git/sha "e6ea2027b5d4277917732d43d550083c8e105da9"}}}
Jika Anda menggunakan npm Anda dapat menginstal Sitefox sebagai ketergantungan dengan cara itu. Jika Anda melakukan itu, Anda perlu menambahkan node_modules/sitefox/src ke classpath Anda entah bagaimana.
npm i sitefox
Catatan : Pengguna M1 Mac mungkin perlu mengatur versi Python di NPM seperti ini:
npm config set python python3
Ini karena build node-sqlite3 terkadang gagal tanpa pengaturan. Lihat masalah ini untuk detail lebih lanjut.
Contoh server dengan dua rute, salah satunya menulis nilai ke database nilai kunci.
( 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)))Lebih banyak contoh Sitefox di sini.
Jika Anda membutuhkan dukungan dengan Sitefox, Anda dapat:
Sitefox menggunakan server web Express dengan default yang masuk akal untuk sesi dan pencatatan. Lihat Dokumentasi Routing Express untuk detailnya.
Buat server baru dengan web/start dan atur rute yang merespons dengan "Hello World!" sebagai berikut:
( -> ( web/start )
( .then ( fn [app host port]
( .get app " /myroute "
( fn [req res]
( .send res " Hello world! " )))) Sitefox dilengkapi dengan sistem opsional untuk memuat ulang rute ketika server diubah. Rute ekspres Anda akan dimuat ulang setiap kali kode server Anda disegarkan (misalnya oleh build shadow-cljs). Dalam contoh ini setup-routes fungsi akan dipanggil ketika pembangunan kembali terjadi.
( 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)) Saya merekomendasikan perpustakaan Promesa untuk mengelola aliran kontrol janji. Contoh ini mengasumsikan membutuhkan [promesa.core :as p] :
( p/let [[app host port] ( web/start )]
; now use express `app` to set up routes and middleware
)Lihat juga contoh -contoh ini:
Alih -alih templat, Sitefox menawarkan pintasan untuk rendering reagen sisi server, digabungkan dengan dokumen HTML dengan.
[sitefox.html :refer [render-into]]Anda dapat memuat dokumen HTML dan membuat formulir reagen ke dalam elemen yang dipilih:
( 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 menggunakan Node-HTML-Parser dan menawarkan pintasan untuk bekerja dengan HTML & Reagent:
html/parse adalah singkatan untuk node-html-parser/parse .html/render adalah singkatan untuk render-to-static-markup reagen.html/$ adalah singkatan untuk querySelector parser.html/$$ adalah singkatan untuk querySelectorAll parser.Juga lihat proyek contoh templat.
Sitefox memudahkan untuk mulai menyimpan data nilai kunci tanpa konfigurasi. Anda dapat beralih ke data yang lebih terstruktur nanti jika Anda membutuhkannya. Ini menggabungkan keyv yang merupakan toko nilai kunci yang didukung database. Anda dapat mengakses toko nilai kunci melalui db/kv dan database yang mendasarinya melalui db/client .
Lihat dokumentasi lengkap untuk modul DB.
Secara default, database SQLite lokal digunakan dan Anda dapat segera memulai data yang bertahan di server tanpa konfigurasi apa pun. Setelah Anda pindah ke produksi, Anda dapat mengonfigurasi database lain menggunakan Variable Variable DATABASE_URL . Misalnya, untuk menggunakan database Postgres yang disebut "DBNAME" Anda dapat mengaksesnya sebagai berikut (tergantung pada jaringan/pengaturan lokal Anda):
DATABASE_URL="postgres://%2Fvar%2Frun%2Fpostgresql/DBNAME"
DATABASE_URL=postgres://someuser:somepassword@somehost:5432/DBNAME
DATABASE_URL=postgres:///somedatabase
Perhatikan bahwa Anda juga perlu npm install @keyv/postgres jika Anda ingin menggunakan backend Postgres.
Untuk menggunakan database dan antarmuka nilai-kunci terlebih dahulu memerlukan modul basis data:
[sitefox.db :as db] Sekarang Anda dapat menggunakan db/kv untuk menulis nilai kunci ke "tabel" namespaced:
( let [table ( db/kv " sometable " )]
( .set table " key " " 42 " ))Ambil nilainya lagi:
( -> ( .get table " key " )
( .then ( fn [val] ( print val)))) Anda dapat menggunakan db/client untuk mengakses klien database yang mendasarinya. Misalnya membuat kueri terhadap database yang dikonfigurasi:
( let [c ( db/client )]
( -> ( .query c " select * from sometable WHERE x = 1 " )
( .then ( fn [rows] ( print rows)))))Sekali lagi, Promesa direkomendasikan untuk mengelola aliran kontrol selama operasi basis data.
Untuk mengeksplorasi data nilai-kunci dari baris perintah, gunakan SQLite dan JQ untuk memfilter data seperti ini:
sqlite3 database.sqlite "select * from keyv where key like 'SOMEPREFIX%';" | cut -f 2 -d "|" | jq '.'
Secara default modul node-sqlite3 tidak menyediakan jejak tumpukan penuh dengan nomor baris dll. Ketika kesalahan database terjadi. Dimungkinkan untuk menyalakan jejak tumpukan verbose dengan penalti kinerja kecil sebagai berikut:
( ns yourapp
( :require
[ " sqlite3 " :as sqlite3]))
( .verbose sqlite3) Jika Anda ingin menjalankan SQLite3 dalam produksi, Anda dapat mengalami kesalahan SQLITE_BUSY: database is locked saat melakukan operasi basis data simultan dari klien yang berbeda. Dimungkinkan untuk menyelesaikan masalah konkurensi dan penguncian ini dengan mengaktifkan mode logging write-Ahead di sqlite3 sebagai berikut:
(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))
Kode ini dapat ditempatkan dengan aman di fungsi utama kode server Anda.
Sesi diaktifkan secara default dan setiap pengunjung ke server Anda akan memiliki sesi sendiri. Data sesi adalah sisi server yang bertahan di seluruh muatan halaman sehingga Anda dapat menggunakannya untuk menyimpan status otentikasi misalnya. Sesi didukung ke tabel kv namespaced (lihat bagian database di atas). Anda dapat membaca dan menulis struktur data JS yang sewenang -wenang ke sesi menggunakan req.session .
Untuk menulis nilai ke toko sesi (di dalam fungsi penangan rute):
( let [session ( aget req " session " )]
( aset session " myvalue " 42 ))Untuk membaca nilai dari toko sesi:
( aget req " session " " myvalue " )Sitefox membungkus pustaka paspor untuk mengimplementasikan otentikasi. Anda dapat menambahkan otentikasi berbasis email dan kata sandi sederhana ke aplikasi Anda dengan tiga panggilan fungsi:
( 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 ... ;
)) String template yang dilewati adalah dokumen HTML dan "main" adalah pemilih yang menentukan tempat untuk memasang auth ui. Ini akan mengatur rute berikut secara default di mana Anda dapat mengirim pengguna untuk mendaftar, masuk, dan mengatur ulang kata sandi mereka:
/auth/sign-in/auth/sign-up/auth/reset-passwordDimungkinkan juga untuk mengganti formulir reagen AUTH UI default dan URL pengalihan untuk menyesuaikannya dengan versi Anda sendiri. Lihat dokumentasi auth untuk detail tentang cara menyediakan formulir reagen Anda sendiri. Juga lihat kode sumber untuk formulir Auth Reagen Default jika Anda ingin membuatnya sendiri.
Ketika pengguna mendaftarkan data mereka bertahan ke dalam database KEYV default yang digunakan oleh Sitefox. Anda dapat mengambil struktur data pengguna yang saat ini diautentikasi pada objek permintaan:
( let [user ( aget req " user " )] ...) Anda kemudian dapat memperbarui data pengguna dan menyimpan data mereka kembali ke database. Perpustakaan applied-science.js-interop nyaman untuk ini (diperlukan di sini sebagai j ):
( p/let [user ( aget req " user " )]
( j/assoc! user :somekey 42 )
( auth/save-user user)) Jika Anda ingin membuat tabel baru, ada berguna untuk menguncinya di UUID pengguna yang dapat Anda peroleh dengan (aget user "id") .
Lihat contoh otentikasi untuk lebih detail.
Untuk menambahkan skema otentikasi baru seperti nama pengguna, atau pihak ke -3 oAuth, berkonsultasi dengan dokumen paspor dan auth.cljs. Tarik permintaan yang paling disambut!
Bundel Sitefox Nodemailer karena mengirim email. Konfigurasikan server SMTP keluar Anda:
SMTP_SERVER=smtps://username:[email protected]/?pool=true
Maka Anda dapat menggunakan fungsi send-email sebagai berikut:
( -> ( 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)) Secara default, email terkirim dicatat ke ./logs/mail.log dalam format json-lines.
Jika Anda tidak menentukan server SMTP, modul email akan dalam mode debug. Tidak ada email yang akan dikirim, email keluar yang akan ditulis ke /tmp untuk diperiksa, dan hasil send-email juga akan dicatat ke konsol.
Jika Anda mengatur SMTP_SERVER=ethereal Layanan Ethereal.Email akan digunakan. Setelah menjalankan send-email Anda dapat mencetak properti url hasilnya. Anda dapat menggunakan tautan yang dicetak untuk menguji email Anda dalam mode dev.
Juga lihat Proyek Contoh Kirim-Email.
Lihat contoh validasi formulir yang menggunakan node-input-validator dan memeriksa masalah CSRF.
Untuk memastikan Anda dapat POST tanpa peringatan CSRF, Anda harus membuat elemen tersembunyi seperti ini (sintaks reagen):
[ :input { :name " _csrf " :type " hidden " :default-value ( .csrfToken req)}] Jika Anda membuat permintaan POST AJAX dari sisi klien, Anda harus melewati token CSRF sebagai header. Token yang valid tersedia sebagai string di titik akhir JSON /_csrf-token dan Anda dapat mengambilnya menggunakan fetch-csrf-token dan menambahkannya ke header permintaan pengambilan sebagai berikut:
( 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))})))) Catatan : Anda dapat mengambil token CSRF dari cookie sisi klien sebagai gantinya jika Anda mengatur variabel lingkungan SEND_CSRF_TOKEN . Ini adalah default dalam versi Sitefox sebelumnya. Saat diatur, Sitefox akan mengirim token pada setiap permintaan di Cookie Sisi Klien XSRF-TOKEN dan ini dapat diambil dengan fungsi ui/csrf-token . Ini adalah bentuk perlindungan CSRF yang valid, tetapi kurang aman.
Dalam beberapa keadaan yang jarang Anda mungkin ingin mematikan cek CSRF (misalnya memposting ke API dari perangkat non-browser). Jika Anda tahu apa yang Anda lakukan, Anda dapat menggunakan pre-csrf-router untuk menambahkan rute yang memotong pemeriksaan 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)) Secara default server web akan menulis untuk mencatat file di folder ./logs . File -file ini diputar secara otomatis oleh server. Ada dua jenis log:
logs/access.log yang merupakan log akses web standar dalam format "gabungan".logs/error.log di mana traceback ditulis menggunakan tracebacks/install-traceback-handler .Untuk mengirim pengecualian tanpa batas ke log kesalahan:
(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)
Buat build-id.txt berdasarkan komit git saat ini sebagai berikut:
git rev-parse HEAD | cut -b -8 > build-id.txt
Jika Anda ingin mendapatkan nomor baris ClojureScript yang benar di Tracebacks memerlukan ["source-maps-support" :as sourcemaps] dan kemudian:
(.install sourcemaps)
Anda dapat menggunakan fungsi web/setup-error-handler untuk melayani halaman untuk kesalahan tersebut berdasarkan komponen reagen yang Anda definisikan:
( 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)Anda dapat menggabungkan ini untuk menangkap 500 kesalahan server internal dan pengecualian yang tidak ada sebagai berikut:
(let [traceback-handler (install-traceback-handler admin-email build-id)]
(web/setup-error-handler app template-app "main" component-error-page traceback-handler))
Reload langsung didukung menggunakan nbb dan shadow-cljs . Diaktifkan secara default saat menggunakan ncript NPM Buat. Contoh memiliki detail lebih lanjut.
Sitefox dibuat oleh Chris McCormick (@MCCRMX di Twitter dan@[email protected] di Mastodon). Saya mengulanginya sambil membangun situs untuk diri saya sendiri dan untuk klien.