summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorTim <contact@bytim.eu>2025-06-14 11:49:28 +0200
committerTim <contact@bytim.eu>2025-06-14 11:49:28 +0200
commit8e23d9dade945f87f5fc7fb15042a53a7eeb9a9e (patch)
treec0f5b6587b6f9f0b591b395c69ad7da08717a30b /src
parent229299146376a2b847f4fe3f331efbd26c0abc70 (diff)
downloadchef-8e23d9dade945f87f5fc7fb15042a53a7eeb9a9e.tar.xz
chef-8e23d9dade945f87f5fc7fb15042a53a7eeb9a9e.zip
Refactor project structure
Diffstat (limited to 'src')
-rw-r--r--src/chef/api/admin/category.clj35
-rw-r--r--src/chef/api/admin/recipe.clj45
-rw-r--r--src/chef/components/search.clj49
-rw-r--r--src/chef/frontend/admin.clj (renamed from src/chef/pages/admin.clj)55
-rw-r--r--src/chef/frontend/admin/recipe_editor.clj (renamed from src/chef/pages/admin/recipe_editor.clj)30
-rw-r--r--src/chef/frontend/visitor/home.clj (renamed from src/chef/pages/home.clj)28
-rw-r--r--src/chef/frontend/visitor/recipe.clj (renamed from src/chef/pages/recipe.clj)46
-rw-r--r--src/chef/frontend/visitor/recipe/thumbnail.clj10
-rw-r--r--src/chef/frontend/visitor/search.clj38
-rw-r--r--src/chef/logic/categories.clj72
-rw-r--r--src/chef/logic/recipes.clj60
-rw-r--r--src/chef/pages/admin/api.clj126
-rw-r--r--src/chef/routes.clj56
-rw-r--r--src/chef/utils.clj37
14 files changed, 347 insertions, 340 deletions
diff --git a/src/chef/api/admin/category.clj b/src/chef/api/admin/category.clj
new file mode 100644
index 0000000..2d11510
--- /dev/null
+++ b/src/chef/api/admin/category.clj
@@ -0,0 +1,35 @@
+(ns chef.api.admin.category
+ (:require [chef.utils :as cutils]
+ [chef.logic.categories :as clcategories]
+ [ring.util.response :as ruresp]))
+
+;; POST /
+(defn handle-edit [req]
+ (cutils/auth-only req
+ (if-let [id (cutils/s->int-or-nil (get-in req [:path-params :id]))]
+ (do (clcategories/update-category! id (merge {}
+ (when-let [name (get-in req [:params "name"])]
+ {:name name})
+ (when-let [question (get-in req [:params "question"])]
+ {:question question})))
+ (ruresp/response "Updated."))
+ (ruresp/bad-request "Bad request."))))
+
+;; DELETE /
+(defn handle-delete [req]
+ (cutils/auth-only req
+ (let [id (cutils/s->int-or-nil (get-in req [:path-params :id]))]
+ (if (and (some? id)
+ (not= id -1))
+ (do (clcategories/delete-category-and-children! id)
+ (-> (ruresp/response "Deleted.")
+ (ruresp/header "HX-Refresh" "true")))
+ (ruresp/bad-request "Bad request.")))))
+
+;; POST /create
+(defn handle-create [req]
+ (cutils/auth-only req
+ (clcategories/create-category! (or (get-in req [:params "parent"])
+ -1))
+ (-> (ruresp/created "Created.")
+ (ruresp/header "HX-Refresh" "true"))))
diff --git a/src/chef/api/admin/recipe.clj b/src/chef/api/admin/recipe.clj
new file mode 100644
index 0000000..c69ade5
--- /dev/null
+++ b/src/chef/api/admin/recipe.clj
@@ -0,0 +1,45 @@
+(ns chef.api.admin.recipe
+ (:require [chef.utils :as cutils]
+ [chef.logic.recipes :as clrecipes]
+ [ring.util.response :as ruresp]))
+
+;; POST /
+(defn handle-edit [req]
+ (cutils/auth-only req
+ (let [id (cutils/s->int-or-nil (get-in req [:path-params :id]))
+ ingredients (get-in req [:params "ingredients"])]
+ (if (and (some? id)
+ (cutils/valid-ingredients? ingredients))
+ (do (when-let [thumbnail (get-in req [:params "thumbnail"])]
+ (clrecipes/set-recipe-thumbnail! id thumbnail))
+ (clrecipes/update-recipe! id {:title (get-in req [:params "title"])
+ :category (get-in req [:params "category"])
+ :unit (get-in req [:params "ingredients-unit"])
+ :ingredients ingredients
+ :preparation (get-in req [:params "preparation"])})
+ (ruresp/response "Saved."))
+ (ruresp/bad-request "Bad request.")))))
+
+;; DELETE /
+(defn handle-delete [req]
+ (cutils/auth-only req
+ (if-let [id (cutils/s->int-or-nil (get-in req [:path-params :id]))]
+ (do (clrecipes/delete-recipe! id)
+ (-> (ruresp/response "Deleted.")
+ (ruresp/header "HX-Refresh" "true")))
+ (ruresp/bad-request "Bad request."))))
+
+;; POST /create
+(defn handle-create [req]
+ (cutils/auth-only req
+ (clrecipes/create-recipe!)
+ (-> (ruresp/created "Created.")
+ (ruresp/header "HX-Refresh" "true"))))
+
+;; DELETE /thumbnail
+(defn handle-delete-thumbnail [req]
+ (cutils/auth-only req
+ (if-let [id (cutils/s->int-or-nil (get-in req [:path-params :id]))]
+ (do (clrecipes/remove-recipe-thumbnail! id)
+ (ruresp/response "Done."))
+ (ruresp/bad-request "Bad request."))))
diff --git a/src/chef/components/search.clj b/src/chef/components/search.clj
deleted file mode 100644
index e428a23..0000000
--- a/src/chef/components/search.clj
+++ /dev/null
@@ -1,49 +0,0 @@
-(ns chef.components.search
- (:require [chef.database :as cdb]
- [chef.utils :as cutils]
- [clojure.string :as cstr]
- [hiccup2.core :as html]
- [honey.sql :as sql]
- [next.jdbc :as jdbc]
- [ring.util.response :as ruresp]))
-
-(defn render [query category]
- [:table
- [:tr
- [:th "Rezept"]
- [:th "Kategorie"]]
- (for [recipe (jdbc/execute! @cdb/db
- (sql/format {:select [:*]
- :from [:recipes]}))
- :let [recipe-category (->> {:select [:*]
- :from [:categories]
- :where [:= :id (:recipes/category recipe)]}
- sql/format
- (jdbc/execute! @cdb/db)
- first)]]
- (when (or (= category -1)
- (and (cstr/includes? (-> recipe
- :recipes/title
- cstr/lower-case)
- query)
- (some #(= (:categories/id %) category)
- (cutils/category-parents recipe-category))))
- [:tr
- [:td
- [:b [:a {:href (str "/recipes/" (:recipes/id recipe))} (:recipes/title recipe)]]]
- [:td
- (cutils/category-path (->> {:select [:*]
- :from [:categories]
- :where [:= :id (:recipes/category recipe)]}
- sql/format
- (jdbc/execute! @cdb/db)
- first))]]))])
-
-(defn handler [req]
- (if-let [query (get-in req [:params "query"])]
- (-> (render query (try (Integer/parseInt (get-in req [:params "category"]))
- (catch Exception _ -1)))
- html/html
- str
- ruresp/response)
- (ruresp/bad-request "No search query provide.")))
diff --git a/src/chef/pages/admin.clj b/src/chef/frontend/admin.clj
index ea19cc3..89a2462 100644
--- a/src/chef/pages/admin.clj
+++ b/src/chef/frontend/admin.clj
@@ -1,10 +1,10 @@
-(ns chef.pages.admin
+(ns chef.frontend.admin
(:require [chef.utils :as cutils]
[hiccup2.core :as html]
[ring.util.response :as ruresp]
- [chef.database :as cdb]
- [next.jdbc :as jdbc]
- [honey.sql :as sql]))
+
+ [chef.logic.categories :as clcategories]
+ [chef.logic.recipes :as clrecipes]))
(defn- render-category [data children]
[:li
@@ -13,14 +13,12 @@
[:input {:type :text :placeholder "Name"
:value (:categories/name data)
:name "name"
- :hx-post (str "/api/admin/edit-category/" (:categories/id data))
+ :hx-post (str "/api/admin/category/" (:categories/id data))
:hx-trigger "change"}]
"Startseite")]
(when (or (neg? (:categories/id data))
- (->> (sql/format {:select [:*]
- :from [:categories]
- :where [:= :parent (:categories/id data)]})
- (jdbc/execute! @cdb/db)
+ (->> (:categories/id data)
+ clcategories/find-categories-with-parent
count
pos?))
(list [:p {:style {:display :inline-block
@@ -31,26 +29,25 @@
:width :auto}
:value (:categories/question data)
:name "question"
- :hx-post (str "/api/admin/edit-category/" (:categories/id data))
+ :hx-post (str "/api/admin/category/" (:categories/id data))
:hx-trigger "change"}]))
[:img {:src "/static/icons/plus.svg" :height "30em"
:style {:vertical-align :middle
:margin-left "1em"}
- :hx-post (str "/api/admin/create-category"
+ :hx-post (str "/api/admin/category/create/"
(when (pos? (:categories/id data)) (str "?parent=" (:categories/id data))))
:hx-swap "none"}]
(when (pos? (:categories/id data))
[:img {:src "/static/icons/trash.svg" :height "30em"
:style {:vertical-align :middle
:margin-left "1em"}
- :hx-delete (str "/api/admin/delete-category/" (:categories/id data))
+ :hx-delete (str "/api/admin/category/" (:categories/id data))
:hx-swap "none"}])
[:ul
(for [child children]
- (render-category child (->> (sql/format {:select [:*]
- :from [:categories]
- :where [:= :parent (:categories/id child)]})
- (jdbc/execute! @cdb/db))))]])
+ (->> (:categories/id child)
+ clcategories/find-categories-with-parent
+ (render-category child)))]])
(defn- render-recipe-table-row [recipe]
(let [tr-id (str "recipe-" (:recipes/id recipe))]
@@ -58,12 +55,8 @@
[:td
[:p (:recipes/title recipe)]]
[:td
- (let [category (->> (sql/format {:select [:*]
- :from [:categories]
- :where [:= :id (:recipes/category recipe)]})
- (jdbc/execute! @cdb/db)
- first)]
- [:p (cutils/category-path category)])]
+ (let [category (clcategories/get-category (:recipes/category recipe))]
+ [:p (clcategories/generate-path category)])]
[:td
[:div
[:button {:class ["button" "primary"]
@@ -74,7 +67,7 @@
[:button {:class ["button error"]
:hx-trigger "click"
:hx-swap :none
- :hx-delete (str "/api/admin/delete-recipe/" (:recipes/id recipe))}
+ :hx-delete (str "/api/admin/recipe/" (:recipes/id recipe))}
"Löschen"]]]]))
(defn- render []
@@ -83,28 +76,20 @@
[:h1 "chef - Admin"]
[:h2 "Kategorien"]
[:ul
- (render-category (first (->> (sql/format {:select [:*]
- :from [:categories]
- :where [:= :id -1]})
- (jdbc/execute! @cdb/db)))
- (->> (sql/format {:select [:*]
- :from [:categories]
- :where [:= :parent -1]})
- (jdbc/execute! @cdb/db)))]
+ (render-category (clcategories/get-category -1)
+ (clcategories/find-categories-with-parent -1))]
[:h2 "Rezepte"]
[:table
[:tr
[:th "Titel"]
[:th "Kategorie"]
[:th "Aktionen"]]
- (for [recipe (jdbc/execute! @cdb/db
- (sql/format {:select [:*]
- :from [:recipes]}))]
+ (for [recipe (clrecipes/get-all-recipes)]
(render-recipe-table-row recipe))]
[:button {:class "button primary"
:hx-trigger "click"
:hx-swap :none
- :hx-post "/api/admin/create-recipe"}
+ :hx-post "/api/admin/recipe/create/"}
"Rezept erstellen"]]))
(defn handler [req]
diff --git a/src/chef/pages/admin/recipe_editor.clj b/src/chef/frontend/admin/recipe_editor.clj
index cc30942..69c84ba 100644
--- a/src/chef/pages/admin/recipe_editor.clj
+++ b/src/chef/frontend/admin/recipe_editor.clj
@@ -1,18 +1,18 @@
-(ns chef.pages.admin.recipe-editor
+(ns chef.frontend.admin.recipe-editor
(:require [hiccup2.core :as html]
[hiccup.util :as hutil]
- [honey.sql :as sql]
- [next.jdbc :as jdbc]
- [chef.database :as cdb]
[ring.util.response :as ruresp]
- [chef.utils :as cutils]))
+ [chef.utils :as cutils]
+
+ [chef.logic.categories :as clcategories]
+ [chef.logic.recipes :as clrecipes]))
(defn render [recipe]
(cutils/gen-page "chef - Rezept bearbeiten"
[:div {:style {:margin-left "1em"}}
[:h1 "Rezept bearbeiten"]
[:form {:style {:width "50%"}
- :hx-post (str "/api/admin/edit-recipe/" (:recipes/id recipe))
+ :hx-post (str "/api/admin/recipe/" (:recipes/id recipe))
:hx-swap "none"
:enctype "multipart/form-data"}
[:input {:type :text :name "title" :placeholder "Titel"
@@ -24,18 +24,15 @@
:padding "0.3em"}}]]
[:button {:class ["button" "error"]
:hx-trigger "click"
- :hx-delete (str "/api/admin/delete-thumbnail/" (:recipes/id recipe))
+ :hx-delete (str "/api/admin/recipe/" (:recipes/id recipe) "/thumbnail/")
:hx-swap :none}
"Thumbnail entfernen"]
[:h2 "Kategorie"]
[:select {:name "category"}
- (for [category (->> (sql/format {:select [:*]
- :from [:categories]})
- (jdbc/execute! @cdb/db)
- (filter #(pos? (:categories/id %))))]
+ (for [category (clcategories/get-all-categories)]
[:option {:value (:categories/id category)
:selected (= (:categories/id category) (:recipes/category recipe))}
- (cutils/category-path category)])]
+ (clcategories/generate-path category)])]
[:h2 "Zutaten"]
[:div {:style {:display :flex}}
[:p {:style {:margin-right "0.5em"}} "Pro"]
@@ -57,13 +54,8 @@
(defn handler [req]
(cutils/auth-only req
- (if-let [id (try (Integer/parseInt (get-in req [:path-params :id]))
- (catch Exception _ nil))]
- (->> (sql/format {:select [:*]
- :from [:recipes]
- :where [:= :id id]})
- (jdbc/execute! @cdb/db)
- first
+ (if-let [id (cutils/s->int-or-nil (get-in req [:path-params :id]))]
+ (->> (clrecipes/get-recipe id)
render
html/html
str
diff --git a/src/chef/pages/home.clj b/src/chef/frontend/visitor/home.clj
index 9bc82fb..6503983 100644
--- a/src/chef/pages/home.clj
+++ b/src/chef/frontend/visitor/home.clj
@@ -1,38 +1,28 @@
-(ns chef.pages.home
- (:require [chef.database :as cdb]
- [hiccup2.core :as html]
- [honey.sql :as sql]
- [next.jdbc :as jdbc]
+(ns chef.frontend.visitor.home
+ (:require [hiccup2.core :as html]
[ring.util.response :as ruresp]
[chef.utils :as cutils]
- [chef.components.search :as ccsearch]))
+ [chef.frontend.visitor.search :as cfvsearch]
+
+ [chef.logic.categories :as clcategories]))
(defn- render [req]
(cutils/gen-page "chef"
- (let [category (->> {:select [:*]
- :from [:categories]
- :where [:= :id (or (get-in req [:params "category"]) -1)]}
- sql/format
- (jdbc/execute! @cdb/db)
- first)]
+ (let [category (clcategories/get-category (or (get-in req [:params "category"]) -1))]
[:div {:style {:text-align :center}}
[:h1 "chef"]
[:h2 "Finde das perfekte Gericht für dich!"]
[:b (:categories/question category)]
[:div
- (for [child-category (->> {:select [:*]
- :from [:categories]
- :where [:= :parent (:categories/id category)]}
- sql/format
- (jdbc/execute! @cdb/db))]
+ (for [child-category (clcategories/find-categories-with-parent (:categories/id category))]
[:div
[:button {:style {:margin-bottom "1em"}
:onclick (str "window.location = \"/?category=" (:categories/id child-category) "\"")}
(:categories/name child-category)]
[:br]])]
(when (pos? (:categories/id category))
- [:h3 (cutils/category-path category)])
+ [:h3 (clcategories/generate-path category)])
[:input {:type :text
:style {:width "90%" :margin :auto}
:placeholder "Suche"
@@ -41,7 +31,7 @@
:hx-swap "innerHTML"
:hx-target "#search-results"}]
[:div {:id "search-results"}
- (ccsearch/render "" (:categories/id category))]])))
+ (cfvsearch/render "" (:categories/id category))]])))
(defn handler [req]
(-> req
diff --git a/src/chef/pages/recipe.clj b/src/chef/frontend/visitor/recipe.clj
index be88b2d..e3dbf97 100644
--- a/src/chef/pages/recipe.clj
+++ b/src/chef/frontend/visitor/recipe.clj
@@ -1,12 +1,11 @@
-(ns chef.pages.recipe
- (:require [chef.database :as cdb]
- [chef.utils :as cutils]
+(ns chef.frontend.visitor.recipe
+ (:require [chef.utils :as cutils]
[clojure.string :as cstr]
[hiccup2.core :as html]
- [honey.sql :as sql]
- [next.jdbc :as jdbc]
- [ring.util.response :as ruresp])
- (:import java.io.File))
+ [ring.util.response :as ruresp]
+
+ [chef.logic.recipes :as clrecipes]
+ [chef.logic.categories :as clcategories]))
(defn- render [portions recipe]
(cutils/gen-page (str "chef - " (:recipes/title recipe))
@@ -15,13 +14,10 @@
[:h1 {:style {:display :inline-block
:margin-right "0.5em"}}
(:recipes/title recipe)]
- [:i (str "(" (cutils/category-path (->> {:select [:*]
- :from [:categories]
- :where [:= :id (:recipes/category recipe)]}
- sql/format
- (jdbc/execute! @cdb/db)
- first)) ")")]]
- (when (some? (cutils/get-thumbnail-file recipe))
+ [:i (str "(" (-> (:recipes/category recipe)
+ clcategories/get-category
+ clcategories/generate-path) ")")]]
+ (when (some? (clrecipes/get-recipe-thumbnail (:recipes/id recipe)))
[:img {:src (str "/recipes/" (:recipes/id recipe) "/thumbnail")
:width "50%"}])
[:h2
@@ -51,27 +47,9 @@
[:p %])))]))
(defn handler [req]
- (->> {:select [:*]
- :from [:recipes]
- :where [:= :id (get-in req [:path-params :id])]}
- sql/format
- (jdbc/execute! @cdb/db)
- first
- (render (or (try (Integer/parseInt (get-in req [:params "portions"]))
- (catch Exception _ nil))
+ (->> (clrecipes/get-recipe (get-in req [:path-params :id]))
+ (render (or (cutils/s->int-or-nil (get-in req [:params "portions"]))
1))
html/html
str
ruresp/response))
-
-(defn thumbnail-handler [req]
- (if-let [id (get-in req [:path-params :id])]
- (when-let [thumbnail-file (->> {:select [:*]
- :from [:recipes]
- :where [:= :id id]}
- sql/format
- (jdbc/execute! @cdb/db)
- first
- cutils/get-thumbnail-file)]
- (ruresp/file-response (.getPath ^File thumbnail-file)))
- (ruresp/bad-request "Bad request.")))
diff --git a/src/chef/frontend/visitor/recipe/thumbnail.clj b/src/chef/frontend/visitor/recipe/thumbnail.clj
new file mode 100644
index 0000000..c14f491
--- /dev/null
+++ b/src/chef/frontend/visitor/recipe/thumbnail.clj
@@ -0,0 +1,10 @@
+(ns chef.frontend.visitor.recipe.thumbnail
+ (:require [ring.util.response :as ruresp]
+ [chef.logic.recipes :as clrecipes])
+ (:import java.io.File))
+
+(defn handler [req]
+ (if-let [id (get-in req [:path-params :id])]
+ (when-let [thumbnail-file (clrecipes/get-recipe-thumbnail id)]
+ (ruresp/file-response (.getPath ^File thumbnail-file)))
+ (ruresp/bad-request "Bad request.")))
diff --git a/src/chef/frontend/visitor/search.clj b/src/chef/frontend/visitor/search.clj
new file mode 100644
index 0000000..7a2db93
--- /dev/null
+++ b/src/chef/frontend/visitor/search.clj
@@ -0,0 +1,38 @@
+(ns chef.frontend.visitor.search
+ (:require [chef.utils :as cutils]
+ [clojure.string :as cstr]
+ [hiccup2.core :as html]
+ [ring.util.response :as ruresp]
+
+ [chef.logic.categories :as clcategories]
+ [chef.logic.recipes :as clrecipes]))
+
+(defn render [query category]
+ [:table
+ [:tr
+ [:th "Rezept"]
+ [:th "Kategorie"]]
+ (for [recipe (clrecipes/get-all-recipes)
+ :let [recipe-category (clcategories/get-category (:recipes/category recipe))]]
+ (when (or (= category -1)
+ (and (cstr/includes? (-> recipe
+ :recipes/title
+ cstr/lower-case)
+ query)
+ (some #(= (:categories/id %) category)
+ (clcategories/get-parents recipe-category))))
+ [:tr
+ [:td
+ [:b [:a {:href (str "/recipes/" (:recipes/id recipe))} (:recipes/title recipe)]]]
+ [:td
+ (-> (:recipes/category recipe)
+ clcategories/get-category
+ clcategories/generate-path)]]))])
+
+(defn handler [req]
+ (if-let [query (get-in req [:params "query"])]
+ (-> (render query (or (cutils/s->int-or-nil (get-in req [:params "category"])) -1))
+ html/html
+ str
+ ruresp/response)
+ (ruresp/bad-request "No search query provide.")))
diff --git a/src/chef/logic/categories.clj b/src/chef/logic/categories.clj
new file mode 100644
index 0000000..aa9931e
--- /dev/null
+++ b/src/chef/logic/categories.clj
@@ -0,0 +1,72 @@
+(ns chef.logic.categories
+ (:require [clojure.string :as cstr]
+ [honey.sql :as sql]
+ [next.jdbc :as jdbc]
+ [chef.database :as cdb]))
+
+(defn get-all-categories []
+ (->> {:select [:*]
+ :from [:categories]}
+ sql/format
+ (jdbc/execute! @cdb/db)
+ (filter #(pos? (:categories/id %))) ; Filter out root category
+ ))
+
+(defn get-category [id]
+ (->> {:select [:*]
+ :from [:categories]
+ :where [:= :id id]}
+ sql/format
+ (jdbc/execute! @cdb/db)
+ first))
+
+(defn find-categories-with-parent [parent-id]
+ (->> {:select [:*]
+ :from [:categories]
+ :where [:= :parent parent-id]}
+ sql/format
+ (jdbc/execute! @cdb/db)))
+
+(defn create-category! [parent]
+ (->> {:insert-into [:categories]
+ :values [{:name "New category"
+ :parent parent}]}
+ sql/format
+ (jdbc/execute! @cdb/db)))
+
+(defn- delete-category! [id]
+ (->> {:delete-from [:categories]
+ :where [:= :id id]}
+ sql/format
+ (jdbc/execute! @cdb/db)))
+
+(defn- delete-category-children! [id]
+ (doseq [child (find-categories-with-parent id)]
+ (delete-category! (:categories/id child))
+ (delete-category-children! (:categories/id child))))
+
+(defn delete-category-and-children! [id]
+ (delete-category! id)
+ (delete-category-children! id))
+
+(defn update-category! [id updates]
+ (->> {:update :categories
+ :set updates
+ :where [:= :id id]}
+ sql/format
+ (jdbc/execute! @cdb/db)))
+
+(defn get-parents [category]
+ (loop [parents (list)
+ category category]
+ (let [updated-parents (conj parents category)]
+ (if (not= -1 (:categories/parent category))
+ (recur updated-parents
+ (get-category (:categories/parent category)))
+ updated-parents))))
+
+(defn generate-path [category]
+ (->> category
+ get-parents
+ (map #(:categories/name %))
+ (cstr/join " > ")))
diff --git a/src/chef/logic/recipes.clj b/src/chef/logic/recipes.clj
new file mode 100644
index 0000000..1fd1db5
--- /dev/null
+++ b/src/chef/logic/recipes.clj
@@ -0,0 +1,60 @@
+(ns chef.logic.recipes
+ (:require [clojure.java.io :as cjio]
+ [clojure.string :as cstr]
+ [honey.sql :as sql]
+ [next.jdbc :as jdbc]
+ [chef.database :as cdb])
+ (:import java.io.File))
+
+(defn get-all-recipes []
+ (->> {:select [:*]
+ :from [:recipes]}
+ sql/format
+ (jdbc/execute! @cdb/db)))
+
+(defn get-recipe [id]
+ (->> {:select [:*]
+ :from [:recipes]
+ :where [:= :id id]}
+ sql/format
+ (jdbc/execute! @cdb/db)
+ first))
+
+(defn create-recipe! []
+ (->> {:insert-into [:recipes]
+ :values [{:title "New recipe"}]}
+ sql/format
+ (jdbc/execute! @cdb/db)))
+
+(defn delete-recipe! [id]
+ (->> {:delete-from [:recipes]
+ :where [:= :id id]}
+ sql/format
+ (jdbc/execute! @cdb/db)))
+
+(defn update-recipe! [id updates]
+ (->> {:update :recipes
+ :set updates
+ :where [:= :id id]}
+ sql/format
+ (jdbc/execute! @cdb/db)))
+
+(defn get-recipe-thumbnail [recipe-id]
+ (let [thumbnails-folder (File. "./thumbnails/")]
+ (->> thumbnails-folder
+ .listFiles
+ (filter #(cstr/starts-with? (.getName ^File %)
+ (str recipe-id ".")))
+ first)))
+
+(defn remove-recipe-thumbnail! [id]
+ (when-let [file (get-recipe-thumbnail id)]
+ (.delete ^File file)))
+
+(defn set-recipe-thumbnail! [id multipart-param]
+ (when-let [existing-thumbnail-file (get-recipe-thumbnail id)]
+ (.delete ^File existing-thumbnail-file))
+ (cjio/copy (:tempfile multipart-param)
+ (File. (str "./thumbnails/" id "." (-> (:filename multipart-param)
+ (cstr/split #"\.")
+ last)))))
diff --git a/src/chef/pages/admin/api.clj b/src/chef/pages/admin/api.clj
deleted file mode 100644
index 1119607..0000000
--- a/src/chef/pages/admin/api.clj
+++ /dev/null
@@ -1,126 +0,0 @@
-(ns chef.pages.admin.api
- (:require [chef.utils :as cutils]
- [chef.database :as cdb]
- [clojure.string :as cstr]
- [next.jdbc :as jdbc]
- [honey.sql :as sql]
- [ring.util.response :as ruresp]
- [clojure.java.io :as cjio])
- (:import java.io.File))
-
-(defn create-category [req]
- (cutils/auth-only req
- (jdbc/execute! @cdb/db
- (sql/format {:insert-into [:categories]
- :values [(merge {:name "New category"
- :parent (or (get-in req [:params "parent"])
- -1)})]}))
- (-> (ruresp/created "Created.")
- (ruresp/header "HX-Refresh" "true"))))
-
-(defn- delete-category-children! [id]
- (let [children (->> (sql/format {:select [:*]
- :from [:categories]
- :where [:= :parent id]})
- (jdbc/execute! @cdb/db)
- (map #(:categories/id %)))]
- (doseq [child children]
- (jdbc/execute! @cdb/db
- (sql/format {:delete-from [:categories]
- :where [:= :id child]}))
- (delete-category-children! child))))
-
-(defn delete-category [req]
- (cutils/auth-only req
- (if-let [id (try (Integer/parseInt (get-in req [:path-params :id]))
- (catch Exception _ nil))]
- (when (not= id -1)
- (do (jdbc/execute! @cdb/db
- (sql/format {:delete-from [:categories]
- :where [:= :id id]}))
- (delete-category-children! id)
- (-> (ruresp/response "Deleted.")
- (ruresp/header "HX-Refresh" "true"))))
- (ruresp/bad-request "Bad request."))))
-
-(defn edit-category [req]
- (cutils/auth-only req
- (if-let [id (try (Integer/parseInt (get-in req [:path-params :id]))
- (catch Exception _ nil))]
- (do (when-let [name (get-in req [:params "name"])]
- (jdbc/execute! @cdb/db (sql/format {:update :categories
- :set {:name name}
- :where [:= :id id]})))
- (when-let [question (get-in req [:params "question"])]
- (jdbc/execute! @cdb/db (sql/format {:update :categories
- :set {:question question}
- :where [:= :id id]})))
- (ruresp/response "Updated."))
- (ruresp/bad-request "Bad request."))))
-
-(defn create-recipe [req]
- (cutils/auth-only req
- (jdbc/execute! @cdb/db
- (sql/format {:insert-into [:recipes]
- :values [{:title "New recipe"}]}))
- (-> (ruresp/created "Created.")
- (ruresp/header "HX-Refresh" "true"))))
-
-(defn delete-recipe [req]
- (cutils/auth-only req
- (if-let [id (try (Integer/parseInt (get-in req [:path-params :id]))
- (catch Exception _ nil))]
- (do (jdbc/execute! @cdb/db
- (sql/format {:delete-from [:recipes]
- :where [:= :id id]}))
- (-> (ruresp/response "Deleted.")
- (ruresp/header "HX-Refresh" "true")))
- (ruresp/bad-request "Bad request."))))
-
-(defn edit-recipe [req]
- (cutils/auth-only req
- (let [id (try (Integer/parseInt (get-in req [:path-params :id]))
- (catch Exception _ nil))
- ingredients (get-in req [:params "ingredients"])]
- (if (and (some? id)
- (cutils/valid-ingredients? ingredients))
- (do (when-let [thumbnail (get-in req [:params "thumbnail"])]
- (when-let [existing-thumbnail-file (->> {:select [:*]
- :from [:recipes]
- :where [:= :id id]}
- sql/format
- (jdbc/execute! @cdb/db)
- first
- cutils/get-thumbnail-file)]
- (.delete ^File existing-thumbnail-file))
- (cjio/copy (:tempfile thumbnail)
- (File. (str "./thumbnails/" id "."
- (-> thumbnail
- :filename
- (cstr/split #"\.")
- last)))))
- (jdbc/execute! @cdb/db
- (sql/format {:update :recipes
- :set {:title (get-in req [:params "title"])
- :category (get-in req [:params "category"])
-