From dd449357f502dbe9ca4487d4b06a06ee4e597146 Mon Sep 17 00:00:00 2001 From: unwox Date: Fri, 27 Sep 2024 15:26:33 +0600 Subject: new structure --- bin/serve.fnl | 166 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 166 insertions(+) create mode 100644 bin/serve.fnl (limited to 'bin/serve.fnl') diff --git a/bin/serve.fnl b/bin/serve.fnl new file mode 100644 index 0000000..f4ef3c7 --- /dev/null +++ b/bin/serve.fnl @@ -0,0 +1,166 @@ +(import-macros {: map : reduce} :lib.macro) + +(tset package :path (.. package.path ";./vendor/lpeglj/?.lua")) + +(local io (require :io)) +(local math (require :math)) +(local fennel (require :vendor.fennel)) +(local html (require :vendor.html)) +(local json (require :vendor.json)) +(local array (require :lib.array)) +(local str (require :lib.string)) + +(local ozchai (require :parser.ozchai)) +(local ipuer (require :parser.ipuer)) +(local artoftea (require :parser.artoftea)) + +(when _G.unpack + (tset table :unpack _G.unpack)) + +(local db (luna.db.open "file:var/db.sqlite?_journal=WAL&_sync=NORMAL")) + +(local query-synonyms { + "шэн" "шен" + "шен" "шэн" + "доска" "чабань" + "чабань" "доска"}) + +(fn unescape [s] + (assert (= (type s) :string)) + (pick-values 1 + (-> s + (string.gsub "<" "<") + (string.gsub ">" ">") + (string.gsub """ "\"") + (string.gsub "&" "&")))) + +(fn site-name-template [name] + (if + (= name "ipuer.ru") + [:a {:class "site-icon" :href "https://ipuer.ru"} + [:img {:src "/static/ipuer.jpg"}] + "Институт чая пуэр"] + "")) + +(fn item-template [product] + [:div {:class "tile"} + [:a {:href product.url :style "display: block;"} + [:img {:src product.image} ""]] + (site-name-template product.site) + [:a {:href product.url :style "text-decoration: none;"} + [:NO-ESCAPE (.. "

" (unescape product.title) "

")]] + [:div {:class "price"} + (if product.price (.. product.price "₽") "") + (if product.quantity (.. " за " product.quantity "г") "") + (if (and product.price-per + (< 0 product.price-per)) + [:NO-ESCAPE (.. " (" product.price-per "₽ за 1г)")] + "")] + [:small {} (or product.description "")]]) + +(fn paginator-template [query page limit total] + (local last-page (math.ceil (/ total limit))) + + (if (< limit total) + [:div {:class "paginator"} + [:div {:class "paginator-numbers"} + (if (< 1 page) + [:a {:href (.. "?page=" (- page 1) "&query=" query)} "<"] + "") + (faccumulate [res [:span {}] i 1 last-page] + (do + (table.insert + res [:a {:href (.. "?page=" i "&query=" query) + :class (if (= page i) "paginator-active" "")} + (tostring i)]) + res)) + (if (< page last-page) + [:a {:href (.. "?page=" (+ page 1) "&query=" query)} ">"] + "")] + [:div {} "Всего результатов: " [:strong {} (string.format "%d" total)]]] + "")) + +(fn base-template [query sort page total ...] + (local paginator (paginator-template query page 32 total)) + + [:html {:lang "en"} + [:head {} + [:meta {:charset "UTF-8"}] + [:link {:rel :stylesheet :href "static/style.css"}] + [:title {} "A new cool web server for lua"]] + [:body {} + [:div {:class "container"} + [:div {:class "content"} + [:aside {:class "aside"} + [:div {:class "aside-content"} + [:a {:href "/" :style "display: block;"} + [:img {:class "logo" :src "static/logo.svg" :alt "Логотип meicha.ru"}]] + [:form {:class "form"} + [:input {:type :search :name :query :value query + :autofocus true :placeholder "enter search query"}] + [:button {:type :submit} "Искать"]] + paginator]] + [:section {} + [:div {:class "list"} ...] + [:footer {} paginator]]]]]]) + +(fn query-products [page query sorters] + (local query + (table.concat + (map (fn [_ q] + (if (. query-synonyms q) + (.. "(" q "* OR " (. query-synonyms q) "*)") + (.. q "*"))) + (str.split query)) + " ")) + (local total + (luna.db.query + db + "SELECT count(*) + FROM search + WHERE search.`table` = 'products' + AND search.name MATCH ?" + [query])) + + {:results + (luna.db.query* + db + "SELECT products.id, + highlight(search, 0, '', '') AS \"title\", + products.site, + products.description, + products.image, + products.url, + products.price, + products.weight, + products.price_per AS \"price-per\", + products.year + FROM search + INNER JOIN products ON search.fid = products.id + WHERE search.`table` = 'products' + AND search.name MATCH ? + ORDER BY rank + LIMIT 32 OFFSET ?" + [query (* (- page 1) 32)]) + :total (if (< 0 (# total)) + (. total 1 1) + 0)}) + +(fn root-handler [{: path : query}] + (if (= path "/") + (let [headers {:content-type "text/html"} + page (or (tonumber query.page) 1) + search (or query.query "") + sort "ASC" + {: results : total} (query-products page search sort)] + (values + 200 headers + (html.render + (base-template + search sort page total + (table.unpack (map #(item-template $2) results))) + true))) + (values 404 {} "not found"))) + +(luna.router.route "GET /" root-handler) +(luna.router.static "GET /static/" "static/") -- cgit v1.2.3