diff options
Diffstat (limited to 'main.fnl')
| -rw-r--r-- | main.fnl | 234 |
1 files changed, 234 insertions, 0 deletions
diff --git a/main.fnl b/main.fnl new file mode 100644 index 0000000..9282517 --- /dev/null +++ b/main.fnl @@ -0,0 +1,234 @@ +(import-macros {: map : reduce} :lib.macro) + +(tset package :path (.. package.path ";./lib/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 :site.ozchai)) +(local ipuer (require :site.ipuer)) +(local artoftea (require :site.artoftea)) + +(print (fennel.view (ipuer.products))) +(os.exit 1) + +(when _G.unpack + (tset table :unpack _G.unpack)) + +(local query-synonyms { + "шэн" "шен" + "шен" "шэн" + "доска" "чабань" + "чабань" "доска" +}) + +(local db (luna.db.open "file:db.sqlite?_journal=WAL&_sync=NORMAL")) +(luna.db.exec db " + PRAGMA foreign_keys=ON; + PRAGMA journal_mode=WAL; + PRAGMA synchronous=NORMAL; + + CREATE VIRTUAL TABLE IF NOT EXISTS search USING fts5(name, fid, `table`); + + CREATE TABLE IF NOT EXISTS products ( + id TEXT NOT NULL PRIMARY KEY, + site TEXT NOT NULL, + category TEXT NOT NULL, + title TEXT NOT NULL, + description TEXT NOT NULL, + year INT NOT NULL, + image TEXT NOT NULL, + url TEXT NOT NULL, + price REAL NOT NULL, + weight REAL NOT NULL, + price_per REAL NOT NULL, + misc TEXT NOT NULL, + creation_time DATETIME NOT NULL + );" []) + +(fn now [] + (os.date "%Y-%m-%d %H:%M:%S")) + +(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 (.. "<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 "₽ за 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 store-products [products] + (local sql + (.. "INSERT OR REPLACE INTO products VALUES " + (table.concat + (map (fn [_ _] + "(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)") + products) + ","))) + (local vars + (reduce + (fn [_ product rest] + (array.concat rest + [product.id + product.site + product.category + product.title + (or product.description "") + (or product.year 0) + (or product.image "") + (or product.url "") + (or product.price 0) + (or product.weight 0) + (or product.price-per 0) + (or product.misc "") + (now)])) + products [])) + (luna.db.exec db sql vars)) + +(fn populate-search-table [] + (local tx (luna.db.begin db)) + (luna.db.exec-tx tx "DELETE FROM search" []) + (luna.db.exec-tx tx "INSERT INTO search + SELECT title, id, 'products' FROM products;" []) + (luna.db.commit tx)) + +; (store-products (ipuer.products)) +; (store-products (ozchai.products)) +; (populate-search-table) + +(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/") |
