summaryrefslogtreecommitdiff
path: root/bin/serve.fnl
diff options
context:
space:
mode:
authorunwox <me@unwox.com>2024-09-27 15:26:33 +0600
committerunwox <me@unwox.com>2024-09-27 15:44:16 +0600
commitdd449357f502dbe9ca4487d4b06a06ee4e597146 (patch)
tree9847488a6cc2c1aaf1fc80578e1a7a5d4af99ff5 /bin/serve.fnl
parent9b82db238f9e2e02a76f95c793f8d6ef2387ecfd (diff)
new structure
Diffstat (limited to 'bin/serve.fnl')
-rw-r--r--bin/serve.fnl166
1 files changed, 166 insertions, 0 deletions
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 "&lt;" "<")
+ (string.gsub "&gt;" ">")
+ (string.gsub "&quot;" "\"")
+ (string.gsub "&amp;" "&"))))
+
+(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 (.. "<h2>" (unescape product.title) "</h2>")]]
+ [: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 "₽ за&nbsp;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, '<i>', '</i>') 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/")