Веб -структура для Clojurescript на узле.

В традиции Джанго, Флайзы и Рейлс. Предназначен для инди -разработчиков, которые быстро отправляются. Битва проверена на реальных сайтах.
Философия | Быстрый старт | Документация | 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 - Настройка веб -сервера Port SiteFox привязан.BIND_ADDRESS - Настройте IP -адрес SiteFox веб -сервер, с которым связывается.SMTP_SERVER - Настройка исходящего SMTP -сервера EG 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
Примечание . Пользователям MC MAC может потребоваться установить версию Python в NPM, как это:
npm config set python python3
Это связано с тем, что сборка node-sqlite3 иногда не удается без настройки. Смотрите эту проблему для более подробной информации.
Пример сервера с двумя маршрутами, один из которых записывает значения в базу данных Value.
( 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 использует Express Web Server с разумными по умолчанию для сеансов и ведения журнала. Смотрите документацию по экспресс -маршрутизации для получения подробной информации.
Создайте новый сервер с 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 предлагает ярлыки для рендеринга на стороне сервера, объединенные документы 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 & Reagent:
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Также возможно переопределить формы реагента UI по умолчанию и URL -адреса перенаправления, чтобы настроить их с помощью ваших собственных версий. Посмотрите на документацию Auth для получения подробной информации о том, как предоставить свои собственные формы реагента. Также см. Исходный код для форм автоагента по умолчанию, если вы хотите сделать свой собственный.
Когда пользователь подписывает свои данные, сохраняется в базе данных Keyv по умолчанию, используемой SiteFox. Вы можете получить дата Datastructure пользователя в настоящее время в объекте запроса:
( 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 Custles 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.
Также см. Пример отправленного примера.
См. Пример проверки формы, в котором используется валидатор ввода узла и проверяет задачи 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 из Cookie Cookie на стороне клиента, если вы установите переменную среды SEND_CSRF_TOKEN . Это был дефолт в предыдущих версиях SiteFox. При установке SiteFox отправит токен по каждому запросу GET в Cookie Cookie на стороне клиента XSRF-TOKEN , и его можно получить с помощью функции 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)) По умолчанию веб -сервер будет писать для регистрации файлов в папке ./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))
Live Reloading поддерживается с использованием как nbb , так и shadow-cljs . По умолчанию он включен по умолчанию при использовании NPM Create Scripts. Примеры имеют более подробную информацию.
SiteFox был сделан Крисом Маккормиком (@mccrmx в Twitter и@[email protected] на Mastodon). Я повторил это, создавая сайты для себя и для клиентов.