إطار الويب لـ ClojureScript على العقدة.

في تقليد Django ، قارورة ، وقضبان. مصممة للداخل المستقلين الذين يشحنون بسرعة. معركة تم اختبارها على مواقع حقيقية.
الفلسفة | بداية سريعة | الوثائق | 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 - تكوين خادم Web Sitefox Port.BIND_ADDRESS - تكوين عنوان IP Sitefox Web Server يرتبط به.SMTP_SERVER - تكوين خادم SMTP الصادر على سبيل المثال SMTP_SERVER=smtps://username:[email protected]/?pool=true .DATABASE_URL - قم بتكوين قاعدة البيانات للاتصال بـ. الإعدادات الافتراضية إلى sqlite://./database.sqlite . أسرع طريقة للبدء هي استخدام إحدى البرامج النصية create التي ستقوم بإعداد مشروع مثال لك بأمر واحد. إذا كنت تقوم ببناء موقع ويب بسيط دون الكثير من التفاعل في الواجهة الأمامية خارج تقديم النموذج ، فإن برنامج CRIPT CREPT NBB هو الطريق:
npm init sitefox-nbb mywebsite
سيؤدي ذلك إلى إنشاء مجلد يسمى mywebsite يحتوي على مشروعك الجديد. ملاحظة يمكنك استخدام Scittle لتشغيل CLJS من جانب العميل.
إذا كنت تقوم ببناء تطبيق ClojureScript الكامل ، فإن البرنامج النصي CRIPT SCRIPT هو الطريق:
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 إلى classpath بطريقة ما.
npm i sitefox
ملاحظة : قد يحتاج مستخدمو M1 Mac إلى تعيين إصدار Python في NPM مثل هذا:
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 خادم الويب Express مع افتراضيات معقولة للجلسات وتسجيلها. راجع وثائق التوجيه السريع للحصول على التفاصيل.
قم بإنشاء خادم جديد يحتوي على web/start وإعداد مسار يستجيب بـ "Hello World!" على النحو التالي:
( -> ( web/start )
( .then ( fn [app host port]
( .get app " /myroute "
( fn [req res]
( .send res " Hello world! " )))) يأتي SiteFox مع نظام اختياري لإعادة تحميل الطرق عند تغيير الخادم. سيتم إعادة تحميل طرقك السريعة في كل مرة يتم فيها تحديث رمز الخادم الخاص بك (على سبيل المثال عن طريق بناء Shadow-Cljs). في هذا المثال ، سيتم استدعاء 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 اختصارات لتقديم كاشف جانب الخادم ، مستندات 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 & Congent:
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 المتغير للبيئة. على سبيل المثال ، لاستخدام قاعدة بيانات postgres تسمى "dbname" يمكنك الوصول إليها على النحو التالي (اعتمادًا على الشبكات/الإعداد المحلي):
DATABASE_URL="postgres://%2Fvar%2Frun%2Fpostgresql/DBNAME"
DATABASE_URL=postgres://someuser:somepassword@somehost:5432/DBNAME
DATABASE_URL=postgres:///somedatabase
لاحظ أنك ستحتاج أيضًا إلى npm install @keyv/postgres إذا كنت ترغب في استخدام الواجهة الخلفية 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 مسمى (انظر قسم قاعدة البيانات أعلاه). يمكنك قراءة وكتابة هياكل بيانات JS التعسفية إلى الجلسة باستخدام req.session .
لكتابة قيمة إلى متجر الجلسة (داخل وظيفة معالج الطريق):
( 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" هو المحدد الذي يحدد مكان تركيب واجهة المستخدم Auth. سيقوم هذا بإعداد الطرق التالية افتراضيًا حيث يمكنك إرسال المستخدمين للتسجيل وتسجيل الدخول وإعادة ضبط كلمة المرور الخاصة بهم:
/auth/sign-in/auth/sign-up/auth/reset-passwordمن الممكن أيضًا تجاوز نماذج كاشف واجهة المستخدم الافتراضية وأعداد URL لإعادة توجيه تخصيصها مع الإصدارات الخاصة بك. راجع وثائق المصادقة للحصول على تفاصيل حول كيفية توفير نماذج الكاشف الخاصة بك. راجع أيضًا الرمز المصدري لنماذج مصادقة الكاشف الافتراضية إذا كنت ترغب في صنع خاص بك.
عندما يتم تسجيل المستخدم لبياناته في قاعدة بيانات KEYV الافتراضية المستخدمة بواسطة SiteFox. يمكنك استرداد بنية بيانات المستخدم المصادقة عليها حاليًا على كائن الطلب:
( 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 الطرف الثالث ، استشر مستندات جواز السفر و 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)) بشكل افتراضي ، يتم تسجيل رسائل البريد الإلكتروني المرسلة إلى ./logs/mail.log بتنسيق JSON-Lines.
إذا لم تحدد خادم SMTP ، فستكون وحدة البريد الإلكتروني في وضع التصحيح. لن يتم إرسال أي رسائل بريد إلكتروني ، وسيتم كتابة رسائل البريد الإلكتروني الصادرة إلى /tmp للتفتيش ، وسيتم أيضًا تسجيل نتائج send-email إلى وحدة التحكم.
إذا قمت بتعيين SMTP_SERVER=ethereal فسيتم استخدام خدمة Ethereal.email. بعد تشغيل send-email يمكنك طباعة خاصية url للنتيجة. يمكنك استخدام الروابط المطبوعة لاختبار رسائل البريد الإلكتروني الخاصة بك في وضع dev.
انظر أيضًا مشروع مثال Send-Email.
راجع مثال التحقق من صحة النموذج الذي يستخدم العقدة الفاخرة والتحقق من مشاكل CSRF.
للتأكد من أنه يمكنك POST بدون تحذيرات CSRF ، يجب عليك إنشاء عنصر مخفي مثل هذا (بناء جملة الكاشف):
[ :input { :name " _csrf " :type " hidden " :default-value ( .csrfToken req)}] إذا كنت تقوم بتقديم طلب POST Ajax من جانب العميل ، فيجب عليك تمرير رمز CSRF كرأس. يتوفر الرمز المميز الصحيح كسلسلة في نقطة نهاية JSON /_csrf-token ويمكنك جلبها باستخدام 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))})))) ملاحظة : يمكنك جلب رمز CSRF من ملف تعريف الارتباط جانب العميل بدلاً من ذلك إذا قمت بتعيين متغير البيئة SEND_CSRF_TOKEN . كان هذا هو الافتراضي في إصدارات SiteFox السابقة. عند التعيين ، سترسل SiteFox الرمز المميز لكل طلب Get في جانب العميل Cookie XSRF-TOKEN ويمكن استرداد هذا باستخدام وظيفة ui/csrf-token . هذا شكل صالح ، ولكنه أقل أمانًا من حماية CSRF.
في بعض الظروف النادرة ، قد ترغب في إيقاف تشغيل الشيكات CSRF (على سبيل المثال النشر إلى واجهة برمجة تطبيقات من جهاز غير متفرد). إذا كنت تعرف ما تفعله ، فيمكنك استخدام 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)) بشكل افتراضي ، سيقوم خادم الويب بكتابة لتسجيل الملفات في مجلد ./logs . يتم تدوير هذه الملفات تلقائيًا بواسطة الخادم. هناك نوعان من السجلات:
logs/access.log التي هي سجلات الوصول إلى الويب القياسية بتنسيق "مجتمعة".logs/error.log حيث تتم كتابة Tracebacks باستخدام 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)
إنشاء build-id.txt استنادًا إلى الالتزام الحالي GIT على النحو التالي:
git rev-parse HEAD | cut -b -8 > build-id.txt
إذا كنت ترغب في الحصول على أرقام سطر ClojureScript الصحيحة في Tracebacks تتطلب ["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 بواسطة كريس ماكورميك (mccrmx على Twitter و@[email protected] على mastodon). لقد تكررت عليه أثناء بناء مواقع لنفسي ولعملاء.