mirror of
https://github.com/penpot/penpot.git
synced 2025-05-22 00:16:13 +02:00
♻️ Restructure the services directory.
This commit is contained in:
parent
eeb5482d36
commit
b66bc02098
45 changed files with 951 additions and 960 deletions
|
@ -1,93 +0,0 @@
|
|||
;; This Source Code Form is subject to the terms of the Mozilla Public
|
||||
;; License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
;;
|
||||
;; Copyright (c) 2016-2019 Andrey Antukh <niwi@niwi.nz>
|
||||
|
||||
(ns user
|
||||
(:require
|
||||
[clojure.spec.alpha :as s]
|
||||
[clojure.tools.namespace.repl :as repl]
|
||||
[clojure.walk :refer [macroexpand-all]]
|
||||
[clojure.pprint :refer [pprint]]
|
||||
[clojure.test :as test]
|
||||
[clojure.java.io :as io]
|
||||
[criterium.core :refer [quick-bench bench with-progress-reporting]]
|
||||
[expound.alpha :as expound]
|
||||
[promesa.core :as p]
|
||||
[sieppari.core :as sp]
|
||||
[sieppari.context :as spx]
|
||||
[buddy.core.codecs :as codecs]
|
||||
[buddy.core.codecs.base64 :as b64]
|
||||
[buddy.core.nonce :as nonce]
|
||||
[mount.core :as mount]
|
||||
[uxbox.main]
|
||||
[uxbox.util.sql :as sql]
|
||||
[uxbox.util.blob :as blob])
|
||||
(:gen-class))
|
||||
|
||||
(defmacro run-quick-bench
|
||||
[& exprs]
|
||||
`(with-progress-reporting (quick-bench (do ~@exprs) :verbose)))
|
||||
|
||||
(defmacro run-quick-bench'
|
||||
[& exprs]
|
||||
`(quick-bench (do ~@exprs)))
|
||||
|
||||
(defmacro run-bench
|
||||
[& exprs]
|
||||
`(with-progress-reporting (bench (do ~@exprs) :verbose)))
|
||||
|
||||
(defmacro run-bench'
|
||||
[& exprs]
|
||||
`(bench (do ~@exprs)))
|
||||
|
||||
(def stress-data
|
||||
{:shapes [{:id #uuid "352cbd3c-1336-5793-80f7-31027d0acfe7", :name "Canvas-1", :type :canvas, :page #uuid "1b7d4218-e3be-5254-9582-18f767d30501", :x1 200, :y1 200, :x2 1224, :y2 968} {:stroke-color "#000000", :name "Rect-1-copy-4-copy-1", :y1 334, :width 2, :type :rect, :page #uuid "2ef8e638-4018-5581-aa18-0887f126966c", :canvas #uuid "352cbd3c-1336-5793-80f7-31027d0acfe7", :proportion-lock false, :id #uuid "c1966bac-b568-4f8f-9917-227cc37e5672", :x1 674, :proportion 1.353448275862069, :y2 450, :x2 831, :height 2} {:stroke-color "#000000", :name "Rect-1-copy-7-copy-1", :y1 400, :width 2, :type :rect, :page #uuid "2ef8e638-4018-5581-aa18-0887f126966c", :canvas #uuid "352cbd3c-1336-5793-80f7-31027d0acfe7", :proportion-lock false, :id #uuid "20f11fd1-e9a2-41b1-a539-1298194c6d07", :x1 836, :proportion 1.353448275862069, :y2 516, :x2 993, :height 2} {:stroke-color "#000000", :name "Rect-1-copy-11", :y1 355, :width 2, :type :rect, :page #uuid "2ef8e638-4018-5581-aa18-0887f126966c", :canvas #uuid "352cbd3c-1336-5793-80f7-31027d0acfe7", :proportion-lock false, :id #uuid "e1efb49a-03dd-4b87-b568-f35e3a85cf19", :x1 223, :proportion 1.353448275862069, :y2 471, :x2 380, :height 2} {:stroke-color "#000000", :name "Rect-1-copy-10", :y1 404, :width 2, :type :rect, :page #uuid "2ef8e638-4018-5581-aa18-0887f126966c", :canvas #uuid "352cbd3c-1336-5793-80f7-31027d0acfe7", :proportion-lock false, :id #uuid "4bf2440d-38d4-461b-af41-001cfb00be49", :x1 928, :proportion 1.353448275862069, :y2 520, :x2 1085, :height 2} {:stroke-color "#000000", :name "Rect-1-copy-9", :y1 525, :width 2, :type :rect, :page #uuid "2ef8e638-4018-5581-aa18-0887f126966c", :canvas #uuid "352cbd3c-1336-5793-80f7-31027d0acfe7", :proportion-lock false, :id #uuid "bd1cb612-3960-4428-b030-ea0e59c70b06", :x1 497, :proportion 1.353448275862069, :y2 641, :x2 654, :height 2} {:stroke-color "#000000", :name "Rect-1-copy-8", :y1 393, :width 2, :type :rect, :page #uuid "2ef8e638-4018-5581-aa18-0887f126966c", :canvas #uuid "352cbd3c-1336-5793-80f7-31027d0acfe7", :proportion-lock false, :id #uuid "78104154-3d6b-43dc-8444-873ca3285bca", :x1 395, :proportion 1.353448275862069, :y2 509, :x2 552, :height 2} {:stroke-color "#000000", :name "Rect-1-copy-7", :y1 711, :width 2, :type :rect, :page #uuid "2ef8e638-4018-5581-aa18-0887f126966c", :canvas #uuid "352cbd3c-1336-5793-80f7-31027d0acfe7", :proportion-lock false, :id #uuid "35540bc3-e1a9-4052-8cb7-a4b07da229dd", :x1 838, :proportion 1.353448275862069, :y2 827, :x2 995, :height 2} {:stroke-color "#000000", :name "Rect-1-copy-6", :y1 502, :width 2, :type :rect, :page #uuid "2ef8e638-4018-5581-aa18-0887f126966c", :canvas #uuid "352cbd3c-1336-5793-80f7-31027d0acfe7", :proportion-lock false, :id #uuid "a4d2ef30-1da9-4aba-98ed-15088aba36e0", :x1 893, :proportion 1.353448275862069, :y2 618, :x2 1050, :height 2} {:stroke-color "#000000", :name "Rect-1-copy-5", :y1 479, :width 2, :type :rect, :page #uuid "2ef8e638-4018-5581-aa18-0887f126966c", :canvas #uuid "352cbd3c-1336-5793-80f7-31027d0acfe7", :proportion-lock false, :id #uuid "4de4a87a-74f3-47ce-85e6-a0d5da9adb62", :x1 595, :proportion 1.353448275862069, :y2 595, :x2 752, :height 2} {:stroke-color "#000000", :name "Rect-1-copy-4", :y1 645, :width 2, :type :rect, :page #uuid "2ef8e638-4018-5581-aa18-0887f126966c", :canvas #uuid "352cbd3c-1336-5793-80f7-31027d0acfe7", :proportion-lock false, :id #uuid "c6c062d2-20fe-4e48-8035-63358db2df4c", :x1 676, :proportion 1.353448275862069, :y2 761, :x2 833, :height 2} {:stroke-color "#000000", :name "Rect-1-copy-3", :y1 609, :width 2, :type :rect, :page #uuid "2ef8e638-4018-5581-aa18-0887f126966c", :canvas #uuid "352cbd3c-1336-5793-80f7-31027d0acfe7", :proportion-lock false, :id #uuid "b3089c39-24f9-4574-8df1-d1e4596a6200", :x1 831, :proportion 1.353448275862069, :y2 725, :x2 988, :height 2} {:stroke-color "#000000", :name "Rect-1-copy-2", :y1 404, :width 2, :type :rect, :page #uuid "2ef8e638-4018-5581-aa18-0887f126966c", :canvas #uuid "352cbd3c-1336-5793-80f7-31027d0acfe7", :proportion-lock false, :id #uuid "da7c624b-807a-41b1-84a6-7e14ed4f150c", :x1 733, :proportion 1.353448275862069, :y2 520, :x2 890, :height 2} {:stroke-color "#000000", :name "Rect-1-copy-1", :y1 609, :width 2, :type :rect, :page #uuid "2ef8e638-4018-5581-aa18-0887f126966c", :canvas #uuid "352cbd3c-1336-5793-80f7-31027d0acfe7", :proportion-lock false, :id #uuid "ec65a0a8-9de1-48d7-b245-71f98961dc7c", :x1 518, :proportion 1.353448275862069, :y2 725, :x2 675, :height 2} {:stroke-color "#000000", :name "Rect-1", :y1 566, :width 2, :type :rect, :page #uuid "2ef8e638-4018-5581-aa18-0887f126966c", :canvas #uuid "352cbd3c-1336-5793-80f7-31027d0acfe7", :proportion-lock false, :id #uuid "c7eafc25-214a-4d16-abbe-19aa0f2eb25b", :x1 306, :proportion 1.353448275862069, :y2 682, :x2 463, :height 2}]})
|
||||
|
||||
;; (def a1 (blob/encode stress-data))
|
||||
;; (def b1 (blob/encode-with-json stress-data))
|
||||
;; (def b2 (blob/encode-json-snappy stress-data))
|
||||
|
||||
;; --- Development Stuff
|
||||
|
||||
(defn- make-secret
|
||||
[]
|
||||
(-> (nonce/random-bytes 64)
|
||||
(b64/encode true)
|
||||
(codecs/bytes->str)))
|
||||
|
||||
(defn- start
|
||||
[]
|
||||
(-> #_(mount/except #{#'uxbox.scheduled-jobs/scheduler})
|
||||
(mount/start)))
|
||||
|
||||
(defn- stop
|
||||
[]
|
||||
(mount/stop))
|
||||
|
||||
(defn restart
|
||||
[]
|
||||
(stop)
|
||||
(repl/refresh :after 'user/start))
|
||||
|
||||
;; (defn- start-minimal
|
||||
;; []
|
||||
;; (-> (mount/only #{#'uxbox.config/config
|
||||
;; #'uxbox.db/datasource
|
||||
;; #'uxbox.migrations/migrations})
|
||||
;; (mount/start)))
|
||||
|
||||
(defn- run-test
|
||||
([] (run-test #"^uxbox.tests.*"))
|
||||
([o]
|
||||
;; (repl/refresh)
|
||||
(cond
|
||||
(instance? java.util.regex.Pattern o)
|
||||
(test/run-all-tests o)
|
||||
|
||||
(symbol? o)
|
||||
(if-let [sns (namespace o)]
|
||||
(do (require (symbol sns))
|
||||
(test/test-vars [(resolve o)]))
|
||||
(test/test-ns o)))))
|
|
@ -16,7 +16,6 @@
|
|||
[uxbox.http.session :as session]
|
||||
[uxbox.http.handlers :as handlers]
|
||||
[uxbox.http.debug :as debug]
|
||||
[uxbox.services.core :as sv]
|
||||
[vertx.core :as vc]
|
||||
[vertx.http :as vh]
|
||||
[vertx.web :as vw]
|
||||
|
@ -62,6 +61,7 @@
|
|||
(vw/assets "/static/*" {:root "resources/public/static"})
|
||||
(vw/router routes))]
|
||||
|
||||
(log/info "Starting http server on" (:http-server-port cfg/config) "port.")
|
||||
(vh/server ctx {:handler handler
|
||||
:port (:http-server-port cfg/config)})))
|
||||
|
||||
|
|
|
@ -11,7 +11,6 @@
|
|||
[promesa.core :as p]
|
||||
[uxbox.http.errors :as errors]
|
||||
[uxbox.http.session :as session]
|
||||
[uxbox.services.core :as sv]
|
||||
[uxbox.util.uuid :as uuid]))
|
||||
|
||||
(defn emails-list
|
||||
|
|
|
@ -9,16 +9,18 @@
|
|||
[promesa.core :as p]
|
||||
[uxbox.emails :as emails]
|
||||
[uxbox.http.session :as session]
|
||||
[uxbox.services.core :as sv]
|
||||
[uxbox.services.init]
|
||||
[uxbox.services.mutations :as sm]
|
||||
[uxbox.services.queries :as sq]
|
||||
[uxbox.util.uuid :as uuid]))
|
||||
|
||||
(defn query-handler
|
||||
[req]
|
||||
(let [type (get-in req [:path-params :type])
|
||||
data (merge (:params req)
|
||||
{::sv/type (keyword type)
|
||||
{::sq/type (keyword type)
|
||||
:user (:user req)})]
|
||||
(-> (sv/query (with-meta data {:req req}))
|
||||
(-> (sq/handle (with-meta data {:req req}))
|
||||
(p/then' (fn [result]
|
||||
{:status 200
|
||||
:body result})))))
|
||||
|
@ -29,9 +31,9 @@
|
|||
data (merge (:params req)
|
||||
(:body-params req)
|
||||
(:uploads req)
|
||||
{::sv/type (keyword type)
|
||||
{::sm/type (keyword type)
|
||||
:user (:user req)})]
|
||||
(-> (sv/mutation (with-meta data {:req req}))
|
||||
(-> (sm/handle (with-meta data {:req req}))
|
||||
(p/then' (fn [result]
|
||||
{:status 200 :body result})))))
|
||||
|
||||
|
@ -39,7 +41,7 @@
|
|||
[req]
|
||||
(let [data (:body-params req)
|
||||
user-agent (get-in req [:headers "user-agent"])]
|
||||
(-> (sv/mutation (assoc data ::sv/type :login))
|
||||
(-> (sm/handle (assoc data ::sm/type :login))
|
||||
(p/then #(session/create % user-agent))
|
||||
(p/then' (fn [token]
|
||||
{:status 204
|
||||
|
@ -59,9 +61,9 @@
|
|||
(defn register-handler
|
||||
[req]
|
||||
(let [data (merge (:body-params req)
|
||||
{::sv/type :register-profile})
|
||||
{::sm/type :register-profile})
|
||||
user-agent (get-in req [:headers "user-agent"])]
|
||||
(-> (sv/mutation (with-meta data {:req req}))
|
||||
(-> (sm/handle (with-meta data {:req req}))
|
||||
(p/then (fn [{:keys [id] :as user}]
|
||||
(session/create id user-agent)))
|
||||
(p/then' (fn [token]
|
||||
|
|
|
@ -19,28 +19,25 @@
|
|||
:steps
|
||||
[{:desc "Initial triggers and utils."
|
||||
:name "0001-main"
|
||||
:fn (mg/resource "migrations/0001.main.up.sql")}
|
||||
:fn (mg/resource "migrations/0001.main.sql")}
|
||||
{:desc "Initial auth related tables"
|
||||
:name "0002-auth"
|
||||
:fn (mg/resource "migrations/0002.auth.up.sql")}
|
||||
:name "0002-users"
|
||||
:fn (mg/resource "migrations/0002.users.sql")}
|
||||
{:desc "Initial projects tables"
|
||||
:name "0003-projects"
|
||||
:fn (mg/resource "migrations/0003.projects.up.sql")}
|
||||
:fn (mg/resource "migrations/0003.projects.sql")}
|
||||
{:desc "Initial pages tables"
|
||||
:name "0004-pages"
|
||||
:fn (mg/resource "migrations/0004.pages.up.sql")}
|
||||
{:desc "Initial kvstore tables"
|
||||
:name "0005-kvstore"
|
||||
:fn (mg/resource "migrations/0005.kvstore.up.sql")}
|
||||
:fn (mg/resource "migrations/0004.pages.sql")}
|
||||
{:desc "Initial emails related tables"
|
||||
:name "0006-emails"
|
||||
:fn (mg/resource "migrations/0006.emails.up.sql")}
|
||||
:name "0005-emails"
|
||||
:fn (mg/resource "migrations/0005.emails.sql")}
|
||||
{:desc "Initial images tables"
|
||||
:name "0007-images"
|
||||
:fn (mg/resource "migrations/0007.images.up.sql")}
|
||||
:name "0006-images"
|
||||
:fn (mg/resource "migrations/0006.images.sql")}
|
||||
{:desc "Initial icons tables"
|
||||
:name "0008-icons"
|
||||
:fn (mg/resource "migrations/0008.icons.up.sql")}
|
||||
:name "0007-icons"
|
||||
:fn (mg/resource "migrations/0007.icons.sql")}
|
||||
]})
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
|
|
@ -1,111 +0,0 @@
|
|||
;; This Source Code Form is subject to the terms of the Mozilla Public
|
||||
;; License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
;;
|
||||
;; Copyright (c) 2016 Andrey Antukh <niwi@niwi.nz>
|
||||
|
||||
(ns uxbox.portation
|
||||
"Support for export/import operations of projects."
|
||||
(:refer-clojure :exclude [with-open])
|
||||
#_(:require [clojure.java.io :as io]
|
||||
[suricatta.core :as sc]
|
||||
[datoteka.core :as fs]
|
||||
[uxbox.db :as db]
|
||||
[uxbox.util.uuid :as uuid]
|
||||
[uxbox.util.closeable :refer (with-open)]
|
||||
[uxbox.util.transit :as t]))
|
||||
|
||||
;; ;; --- Export
|
||||
|
||||
;; (defn- write-project
|
||||
;; [conn writer id]
|
||||
;; (let [sql (sql/get-project-by-id {:id id})
|
||||
;; result (sc/fetch-one conn sql)]
|
||||
;; (when-not result
|
||||
;; (ex-info "No project found with specified id" {:id id}))
|
||||
;; (t/write! writer {::type ::project ::payload result})))
|
||||
|
||||
;; (defn- write-pages
|
||||
;; [conn writer id]
|
||||
;; (let [sql (sql/get-pages-for-project {:project id})
|
||||
;; results (sc/fetch conn sql)]
|
||||
;; (run! #(t/write! writer {::type ::page ::payload %}) results)))
|
||||
|
||||
;; (defn- write-pages-history
|
||||
;; [conn writer id]
|
||||
;; (let [sql (sql/get-page-history-for-project {:project id})
|
||||
;; results (sc/fetch conn sql)]
|
||||
;; (run! #(t/write! writer {::type ::page-history ::payload %}) results)))
|
||||
|
||||
;; (defn- write-data
|
||||
;; [path id]
|
||||
;; (with-open [ostream (io/output-stream path)
|
||||
;; zstream (snappy/output-stream ostream)
|
||||
;; conn (db/connection)]
|
||||
;; (let [writer (t/writer zstream {:type :msgpack})]
|
||||
;; (sc/atomic conn
|
||||
;; (write-project conn writer id)
|
||||
;; (write-pages conn writer id)
|
||||
;; (write-pages-history conn writer id)))))
|
||||
|
||||
;; (defn export
|
||||
;; "Given an id, returns a path to a temporal file with the exported
|
||||
;; bundle of the specified project."
|
||||
;; [id]
|
||||
;; (let [path (fs/create-tempfile)]
|
||||
;; (write-data path id)
|
||||
;; path))
|
||||
|
||||
;; ;; --- Import
|
||||
|
||||
;; (defn- read-entry
|
||||
;; [reader]
|
||||
;; (try
|
||||
;; (t/read! reader)
|
||||
;; (catch RuntimeException e
|
||||
;; (let [cause (.getCause e)]
|
||||
;; (if (instance? java.io.EOFException cause)
|
||||
;; ::eof
|
||||
;; (throw e))))))
|
||||
|
||||
;; (defn- persist-project
|
||||
;; [conn project]
|
||||
;; (let [sql (sql/create-project project)]
|
||||
;; (sc/execute conn sql)))
|
||||
|
||||
;; (defn- persist-page
|
||||
;; [conn page]
|
||||
;; (let [sql (sql/create-page page)]
|
||||
;; (sc/execute conn sql)))
|
||||
|
||||
;; (defn- persist-page-history
|
||||
;; [conn history]
|
||||
;; (let [sql (sql/create-page-history history)]
|
||||
;; (sc/execute conn sql)))
|
||||
|
||||
;; (defn- persist-entry
|
||||
;; [conn entry]
|
||||
;; (let [payload (::payload entry)
|
||||
;; type (::type entry)]
|
||||
;; (case type
|
||||
;; ::project (persist-project conn payload)
|
||||
;; ::page (persist-page conn payload)
|
||||
;; ::page-history (persist-page-history conn payload))))
|
||||
|
||||
;; (defn- read-data
|
||||
;; [conn reader]
|
||||
;; (loop [entry (read-entry reader)]
|
||||
;; (when (not= entry ::eof)
|
||||
;; (persist-entry conn entry)
|
||||
;; (recur (read-entry reader)))))
|
||||
|
||||
;; (defn import!
|
||||
;; "Given a path to the previously exported bundle, try to import it."
|
||||
;; [path]
|
||||
;; (with-open [istream (io/input-stream (fs/path path))
|
||||
;; zstream (snappy/input-stream istream)
|
||||
;; conn (db/connection)]
|
||||
;; (let [reader (t/reader zstream {:type :msgpack})]
|
||||
;; (sc/atomic conn
|
||||
;; (read-data conn reader)
|
||||
;; nil))))
|
|
@ -1,71 +0,0 @@
|
|||
;; This Source Code Form is subject to the terms of the Mozilla Public
|
||||
;; License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
;;
|
||||
;; Copyright (c) 2019 Andrey Antukh <niwi@niwi.nz>
|
||||
|
||||
(ns uxbox.services.core
|
||||
(:require
|
||||
[clojure.tools.logging :as log]
|
||||
[promesa.core :as p]
|
||||
[vertx.core :as vc]
|
||||
[uxbox.core :refer [system]]
|
||||
[uxbox.util.uuid :as uuid]
|
||||
[uxbox.util.dispatcher :as uds]
|
||||
[uxbox.util.exceptions :as ex])
|
||||
(:import
|
||||
java.util.Map
|
||||
java.util.List
|
||||
java.util.Map$Entry
|
||||
java.util.HashMap))
|
||||
|
||||
;; (def context-interceptor
|
||||
;; {:enter (fn [data]
|
||||
;; (update data :request assoc ::ctx (vc/get-or-create-context system)))})
|
||||
|
||||
(def logging-interceptor
|
||||
{:enter (fn [data]
|
||||
(let [type (get-in data [:request ::type])]
|
||||
(assoc data ::start-time (System/nanoTime))))
|
||||
:leave (fn [data]
|
||||
(let [elapsed (- (System/nanoTime) (::start-time data))
|
||||
elapsed (str (quot elapsed 1000000) "ms")
|
||||
type (get-in data [:request ::type])]
|
||||
(log/info "service" type "processed in" elapsed)
|
||||
data))})
|
||||
|
||||
|
||||
(uds/defservice query
|
||||
{:dispatch-by ::type
|
||||
:interceptors [uds/spec-interceptor
|
||||
logging-interceptor
|
||||
#_context-interceptor]})
|
||||
|
||||
(uds/defservice mutation
|
||||
{:dispatch-by ::type
|
||||
:interceptors [uds/spec-interceptor
|
||||
#_context-interceptor]})
|
||||
|
||||
;; --- Helpers
|
||||
|
||||
(defmacro defmutation
|
||||
[key & rest]
|
||||
`(uds/defmethod mutation ~key ~@rest))
|
||||
|
||||
(defmacro defquery
|
||||
[key & rest]
|
||||
`(uds/defmethod query ~key ~@rest))
|
||||
|
||||
(defn raise-not-found-if-nil
|
||||
[v]
|
||||
(if (nil? v)
|
||||
(ex/raise :type :not-found
|
||||
:hint "Object doest not exists.")
|
||||
v))
|
||||
|
||||
(def constantly-nil (constantly nil))
|
||||
|
||||
(defn handle-on-context
|
||||
[p]
|
||||
(->> (vc/get-or-create-context system)
|
||||
(vc/handle-on-context p)))
|
35
backend/src/uxbox/services/init.clj
Normal file
35
backend/src/uxbox/services/init.clj
Normal file
|
@ -0,0 +1,35 @@
|
|||
;; This Source Code Form is subject to the terms of the Mozilla Public
|
||||
;; License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
;;
|
||||
;; Copyright (c) 2019 Andrey Antukh <niwi@niwi.nz>
|
||||
|
||||
(ns uxbox.services.init
|
||||
"A initialization of services."
|
||||
(:require
|
||||
[mount.core :as mount :refer [defstate]]))
|
||||
|
||||
(defn- load-query-services
|
||||
[]
|
||||
(require 'uxbox.services.queries.icons)
|
||||
(require 'uxbox.services.queries.images)
|
||||
(require 'uxbox.services.queries.pages)
|
||||
(require 'uxbox.services.queries.profiles)
|
||||
(require 'uxbox.services.queries.projects)
|
||||
(require 'uxbox.services.queries.user-storage))
|
||||
|
||||
(defn- load-mutation-services
|
||||
[]
|
||||
(require 'uxbox.services.mutations.auth)
|
||||
(require 'uxbox.services.mutations.icons)
|
||||
(require 'uxbox.services.mutations.images)
|
||||
(require 'uxbox.services.mutations.projects)
|
||||
(require 'uxbox.services.mutations.pages)
|
||||
(require 'uxbox.services.mutations.profiles)
|
||||
(require 'uxbox.services.mutations.user-storage))
|
||||
|
||||
(defstate query-services
|
||||
:start (load-query-services))
|
||||
|
||||
(defstate mutation-services
|
||||
:start (load-mutation-services))
|
|
@ -1,70 +0,0 @@
|
|||
;; This Source Code Form is subject to the terms of the Mozilla Public
|
||||
;; License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
;;
|
||||
;; Copyright (c) 2019 Andrey Antukh <niwi@niwi.nz>
|
||||
|
||||
(ns uxbox.services.kvstore
|
||||
(:require
|
||||
[clojure.spec.alpha :as s]
|
||||
[promesa.core :as p]
|
||||
[uxbox.db :as db]
|
||||
[uxbox.services.core :as sv]
|
||||
[uxbox.util.blob :as blob]
|
||||
[uxbox.util.data :as data]
|
||||
[uxbox.util.spec :as us]
|
||||
[uxbox.util.time :as dt]
|
||||
[uxbox.util.uuid :as uuid]))
|
||||
|
||||
(defn- decode-row
|
||||
[{:keys [value] :as row}]
|
||||
(when row
|
||||
(cond-> row
|
||||
value (assoc :value (blob/decode value)))))
|
||||
|
||||
;; --- Update KVStore
|
||||
|
||||
(s/def ::user ::us/uuid)
|
||||
(s/def ::key ::us/string)
|
||||
(s/def ::value any?)
|
||||
|
||||
(s/def ::upsert-kvstore
|
||||
(s/keys :req-un [::key ::value ::user]))
|
||||
|
||||
(sv/defmutation ::upsert-kvstore
|
||||
[{:keys [key value user] :as params}]
|
||||
(let [sql "insert into kvstore (key, value, user_id)
|
||||
values ($1, $2, $3)
|
||||
on conflict (user_id, key)
|
||||
do update set value = $2"
|
||||
val (blob/encode value)]
|
||||
(-> (db/query-one db/pool [sql key val user])
|
||||
(p/then' sv/constantly-nil))))
|
||||
|
||||
;; --- Retrieve KVStore
|
||||
|
||||
(s/def ::kvstore-entry
|
||||
(s/keys :req-un [::key ::user]))
|
||||
|
||||
(sv/defquery ::kvstore-entry
|
||||
[{:keys [key user]}]
|
||||
(let [sql "select kv.*
|
||||
from kvstore as kv
|
||||
where kv.user_id = $2
|
||||
and kv.key = $1"]
|
||||
(-> (db/query-one db/pool [sql key user])
|
||||
(p/then' sv/raise-not-found-if-nil)
|
||||
(p/then' decode-row))))
|
||||
|
||||
;; --- Delete KVStore
|
||||
|
||||
(s/def ::delete-kvstore
|
||||
(s/keys :req-un [::key ::user]))
|
||||
|
||||
(sv/defmutation ::delete-kvstore
|
||||
[{:keys [user key] :as params}]
|
||||
(let [sql "delete from kvstore
|
||||
where user_id = $2
|
||||
and key = $1"]
|
||||
(-> (db/query-one db/pool [sql key user])
|
||||
(p/then' sv/constantly-nil))))
|
19
backend/src/uxbox/services/mutations.clj
Normal file
19
backend/src/uxbox/services/mutations.clj
Normal file
|
@ -0,0 +1,19 @@
|
|||
;; This Source Code Form is subject to the terms of the Mozilla Public
|
||||
;; License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
;;
|
||||
;; Copyright (c) 2019 Andrey Antukh <niwi@niwi.nz>
|
||||
|
||||
(ns uxbox.services.mutations
|
||||
(:require
|
||||
[uxbox.util.dispatcher :as uds]))
|
||||
|
||||
(uds/defservice handle
|
||||
{:dispatch-by ::type
|
||||
:interceptors [uds/spec-interceptor
|
||||
#_logging-interceptor
|
||||
#_context-interceptor]})
|
||||
|
||||
(defmacro defmutation
|
||||
[key & rest]
|
||||
`(uds/defmethod handle ~key ~@rest))
|
|
@ -4,33 +4,32 @@
|
|||
;;
|
||||
;; Copyright (c) 2019 Andrey Antukh <niwi@niwi.nz>
|
||||
|
||||
(ns uxbox.services.auth
|
||||
(ns uxbox.services.mutations.auth
|
||||
(:require
|
||||
[clojure.spec.alpha :as s]
|
||||
[buddy.hashers :as hashers]
|
||||
[promesa.core :as p]
|
||||
[uxbox.config :as cfg]
|
||||
[uxbox.db :as db]
|
||||
[uxbox.services.core :as sc]
|
||||
[uxbox.services.users :as users]
|
||||
[uxbox.services.mutations :as sm]
|
||||
[uxbox.util.spec :as us]
|
||||
[uxbox.util.exceptions :as ex]))
|
||||
|
||||
(s/def ::username ::us/string)
|
||||
(s/def ::password ::us/string)
|
||||
(s/def ::scope ::us/string)
|
||||
|
||||
(s/def ::login-params
|
||||
(s/keys :req-un [::username ::password]
|
||||
:opt-un [::scope]))
|
||||
|
||||
(def ^:private user-by-username-sql
|
||||
"select id, password
|
||||
from users
|
||||
where username=$1 or email=$1
|
||||
and deleted_at is null")
|
||||
|
||||
(sc/defmutation :login
|
||||
(s/def ::username ::us/string)
|
||||
(s/def ::password ::us/string)
|
||||
(s/def ::scope ::us/string)
|
||||
|
||||
(s/def ::login
|
||||
(s/keys :req-un [::username ::password]
|
||||
:opt-un [::scope]))
|
||||
|
||||
(sm/defmutation ::login
|
||||
{:doc "User login"
|
||||
:spec ::login-params}
|
||||
[{:keys [username password scope] :as params}]
|
||||
|
@ -44,7 +43,7 @@
|
|||
(when-not (check-password user password)
|
||||
(ex/raise :type :validation
|
||||
:code ::wrong-credentials))
|
||||
(:id user))]
|
||||
|
||||
{:id (:id user)})]
|
||||
(-> (db/query-one db/pool [user-by-username-sql username])
|
||||
(p/then' check-user))))
|
|
@ -4,15 +4,15 @@
|
|||
;;
|
||||
;; Copyright (c) 2019 Andrey Antukh <niwi@niwi.nz>
|
||||
|
||||
(ns uxbox.services.icons
|
||||
"Icons library related services."
|
||||
(ns uxbox.services.mutations.icons
|
||||
(:require
|
||||
[clojure.spec.alpha :as s]
|
||||
[promesa.core :as p]
|
||||
[uxbox.db :as db]
|
||||
[uxbox.services.core :as sv]
|
||||
[uxbox.services.mutations :as sm]
|
||||
[uxbox.services.util :as su]
|
||||
[uxbox.services.queries.icons :refer [decode-icon-row]]
|
||||
[uxbox.util.blob :as blob]
|
||||
[uxbox.util.exceptions :as ex]
|
||||
[uxbox.util.spec :as us]
|
||||
[uxbox.util.uuid :as uuid]))
|
||||
|
||||
|
@ -36,75 +36,13 @@
|
|||
(s/def ::metadata
|
||||
(s/keys :opt-un [::width ::height ::view-box ::mimetype]))
|
||||
|
||||
(defn- decode-icon-row
|
||||
[{:keys [metadata] :as row}]
|
||||
(when row
|
||||
(cond-> row
|
||||
metadata (assoc :metadata (blob/decode metadata)))))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Queries
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
;; --- Query: Collections
|
||||
|
||||
(def ^:private icons-collections-sql
|
||||
"select *,
|
||||
(select count(*) from icons where collection_id = ic.id) as num_icons
|
||||
from icons_collections as ic
|
||||
where (ic.user_id = $1 or
|
||||
ic.user_id = '00000000-0000-0000-0000-000000000000'::uuid)
|
||||
and ic.deleted_at is null
|
||||
order by ic.created_at desc")
|
||||
|
||||
(s/def ::icons-collections
|
||||
(s/keys :req-un [::user]))
|
||||
|
||||
(sv/defquery :icons-collections
|
||||
{:doc "Retrieve all icons collections for current user."
|
||||
:spec ::icons-collections}
|
||||
[{:keys [user] :as params}]
|
||||
(let [sqlv [icons-collections-sql user]]
|
||||
(db/query db/pool sqlv)))
|
||||
|
||||
;; --- List Icons
|
||||
|
||||
(def ^:private icons-by-collection-sql
|
||||
"select *
|
||||
from icons as i
|
||||
where (i.user_id = $1 or
|
||||
i.user_id = '00000000-0000-0000-0000-000000000000'::uuid)
|
||||
and i.deleted_at is null
|
||||
and case when $2::uuid is null then i.collection_id is null
|
||||
else i.collection_id = $2::uuid
|
||||
end
|
||||
order by i.created_at desc")
|
||||
|
||||
(s/def ::icons-by-collection
|
||||
(s/keys :req-un [::user]
|
||||
:opt-un [::collection-id]))
|
||||
|
||||
(sv/defquery :icons-by-collection
|
||||
{:doc "Retrieve icons for specified collection."
|
||||
:spec ::icons-by-collection}
|
||||
[{:keys [user collection-id] :as params}]
|
||||
(let [sqlv [icons-by-collection-sql user collection-id]]
|
||||
(-> (db/query db/pool sqlv)
|
||||
(p/then' #(mapv decode-icon-row %)))))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Mutations
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
;; --- Mutation: Create Collection
|
||||
|
||||
(s/def ::create-icons-collection
|
||||
(s/keys :req-un [::user ::name]
|
||||
:opt-un [::id]))
|
||||
|
||||
(sv/defmutation :create-icons-collection
|
||||
{:doc "Create a new collection of icons."
|
||||
:spec ::create-icons-collection}
|
||||
(sm/defmutation ::create-icons-collection
|
||||
[{:keys [id user name] :as params}]
|
||||
(let [id (or id (uuid/next))
|
||||
sql "insert into icons_collections (id, user_id, name)
|
||||
|
@ -116,9 +54,7 @@
|
|||
(s/def ::update-icons-collection
|
||||
(s/keys :req-un [::user ::name ::id]))
|
||||
|
||||
(sv/defmutation :update-icons-collection
|
||||
{:doc "Update a collection of icons."
|
||||
:spec ::update-icons-collection}
|
||||
(sm/defmutation ::update-icons-collection
|
||||
[{:keys [id user name] :as params}]
|
||||
(let [sql "update icons_collections
|
||||
set name = $3
|
||||
|
@ -126,7 +62,7 @@
|
|||
and user_id = $2
|
||||
returning *"]
|
||||
(-> (db/query-one db/pool [sql id user name])
|
||||
(p/then' sv/raise-not-found-if-nil))))
|
||||
(p/then' su/raise-not-found-if-nil))))
|
||||
|
||||
;; --- Copy Icon
|
||||
|
||||
|
@ -140,14 +76,12 @@
|
|||
and (user_id = $2 or
|
||||
user_id = '00000000-0000-0000-0000-000000000000'::uuid)"]
|
||||
(-> (db/query-one conn [sql id user])
|
||||
(p/then' sv/raise-not-found-if-nil))))
|
||||
(p/then' su/raise-not-found-if-nil))))
|
||||
|
||||
(s/def ::copy-icon
|
||||
(s/keys :req-un [:us/id ::collection-id ::user]))
|
||||
|
||||
(sv/defmutation :copy-icon
|
||||
{:doc "Copy an icon from one collection to other."
|
||||
:spec ::copy-icon}
|
||||
(sm/defmutation ::copy-icon
|
||||
[{:keys [user id collection-id] :as params}]
|
||||
(db/with-atomic [conn db/pool]
|
||||
(-> (retrieve-icon conn {:user user :id id})
|
||||
|
@ -161,9 +95,7 @@
|
|||
(s/def ::delete-icons-collection
|
||||
(s/keys :req-un [::user ::id]))
|
||||
|
||||
(sv/defmutation :delete-icons-collection
|
||||
{:doc "Delete a collection of icons."
|
||||
:spec ::delete-icons-collection}
|
||||
(sm/defmutation ::delete-icons-collection
|
||||
[{:keys [user id] :as params}]
|
||||
(let [sql "update icons_collections
|
||||
set deleted_at = clock_timestamp()
|
||||
|
@ -171,8 +103,8 @@
|
|||
and user_id = $2
|
||||
returning id"]
|
||||
(-> (db/query-one db/pool [sql id user])
|
||||
(p/then' sv/raise-not-found-if-nil)
|
||||
(p/then' sv/constantly-nil))))
|
||||
(p/then' su/raise-not-found-if-nil)
|
||||
(p/then' su/constantly-nil))))
|
||||
|
||||
;; --- Mutation: Create Icon (Upload)
|
||||
|
||||
|
@ -194,9 +126,7 @@
|
|||
(s/keys :req-un [::user ::name ::metadata ::content]
|
||||
:opt-un [::id ::collection-id]))
|
||||
|
||||
(sv/defmutation :create-icon
|
||||
{:doc "Create (upload) a new icon."
|
||||
:spec ::create-icon}
|
||||
(sm/defmutation ::create-icon
|
||||
[params]
|
||||
(create-icon db/pool params))
|
||||
|
||||
|
@ -205,9 +135,7 @@
|
|||
(s/def ::update-icon
|
||||
(s/keys :req-un [::id ::user ::name ::collection-id]))
|
||||
|
||||
(sv/defmutation :update-icon
|
||||
{:doc "Update an icon entry."
|
||||
:spec ::update-icon}
|
||||
(sm/defmutation ::update-icon
|
||||
[{:keys [id name user collection-id] :as params}]
|
||||
(let [sql "update icons
|
||||
set name = $1,
|
||||
|
@ -216,16 +144,14 @@
|
|||
and user_id = $4
|
||||
returning *"]
|
||||
(-> (db/query-one db/pool [sql name collection-id id user])
|
||||
(p/then' sv/raise-not-found-if-nil))))
|
||||
(p/then' su/raise-not-found-if-nil))))
|
||||
|
||||
;; --- Mutation: Delete Icon
|
||||
|
||||
(s/def ::delete-icon
|
||||
(s/keys :req-un [::user ::id]))
|
||||
|
||||
(sv/defmutation :delete-icon
|
||||
{:doc "Delete an icon entry."
|
||||
:spec ::delete-icon}
|
||||
(sm/defmutation ::delete-icon
|
||||
[{:keys [id user] :as params}]
|
||||
(let [sql "update icons
|
||||
set deleted_at = clock_timestamp()
|
||||
|
@ -233,5 +159,5 @@
|
|||
and user_id = $2
|
||||
returning id"]
|
||||
(-> (db/query-one db/pool [sql id user])
|
||||
(p/then' sv/raise-not-found-if-nil)
|
||||
(p/then' sv/constantly-nil))))
|
||||
(p/then' su/raise-not-found-if-nil)
|
||||
(p/then' su/constantly-nil))))
|
|
@ -4,8 +4,7 @@
|
|||
;;
|
||||
;; Copyright (c) 2019 Andrey Antukh <niwi@niwi.nz>
|
||||
|
||||
(ns uxbox.services.images
|
||||
"Images library related services."
|
||||
(ns uxbox.services.mutations.images
|
||||
(:require
|
||||
[clojure.spec.alpha :as s]
|
||||
[datoteka.core :as fs]
|
||||
|
@ -15,7 +14,8 @@
|
|||
[uxbox.db :as db]
|
||||
[uxbox.media :as media]
|
||||
[uxbox.images :as images]
|
||||
[uxbox.services.core :as sc]
|
||||
[uxbox.services.mutations :as sm]
|
||||
[uxbox.services.util :as su]
|
||||
[uxbox.util.blob :as blob]
|
||||
[uxbox.util.data :as data]
|
||||
[uxbox.util.exceptions :as ex]
|
||||
|
@ -35,7 +35,7 @@
|
|||
[row]
|
||||
(let [opts +thumbnail-options+]
|
||||
(-> (px/submit! #(images/populate-thumbnails row opts))
|
||||
(sc/handle-on-context))))
|
||||
(su/handle-on-context))))
|
||||
|
||||
(defn- populate-thumbnails
|
||||
[rows]
|
||||
|
@ -58,9 +58,7 @@
|
|||
(s/keys :req-un [::user ::us/name]
|
||||
:opt-un [::id]))
|
||||
|
||||
(sc/defmutation :create-image-collection
|
||||
{:doc "Create image collection"
|
||||
:spec ::create-image-collection}
|
||||
(sm/defmutation ::create-image-collection
|
||||
[{:keys [id user name] :as params}]
|
||||
(let [sql "insert into images_collections (id, user_id, name)
|
||||
values ($1, $2, $3) returning *;"]
|
||||
|
@ -71,9 +69,7 @@
|
|||
(s/def ::update-images-collection
|
||||
(s/keys :req-un [::id ::user ::us/name]))
|
||||
|
||||
(sc/defmutation :update-images-collection
|
||||
{:doc "Update image collection."
|
||||
:spec ::update-images-collection}
|
||||
(sm/defmutation ::update-images-collection
|
||||
[{:keys [id user name] :as params}]
|
||||
(let [sql "update images_collections
|
||||
set name = $3
|
||||
|
@ -82,30 +78,12 @@
|
|||
returning *;"]
|
||||
(db/query-one db/pool [sql id user name])))
|
||||
|
||||
;; --- List Collections
|
||||
|
||||
(def ^:private images-collections-sql
|
||||
"select *,
|
||||
(select count(*) from images where collection_id = ic.id) as num_images
|
||||
from images_collections as ic
|
||||
where (ic.user_id = $1 or
|
||||
ic.user_id = '00000000-0000-0000-0000-000000000000'::uuid)
|
||||
and ic.deleted_at is null
|
||||
order by ic.created_at desc;")
|
||||
|
||||
(sc/defquery :images-collections
|
||||
{:doc "Retrieve image collections for the current logged user"}
|
||||
[{:keys [user] :as params}]
|
||||
(db/query db/pool [images-collections-sql user]))
|
||||
|
||||
;; --- Delete Collection
|
||||
|
||||
(s/def ::delete-images-collection
|
||||
(s/keys :req-un [::user ::id]))
|
||||
|
||||
(sc/defmutation :delete-images-collection
|
||||
{:doc "Delete an image collection"
|
||||
:spec ::delete-images-collection}
|
||||
(sm/defmutation ::delete-images-collection
|
||||
[{:keys [id user] :as params}]
|
||||
(let [sql "update images_collections
|
||||
set deleted_at = clock_timestamp()
|
||||
|
@ -113,35 +91,16 @@
|
|||
and user_id = $2
|
||||
returning id"]
|
||||
(-> (db/query-one db/pool [sql id user])
|
||||
(p/then' sc/raise-not-found-if-nil))))
|
||||
|
||||
;; --- Retrieve Image
|
||||
|
||||
(defn retrieve-image
|
||||
[conn id]
|
||||
(let [sql "select * from images
|
||||
where id = $1
|
||||
and deleted_at is null;"]
|
||||
(db/query-one conn [sql id])))
|
||||
|
||||
;; (s/def ::retrieve-image
|
||||
;; (s/keys :req-un [::user ::us/id]))
|
||||
|
||||
;; (defmethod core/query :retrieve-image
|
||||
;; [params]
|
||||
;; (s/assert ::retrieve-image params)
|
||||
;; (with-open [conn (db/connection)]
|
||||
;; (retrieve-image conn params)))
|
||||
(p/then' su/raise-not-found-if-nil))))
|
||||
|
||||
;; --- Create Image (Upload)
|
||||
|
||||
(defn- store-image-in-fs
|
||||
[{:keys [name path] :as upload}]
|
||||
(prn "store-image-in-fs" upload)
|
||||
(let [filename (fs/name name)
|
||||
storage media/images-storage]
|
||||
(-> (ds/save storage filename path)
|
||||
(vc/handle-on-context))))
|
||||
(su/handle-on-context))))
|
||||
|
||||
(def ^:private create-image-sql
|
||||
"insert into images (user_id, name, collection_id, path, width, height, mimetype)
|
||||
|
@ -167,9 +126,7 @@
|
|||
(s/keys :req-un [::user ::name ::file ::width ::height ::mimetype]
|
||||
:opt-un [::id ::collection-id]))
|
||||
|
||||
(sc/defmutation :create-image
|
||||
{:doc "Create (upload) new image."
|
||||
:spec ::create-image}
|
||||
(sm/defmutation ::create-image
|
||||
[{:keys [file] :as params}]
|
||||
(when-not (valid-image-types? (:mtype file))
|
||||
(ex/raise :type :validation
|
||||
|
@ -192,9 +149,7 @@
|
|||
and user_id = $4
|
||||
returning *;")
|
||||
|
||||
(sc/defmutation :update-image
|
||||
{:doc "Update a image entry."
|
||||
:spec ::update-image}
|
||||
(sm/defmutation ::update-image
|
||||
[{:keys [id name user collection-id] :as params}]
|
||||
(let [sql update-image-sql]
|
||||
(db/query-one db/pool [sql id collection-id name user])))
|
||||
|
@ -206,9 +161,7 @@
|
|||
(s/def ::copy-image
|
||||
(s/keys :req-un [::id ::collection-id ::user]))
|
||||
|
||||
(sc/defmutation :copy-image
|
||||
{:doc "Copy image from one collection to an other."
|
||||
:spec ::copy-image}
|
||||
(sm/defmutation ::copy-image
|
||||
[{:keys [user id collection-id] :as params}]
|
||||
(letfn [(copy-image [conn {:keys [path] :as image}]
|
||||
(-> (ds/lookup media/images-storage (:path image))
|
||||
|
@ -221,7 +174,7 @@
|
|||
|
||||
(db/with-atomic [conn db/pool]
|
||||
(-> (retrieve-image conn {:id id :user user})
|
||||
(p/then sc/raise-not-found-if-nil)
|
||||
(p/then su/raise-not-found-if-nil)
|
||||
(p/then (partial copy-image conn))))))
|
||||
|
||||
;; --- Delete Image
|
||||
|
@ -237,9 +190,7 @@
|
|||
(s/def ::delete-image
|
||||
(s/keys :req-un [::id ::user]))
|
||||
|
||||
(sc/defmutation :delete-image
|
||||
{:doc "Delete image entry."
|
||||
:spec ::delete-image}
|
||||
(sm/defmutation ::delete-image
|
||||
[{:keys [user id] :as params}]
|
||||
(let [sql "update images
|
||||
set deleted_at = clock_timestamp()
|
||||
|
@ -247,29 +198,3 @@
|
|||
and user_id = $2
|
||||
returning *"]
|
||||
(db/query-one db/pool [sql id user])))
|
||||
|
||||
;; --- Query Images by Collection (id)
|
||||
|
||||
(def images-by-collection-sql
|
||||
"select * from images
|
||||
where (user_id = $1 or
|
||||
user_id = '00000000-0000-0000-0000-000000000000'::uuid)
|
||||
and deleted_at is null
|
||||
and case when $2::uuid is null then collection_id is null
|
||||
else collection_id = $2::uuid
|
||||
end
|
||||
order by created_at desc;")
|
||||
|
||||
(s/def ::images-by-collection-query
|
||||
(s/keys :req-un [::user]
|
||||
:opt-un [::collection-id]))
|
||||
|
||||
(sc/defquery :images-by-collection
|
||||
{:doc "Get all images of a collection"
|
||||
:spec ::images-by-collection-query}
|
||||
[{:keys [user collection-id] :as params}]
|
||||
(let [sqlv [images-by-collection-sql user collection-id]]
|
||||
(-> (db/query db/pool sqlv)
|
||||
(p/then populate-thumbnails)
|
||||
(p/then #(mapv populate-urls %)))))
|
||||
|
|
@ -4,22 +4,21 @@
|
|||
;;
|
||||
;; Copyright (c) 2019 Andrey Antukh <niwi@niwi.nz>
|
||||
|
||||
(ns uxbox.services.pages
|
||||
(ns uxbox.services.mutations.pages
|
||||
(:require
|
||||
[clojure.spec.alpha :as s]
|
||||
[promesa.core :as p]
|
||||
[uxbox.db :as db]
|
||||
[uxbox.util.spec :as us]
|
||||
[uxbox.services.core :as sv]
|
||||
[uxbox.services.mutations :as sm]
|
||||
[uxbox.services.util :as su]
|
||||
[uxbox.services.queries.pages :refer [decode-row]]
|
||||
[uxbox.util.sql :as sql]
|
||||
[uxbox.util.time :as dt]
|
||||
[uxbox.util.blob :as blob]
|
||||
[uxbox.util.uuid :as uuid]))
|
||||
|
||||
;; --- Helpers & Specs
|
||||
|
||||
(declare decode-row)
|
||||
|
||||
;; TODO: validate `:data` and `:metadata`
|
||||
|
||||
(s/def ::id ::us/uuid)
|
||||
|
@ -29,84 +28,23 @@
|
|||
(s/def ::project-id ::us/uuid)
|
||||
(s/def ::metadata any?)
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Queries
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
;; --- Query: Pages by Project
|
||||
|
||||
(s/def ::pages-by-project
|
||||
(s/keys :req-un [::user ::project-id]))
|
||||
|
||||
(sv/defquery ::pages-by-project
|
||||
[{:keys [user project-id] :as params}]
|
||||
(let [sql "select pg.*,
|
||||
pg.data,
|
||||
pg.metadata
|
||||
from pages as pg
|
||||
where pg.user_id = $2
|
||||
and pg.project_id = $1
|
||||
and pg.deleted_at is null
|
||||
order by pg.created_at asc;"]
|
||||
(-> (db/query db/pool [sql project-id user])
|
||||
(p/then #(mapv decode-row %)))))
|
||||
|
||||
;; --- Query: Page by Id
|
||||
|
||||
(s/def ::page
|
||||
(s/keys :req-un [::user ::id]))
|
||||
|
||||
(sv/defquery ::page
|
||||
[{:keys [user id] :as params}]
|
||||
(let [sql "select pg.*,
|
||||
pg.data,
|
||||
pg.metadata
|
||||
from pages as pg
|
||||
where pg.user_id = $2
|
||||
and pg.id = $1
|
||||
and pg.deleted_at is null"]
|
||||
(-> (db/query-one db/pool [sql id user])
|
||||
(p/then' decode-row))))
|
||||
|
||||
;; --- Query: Page History
|
||||
|
||||
(s/def ::page-id ::us/uuid)
|
||||
(s/def ::max ::us/integer)
|
||||
(s/def ::pinned ::us/boolean)
|
||||
(s/def ::since ::us/integer)
|
||||
|
||||
(s/def ::page-history
|
||||
(s/keys :req-un [::page-id ::user]
|
||||
:opt-un [::max ::pinned ::since]))
|
||||
|
||||
(sv/defquery ::page-history
|
||||
[{:keys [page-id user since max pinned] :or {since Long/MAX_VALUE max 10}}]
|
||||
(let [sql (-> (sql/from ["pages_history" "ph"])
|
||||
(sql/select "ph.*")
|
||||
(sql/where ["ph.user_id = ?" user]
|
||||
["ph.page_id = ?" page-id]
|
||||
["ph.version < ?" since]
|
||||
(when pinned
|
||||
["ph.pinned = ?" true]))
|
||||
(sql/order "ph.version desc")
|
||||
(sql/limit max))]
|
||||
(-> (db/query db/pool (sql/fmt sql))
|
||||
(p/then (partial mapv decode-row)))))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Mutations
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
;; --- Mutation: Create Page
|
||||
|
||||
(declare create-page)
|
||||
|
||||
(s/def ::create-page
|
||||
(s/keys :req-un [::data ::user ::project-id ::name ::metadata]
|
||||
:opt-un [::id]))
|
||||
|
||||
(sv/defmutation ::create-page
|
||||
[{:keys [id user project-id name data metadata]}]
|
||||
(let [sql "insert into pages (id, user_id, project_id, name, data, metadata)
|
||||
values ($1, $2, $3, $4, $5, $6) returning *"
|
||||
(sm/defmutation ::create-page
|
||||
[params]
|
||||
(create-page db/pool params))
|
||||
|
||||
(defn create-page
|
||||
[conn {:keys [id user project-id name data metadata] :as params}]
|
||||
(let [sql "insert into pages (id, user_id, project_id, name, data, metadata, version)
|
||||
values ($1, $2, $3, $4, $5, $6, 0)
|
||||
returning *"
|
||||
id (or id (uuid/next))
|
||||
data (blob/encode data)
|
||||
mdata (blob/encode metadata)]
|
||||
|
@ -125,7 +63,7 @@
|
|||
and deleted_at is null
|
||||
for update;"]
|
||||
(-> (db/query-one conn [sql id])
|
||||
(p/then' sv/raise-not-found-if-nil))))
|
||||
(p/then' su/raise-not-found-if-nil))))
|
||||
|
||||
(update-page [conn {:keys [id name version data metadata user]}]
|
||||
(let [sql "update pages
|
||||
|
@ -136,15 +74,15 @@
|
|||
where id = $5
|
||||
and user_id = $6"]
|
||||
(-> (db/query-one conn [sql name version data metadata id user])
|
||||
(p/then' sv/constantly-nil))))
|
||||
(p/then' su/constantly-nil))))
|
||||
|
||||
(update-history [conn {:keys [user id version data metadata]}]
|
||||
(let [sql "insert into pages_history (user_id, page_id, version, data, metadata)
|
||||
values ($1, $2, $3, $4, $5)"]
|
||||
(-> (db/query-one conn [sql user id version data metadata])
|
||||
(p/then' sv/constantly-nil))))]
|
||||
(p/then' su/constantly-nil))))]
|
||||
|
||||
(sv/defmutation ::update-page
|
||||
(sm/defmutation ::update-page
|
||||
[{:keys [id data metadata] :as params}]
|
||||
(db/with-atomic [conn db/pool]
|
||||
(-> (select-for-update conn id)
|
||||
|
@ -166,7 +104,7 @@
|
|||
(s/def ::update-page-metadata
|
||||
(s/keys :req-un [::user ::project-id ::name ::metadata ::id]))
|
||||
|
||||
(sv/defmutation ::update-page-metadata
|
||||
(sm/defmutation ::update-page-metadata
|
||||
[{:keys [id user project-id name metadata]}]
|
||||
(let [sql "update pages
|
||||
set name = $3,
|
||||
|
@ -184,7 +122,7 @@
|
|||
(s/def ::delete-page
|
||||
(s/keys :req-un [::user ::id]))
|
||||
|
||||
(sv/defmutation ::delete-page
|
||||
(sm/defmutation ::delete-page
|
||||
[{:keys [id user]}]
|
||||
(let [sql "update pages
|
||||
set deleted_at = clock_timestamp()
|
||||
|
@ -193,8 +131,8 @@
|
|||
and deleted_at is null
|
||||
returning id"]
|
||||
(-> (db/query-one db/pool [sql id user])
|
||||
(p/then sv/raise-not-found-if-nil)
|
||||
(p/then sv/constantly-nil))))
|
||||
(p/then su/raise-not-found-if-nil)
|
||||
(p/then su/constantly-nil))))
|
||||
|
||||
;; ;; --- Update Page History
|
||||
|
||||
|
@ -211,29 +149,9 @@
|
|||
;; (s/def ::update-page-history
|
||||
;; (s/keys :req-un [::user ::id ::pinned ::label]))
|
||||
|
||||
;; (sv/defmutation :update-page-history
|
||||
;; (sm/defmutation :update-page-history
|
||||
;; {:doc "Update page history"
|
||||
;; :spec ::update-page-history}
|
||||
;; [params]
|
||||
;; (with-open [conn (db/connection)]
|
||||
;; (update-page-history conn params)))
|
||||
|
||||
;; --- Helpers
|
||||
|
||||
(defn- decode-row
|
||||
[{:keys [data metadata] :as row}]
|
||||
(when row
|
||||
(cond-> row
|
||||
data (assoc :data (blob/decode data))
|
||||
metadata (assoc :metadata (blob/decode metadata)))))
|
||||
|
||||
;; select pg.* from pages as pg
|
||||
;; where pg.id = :id
|
||||
;; and pg.deleted_at is null;
|
||||
|
||||
;; (defn get-page-by-id
|
||||
;; [conn id]
|
||||
;; (s/assert ::us/id id)
|
||||
;; (let [sqlv (sql/get-page-by-id {:id id})]
|
||||
;; (some-> (db/fetch-one conn sqlv)
|
||||
;; (decode-row))))
|
|
@ -4,7 +4,7 @@
|
|||
;;
|
||||
;; Copyright (c) 2016 Andrey Antukh <niwi@niwi.nz>
|
||||
|
||||
(ns uxbox.services.users
|
||||
(ns uxbox.services.mutations.profiles
|
||||
(:require
|
||||
[buddy.hashers :as hashers]
|
||||
[clojure.spec.alpha :as s]
|
||||
|
@ -17,7 +17,12 @@
|
|||
[uxbox.emails :as emails]
|
||||
[uxbox.images :as images]
|
||||
[uxbox.media :as media]
|
||||
[uxbox.services.core :as sv]
|
||||
[uxbox.services.mutations :as sm]
|
||||
[uxbox.services.util :as su]
|
||||
[uxbox.services.queries.profiles :refer [get-profile
|
||||
decode-profile-row
|
||||
strip-private-attrs
|
||||
resolve-thumbnail]]
|
||||
[uxbox.util.blob :as blob]
|
||||
[uxbox.util.exceptions :as ex]
|
||||
[uxbox.util.spec :as us]
|
||||
|
@ -27,9 +32,6 @@
|
|||
|
||||
;; --- Helpers & Specs
|
||||
|
||||
(declare decode-profile-row)
|
||||
(declare strip-private-attrs)
|
||||
|
||||
(s/def ::email ::us/email)
|
||||
(s/def ::fullname ::us/string)
|
||||
(s/def ::metadata any?)
|
||||
|
@ -39,42 +41,6 @@
|
|||
(s/def ::user ::us/uuid)
|
||||
(s/def ::username ::us/string)
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Queries
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
;; --- Query: Profile (own)
|
||||
|
||||
(defn- resolve-thumbnail
|
||||
[user]
|
||||
(let [opts {:src :photo
|
||||
:dst :photo
|
||||
:size [100 100]
|
||||
:quality 90
|
||||
:format "jpg"}]
|
||||
(-> (px/submit! #(images/populate-thumbnails user opts))
|
||||
(sv/handle-on-context))))
|
||||
|
||||
(defn- get-profile
|
||||
[conn id]
|
||||
(let [sql "select * from users where id=$1 and deleted_at is null"]
|
||||
(-> (db/query-one db/pool [sql id])
|
||||
(p/then' decode-profile-row))))
|
||||
|
||||
(s/def ::profile
|
||||
(s/keys :req-un [::user]))
|
||||
|
||||
(sv/defquery :profile
|
||||
{:doc "Retrieve the user profile."
|
||||
:spec ::profile}
|
||||
[{:keys [user] :as params}]
|
||||
(-> (get-profile db/pool user)
|
||||
(p/then' strip-private-attrs)))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Mutations
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
;; --- Mutation: Update Profile (own)
|
||||
|
||||
(defn- check-username-and-email!
|
||||
|
@ -110,14 +76,14 @@
|
|||
and deleted_at is null
|
||||
returning *"]
|
||||
(-> (db/query-one conn [sql id username email fullname (blob/encode metadata)])
|
||||
(p/then' sv/raise-not-found-if-nil)
|
||||
(p/then' su/raise-not-found-if-nil)
|
||||
(p/then' decode-profile-row)
|
||||
(p/then' strip-private-attrs))))
|
||||
|
||||
(s/def ::update-profile
|
||||
(s/keys :req-un [::id ::username ::email ::fullname ::metadata]))
|
||||
|
||||
(sv/defmutation :update-profile
|
||||
(sm/defmutation :update-profile
|
||||
{:doc "Update self profile."
|
||||
:spec ::update-profile}
|
||||
[params]
|
||||
|
@ -144,13 +110,13 @@
|
|||
and deleted_at is null
|
||||
returning id"]
|
||||
(-> (db/query-one conn [sql user password])
|
||||
(p/then' sv/raise-not-found-if-nil)
|
||||
(p/then' sv/constantly-nil))))
|
||||
(p/then' su/raise-not-found-if-nil)
|
||||
(p/then' su/constantly-nil))))
|
||||
|
||||
(s/def ::update-password
|
||||
(s/keys :req-un [::user ::us/password ::old-password]))
|
||||
|
||||
(sv/defmutation :update-password
|
||||
(sm/defmutation :update-password
|
||||
{:doc "Update self password."
|
||||
:spec ::update-password}
|
||||
[params]
|
||||
|
@ -168,7 +134,7 @@
|
|||
(def valid-image-types?
|
||||
#{"image/jpeg", "image/png", "image/webp"})
|
||||
|
||||
(sv/defmutation :update-profile-photo
|
||||
(sm/defmutation :update-profile-photo
|
||||
{:doc "Update profile photo."
|
||||
:spec ::update-profile-photo}
|
||||
[{:keys [user file] :as params}]
|
||||
|
@ -176,7 +142,7 @@
|
|||
(let [filename (fs/name name)
|
||||
storage media/images-storage]
|
||||
(-> (ds/save storage filename path)
|
||||
#_(sv/handle-on-context))))
|
||||
#_(su/handle-on-context))))
|
||||
|
||||
(update-user-photo [path]
|
||||
(let [sql "update users
|
||||
|
@ -185,7 +151,7 @@
|
|||
and deleted_at is null
|
||||
returning *"]
|
||||
(-> (db/query-one db/pool [sql (str path) user])
|
||||
(p/then' sv/raise-not-found-if-nil)
|
||||
(p/then' su/raise-not-found-if-nil)
|
||||
(p/then' strip-private-attrs)
|
||||
(p/then resolve-thumbnail))))]
|
||||
|
||||
|
@ -216,33 +182,38 @@
|
|||
:code ::username-or-email-already-exists))
|
||||
params)))))
|
||||
|
||||
(defn- register-profile
|
||||
(defn create-profile
|
||||
"Create the user entry on the database with limited input
|
||||
filling all the other fields with defaults."
|
||||
[conn {:keys [username fullname email password] :as params}]
|
||||
(let [metadata (blob/encode {})
|
||||
[conn {:keys [id username fullname email password metadata] :as params}]
|
||||
(let [id (or id (uuid/next))
|
||||
metadata (blob/encode metadata)
|
||||
password (hashers/encrypt password)
|
||||
sqlv [create-user-sql
|
||||
(uuid/next)
|
||||
id
|
||||
fullname
|
||||
username
|
||||
email
|
||||
password
|
||||
metadata]]
|
||||
(-> (db/query-one conn sqlv)
|
||||
(p/then' decode-profile-row)
|
||||
(p/then' strip-private-attrs)
|
||||
#_(p/then (fn [profile]
|
||||
(p/then' decode-profile-row))))
|
||||
|
||||
(defn register-profile
|
||||
[conn params]
|
||||
(-> (create-profile conn params)
|
||||
(p/then' strip-private-attrs)
|
||||
#_(p/then (fn [profile]
|
||||
(-> (emails/send! {::emails/id :users/register
|
||||
::emails/to (:email params)
|
||||
::emails/priority :high
|
||||
:name (:fullname params)})
|
||||
(p/then' (constantly profile))))))))
|
||||
(p/then' (constantly profile)))))))
|
||||
|
||||
(s/def ::register-profile
|
||||
(s/keys :req-un [::username ::email ::password ::fullname]))
|
||||
|
||||
(sv/defmutation :register-profile
|
||||
(sm/defmutation :register-profile
|
||||
{:doc "Register new user."
|
||||
:spec ::register-profile}
|
||||
[params]
|
||||
|
@ -366,16 +337,3 @@
|
|||
;; (some-> (db/fetch-one conn sqlv)
|
||||
;; (trim-user-attrs))))
|
||||
|
||||
;; --- Attrs Helpers
|
||||
|
||||
(defn- decode-profile-row
|
||||
[{:keys [metadata] :as row}]
|
||||
(when row
|
||||
(cond-> row
|
||||
metadata (assoc :metadata (blob/decode metadata)))))
|
||||
|
||||
(defn strip-private-attrs
|
||||
"Only selects a publicy visible user attrs."
|
||||
[profile]
|
||||
(select-keys profile [:id :username :fullname :metadata
|
||||
:email :created-at :photo]))
|
83
backend/src/uxbox/services/mutations/projects.clj
Normal file
83
backend/src/uxbox/services/mutations/projects.clj
Normal file
|
@ -0,0 +1,83 @@
|
|||
;; This Source Code Form is subject to the terms of the Mozilla Public
|
||||
;; License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
;;
|
||||
;; Copyright (c) 2019 Andrey Antukh <niwi@niwi.nz>
|
||||
|
||||
(ns uxbox.services.mutations.projects
|
||||
(:require
|
||||
[clojure.spec.alpha :as s]
|
||||
[promesa.core :as p]
|
||||
[uxbox.db :as db]
|
||||
[uxbox.util.spec :as us]
|
||||
[uxbox.services.mutations :as sm]
|
||||
[uxbox.services.util :as su]
|
||||
[uxbox.util.blob :as blob]
|
||||
[uxbox.util.uuid :as uuid]))
|
||||
|
||||
;; --- Helpers & Specs
|
||||
|
||||
(s/def ::id ::us/uuid)
|
||||
(s/def ::name ::us/string)
|
||||
(s/def ::token ::us/string)
|
||||
(s/def ::user ::us/uuid)
|
||||
|
||||
;; --- Mutation: Create Project
|
||||
|
||||
(declare create-project)
|
||||
|
||||
(s/def ::create-project
|
||||
(s/keys :req-un [::user ::name]
|
||||
:opt-un [::id]))
|
||||
|
||||
;; TODO: create role on project creation (maybe in DB?)
|
||||
|
||||
(sm/defmutation ::create-project
|
||||
[{:keys [id user name] :as params}]
|
||||
(let [id (or id (uuid/next))
|
||||
sql "insert into projects (id, user_id, name)
|
||||
values ($1, $2, $3) returning *"]
|
||||
(db/query-one db/pool [sql id user name])))
|
||||
|
||||
(defn create-project
|
||||
[conn {:keys [id user name] :as params}]
|
||||
(let [id (or id (uuid/next))
|
||||
sql "insert into projects (id, user_id, name)
|
||||
values ($1, $2, $3) returning *"]
|
||||
(db/query-one conn [sql id user name])))
|
||||
|
||||
;; --- Mutation: Update Project
|
||||
|
||||
(s/def ::update-project
|
||||
(s/keys :req-un [::user ::name ::id]))
|
||||
|
||||
(sm/defmutation :update-project
|
||||
{:doc "Update project."
|
||||
:spec ::update-project}
|
||||
[{:keys [id name user] :as params}]
|
||||
(let [sql "update projects
|
||||
set name = $3
|
||||
where id = $1
|
||||
and user_id = $2
|
||||
and deleted_at is null
|
||||
returning *"]
|
||||
(db/query-one db/pool [sql id user name])))
|
||||
|
||||
;; --- Mutation: Delete Project
|
||||
|
||||
(s/def ::delete-project
|
||||
(s/keys :req-un [::id ::user]))
|
||||
|
||||
(sm/defmutation :delete-project
|
||||
{:doc "Delete project"
|
||||
:spec ::delete-project}
|
||||
[{:keys [id user] :as params}]
|
||||
(let [sql "update projects
|
||||
set deleted_at = clock_timestamp()
|
||||
where id = $1
|
||||
and user_id = $2
|
||||
and deleted_at is null
|
||||
returning id"]
|
||||
(-> (db/query-one db/pool [sql id user])
|
||||
(p/then' su/raise-not-found-if-nil)
|
||||
(p/then' su/constantly-nil))))
|
48
backend/src/uxbox/services/mutations/user_storage.clj
Normal file
48
backend/src/uxbox/services/mutations/user_storage.clj
Normal file
|
@ -0,0 +1,48 @@
|
|||
;; This Source Code Form is subject to the terms of the Mozilla Public
|
||||
;; License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
;;
|
||||
;; Copyright (c) 2019 Andrey Antukh <niwi@niwi.nz>
|
||||
|
||||
(ns uxbox.services.mutations.user-storage
|
||||
(:require
|
||||
[clojure.spec.alpha :as s]
|
||||
[promesa.core :as p]
|
||||
[uxbox.db :as db]
|
||||
[uxbox.services.mutations :as sm]
|
||||
[uxbox.services.util :as su]
|
||||
[uxbox.services.queries.user-storage :refer [decode-row]]
|
||||
[uxbox.util.blob :as blob]
|
||||
[uxbox.util.spec :as us]))
|
||||
|
||||
;; --- Update
|
||||
|
||||
(s/def ::user ::us/uuid)
|
||||
(s/def ::key ::us/string)
|
||||
(s/def ::val any?)
|
||||
|
||||
(s/def ::upsert-user-storage-entry
|
||||
(s/keys :req-un [::key ::val ::user]))
|
||||
|
||||
(sm/defmutation ::upsert-user-storage-entry
|
||||
[{:keys [key val user] :as params}]
|
||||
(let [sql "insert into user_storage (key, val, user_id)
|
||||
values ($1, $2, $3)
|
||||
on conflict (user_id, key)
|
||||
do update set val = $2"
|
||||
val (blob/encode val)]
|
||||
(-> (db/query-one db/pool [sql key val user])
|
||||
(p/then' su/constantly-nil))))
|
||||
|
||||
;; --- Delete KVStore
|
||||
|
||||
(s/def ::delete-user-storage-entry
|
||||
(s/keys :req-un [::key ::user]))
|
||||
|
||||
(sm/defmutation ::delete-user-storage-entry
|
||||
[{:keys [user key] :as params}]
|
||||
(let [sql "delete from user_storage
|
||||
where user_id = $2
|
||||
and key = $1"]
|
||||
(-> (db/query-one db/pool [sql key user])
|
||||
(p/then' su/constantly-nil))))
|
|
@ -1,133 +0,0 @@
|
|||
;; This Source Code Form is subject to the terms of the Mozilla Public
|
||||
;; License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
;;
|
||||
;; Copyright (c) 2019 Andrey Antukh <niwi@niwi.nz>
|
||||
|
||||
(ns uxbox.services.projects
|
||||
(:require
|
||||
[clojure.spec.alpha :as s]
|
||||
[promesa.core :as p]
|
||||
[uxbox.db :as db]
|
||||
[uxbox.util.spec :as us]
|
||||
[uxbox.services.core :as sv]
|
||||
[uxbox.util.blob :as blob]
|
||||
[uxbox.util.uuid :as uuid]))
|
||||
|
||||
;; --- Helpers & Specs
|
||||
|
||||
(s/def ::id ::us/uuid)
|
||||
(s/def ::name ::us/string)
|
||||
(s/def ::token ::us/string)
|
||||
(s/def ::user ::us/uuid)
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Queries
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
;; --- Query: Projects
|
||||
|
||||
(s/def ::projects-query
|
||||
(s/keys :req-un [::user]))
|
||||
|
||||
(sv/defquery :projects
|
||||
{:doc "Query all projects"
|
||||
:spec ::projects-query}
|
||||
[{:keys [user] :as params}]
|
||||
(let [sql "select distinct on (p.id, p.created_at)
|
||||
p.*,
|
||||
array_agg(pg.id) over (
|
||||
partition by p.id
|
||||
order by pg.created_at
|
||||
range between unbounded preceding and unbounded following
|
||||
) as pages
|
||||
from projects as p
|
||||
right join pages as pg
|
||||
on (pg.project_id = p.id)
|
||||
where p.user_id = $1
|
||||
order by p.created_at asc"]
|
||||
(-> (db/query db/pool [sql user])
|
||||
(p/then (fn [rows]
|
||||
(mapv #(update % :pages vec) rows))))))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Mutations
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
;; --- Mutation: Create Project
|
||||
|
||||
(s/def ::create-project
|
||||
(s/keys :req-un [::user ::name]
|
||||
:opt-un [::id]))
|
||||
|
||||
(sv/defmutation :create-project
|
||||
{:doc "Create a project."
|
||||
:spec ::create-project}
|
||||
[{:keys [id user name] :as params}]
|
||||
(let [id (or id (uuid/next))
|
||||
sql "insert into projects (id, user_id, name)
|
||||
values ($1, $2, $3) returning *"]
|
||||
(db/query-one db/pool [sql id user name])))
|
||||
|
||||
;; --- Mutation: Update Project
|
||||
|
||||
(s/def ::update-project
|
||||
(s/keys :req-un [::user ::name ::id]))
|
||||
|
||||
(sv/defmutation :update-project
|
||||
{:doc "Update project."
|
||||
:spec ::update-project}
|
||||
[{:keys [id name user] :as params}]
|
||||
(let [sql "update projects
|
||||
set name = $3
|
||||
where id = $1
|
||||
and user_id = $2
|
||||
and deleted_at is null
|
||||
returning *"]
|
||||
(db/query-one db/pool [sql id user name])))
|
||||
|
||||
;; --- Mutation: Delete Project
|
||||
|
||||
(s/def ::delete-project
|
||||
(s/keys :req-un [::id ::user]))
|
||||
|
||||
(sv/defmutation :delete-project
|
||||
{:doc "Delete project"
|
||||
:spec ::delete-project}
|
||||
[{:keys [id user] :as params}]
|
||||
(let [sql "update projects
|
||||
set deleted_at = clock_timestamp()
|
||||
where id = $1
|
||||
and user_id = $2
|
||||
and deleted_at is null
|
||||
returning id"]
|
||||
(-> (db/query-one db/pool [sql id user])
|
||||
(p/then' sv/raise-not-found-if-nil)
|
||||
(p/then' sv/constantly-nil))))
|
||||
|
||||
|
||||
;; --- Retrieve Project by share token
|
||||
|
||||
;; (defn- get-project-by-share-token
|
||||
;; [conn token]
|
||||
;; (let [sqlv (sql/get-project-by-share-token {:token token})
|
||||
;; project (some-> (db/fetch-one conn sqlv)
|
||||
;; (data/normalize))]
|
||||
;; (when-let [id (:id project)]
|
||||
;; (let [pages (vec (pages/get-pages-for-project conn id))]
|
||||
;; (assoc project :pages pages)))))
|
||||
|
||||
;; (defmethod core/query :retrieve-project-by-share-token
|
||||
;; [{:keys [token]}]
|
||||
;; (s/assert ::token token)
|
||||
;; (with-open [conn (db/connection)]
|
||||
;; (get-project-by-share-token conn token)))
|
||||
|
||||
;; --- Retrieve share tokens
|
||||
|
||||
;; (defn get-share-tokens-for-project
|
||||
;; [conn project]
|
||||
;; (s/assert ::project project)
|
||||
;; (let [sqlv (sql/get-share-tokens-for-project {:project project})]
|
||||
;; (db/fetch conn sqlv)))
|
||||
|
19
backend/src/uxbox/services/queries.clj
Normal file
19
backend/src/uxbox/services/queries.clj
Normal file
|
@ -0,0 +1,19 @@
|
|||
;; This Source Code Form is subject to the terms of the Mozilla Public
|
||||
;; License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
;;
|
||||
;; Copyright (c) 2019 Andrey Antukh <niwi@niwi.nz>
|
||||
|
||||
(ns uxbox.services.queries
|
||||
(:require
|
||||
[uxbox.util.dispatcher :as uds]))
|
||||
|
||||
(uds/defservice handle
|
||||
{:dispatch-by ::type
|
||||
:interceptors [uds/spec-interceptor
|
||||
#_logging-interceptor
|
||||
#_context-interceptor]})
|
||||
|
||||
(defmacro defquery
|
||||
[key & rest]
|
||||
`(uds/defmethod handle ~key ~@rest))
|
69
backend/src/uxbox/services/queries/icons.clj
Normal file
69
backend/src/uxbox/services/queries/icons.clj
Normal file
|
@ -0,0 +1,69 @@
|
|||
;; This Source Code Form is subject to the terms of the Mozilla Public
|
||||
;; License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
;;
|
||||
;; Copyright (c) 2019 Andrey Antukh <niwi@niwi.nz>
|
||||
|
||||
(ns uxbox.services.queries.icons
|
||||
(:require
|
||||
[clojure.spec.alpha :as s]
|
||||
[promesa.core :as p]
|
||||
[uxbox.db :as db]
|
||||
[uxbox.services.queries :as sq]
|
||||
[uxbox.util.blob :as blob]
|
||||
[uxbox.util.exceptions :as ex]
|
||||
[uxbox.util.spec :as us]))
|
||||
|
||||
;; --- Helpers & Specs
|
||||
|
||||
(s/def ::id ::us/uuid)
|
||||
(s/def ::user ::us/uuid)
|
||||
(s/def ::collection-id (s/nilable ::us/uuid))
|
||||
|
||||
(defn decode-icon-row
|
||||
[{:keys [metadata] :as row}]
|
||||
(when row
|
||||
(cond-> row
|
||||
metadata (assoc :metadata (blob/decode metadata)))))
|
||||
|
||||
;; --- Query: Collections
|
||||
|
||||
(def ^:private icons-collections-sql
|
||||
"select *,
|
||||
(select count(*) from icons where collection_id = ic.id) as num_icons
|
||||
from icons_collections as ic
|
||||
where (ic.user_id = $1 or
|
||||
ic.user_id = '00000000-0000-0000-0000-000000000000'::uuid)
|
||||
and ic.deleted_at is null
|
||||
order by ic.created_at desc")
|
||||
|
||||
(s/def ::icons-collections
|
||||
(s/keys :req-un [::user]))
|
||||
|
||||
(sq/defquery ::icons-collections
|
||||
[{:keys [user] :as params}]
|
||||
(let [sqlv [icons-collections-sql user]]
|
||||
(db/query db/pool sqlv)))
|
||||
|
||||
;; --- Icons By Collection ID
|
||||
|
||||
(def ^:private icons-by-collection-sql
|
||||
"select *
|
||||
from icons as i
|
||||
where (i.user_id = $1 or
|
||||
i.user_id = '00000000-0000-0000-0000-000000000000'::uuid)
|
||||
and i.deleted_at is null
|
||||
and case when $2::uuid is null then i.collection_id is null
|
||||
else i.collection_id = $2::uuid
|
||||
end
|
||||
order by i.created_at desc")
|
||||
|
||||
(s/def ::icons-by-collection
|
||||
(s/keys :req-un [::user]
|
||||
:opt-un [::collection-id]))
|
||||
|
||||
(sq/defquery ::icons-by-collection
|
||||
[{:keys [user collection-id] :as params}]
|
||||
(let [sqlv [icons-by-collection-sql user collection-id]]
|
||||
(-> (db/query db/pool sqlv)
|
||||
(p/then' #(mapv decode-icon-row %)))))
|
109
backend/src/uxbox/services/queries/images.clj
Normal file
109
backend/src/uxbox/services/queries/images.clj
Normal file
|
@ -0,0 +1,109 @@
|
|||
;; This Source Code Form is subject to the terms of the Mozilla Public
|
||||
;; License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
;;
|
||||
;; Copyright (c) 2019 Andrey Antukh <niwi@niwi.nz>
|
||||
|
||||
(ns uxbox.services.queries.images
|
||||
(:require
|
||||
[clojure.spec.alpha :as s]
|
||||
[promesa.core :as p]
|
||||
[promesa.exec :as px]
|
||||
[uxbox.db :as db]
|
||||
[uxbox.media :as media]
|
||||
[uxbox.images :as images]
|
||||
[uxbox.services.queries :as sq]
|
||||
[uxbox.services.util :as su]
|
||||
[uxbox.util.blob :as blob]
|
||||
[uxbox.util.data :as data]
|
||||
[uxbox.util.exceptions :as ex]
|
||||
[uxbox.util.spec :as us]
|
||||
[uxbox.util.uuid :as uuid]
|
||||
[vertx.core :as vc]))
|
||||
|
||||
(def +thumbnail-options+
|
||||
{:src :path
|
||||
:dst :thumbnail
|
||||
:width 300
|
||||
:height 100
|
||||
:quality 92
|
||||
:format "webp"})
|
||||
|
||||
(defn populate-thumbnail
|
||||
[row]
|
||||
(let [opts +thumbnail-options+]
|
||||
(-> (px/submit! #(images/populate-thumbnails row opts))
|
||||
(su/handle-on-context))))
|
||||
|
||||
(defn populate-thumbnails
|
||||
[rows]
|
||||
(if (empty? rows)
|
||||
rows
|
||||
(p/all (map populate-thumbnail rows))))
|
||||
|
||||
(defn populate-urls
|
||||
[row]
|
||||
(images/populate-urls row media/images-storage :path :url))
|
||||
|
||||
(s/def ::id ::us/uuid)
|
||||
(s/def ::name ::us/string)
|
||||
(s/def ::user ::us/uuid)
|
||||
(s/def ::collection-id (s/nilable ::us/uuid))
|
||||
|
||||
(def ^:private images-collections-sql
|
||||
"select *,
|
||||
(select count(*) from images where collection_id = ic.id) as num_images
|
||||
from images_collections as ic
|
||||
where (ic.user_id = $1 or
|
||||
ic.user_id = '00000000-0000-0000-0000-000000000000'::uuid)
|
||||
and ic.deleted_at is null
|
||||
order by ic.created_at desc;")
|
||||
|
||||
(s/def ::images-collections
|
||||
(s/keys :req-un [::user]))
|
||||
|
||||
(sq/defquery ::images-collections
|
||||
[{:keys [user] :as params}]
|
||||
(db/query db/pool [images-collections-sql user]))
|
||||
|
||||
;; --- Retrieve Image
|
||||
|
||||
(defn retrieve-image
|
||||
[conn id]
|
||||
(let [sql "select * from images
|
||||
where id = $1
|
||||
and deleted_at is null;"]
|
||||
(db/query-one conn [sql id])))
|
||||
|
||||
;; (s/def ::retrieve-image
|
||||
;; (s/keys :req-un [::user ::us/id]))
|
||||
|
||||
;; (defmethod core/query :retrieve-image
|
||||
;; [params]
|
||||
;; (s/assert ::retrieve-image params)
|
||||
;; (with-open [conn (db/connection)]
|
||||
;; (retrieve-image conn params)))
|
||||
|
||||
;; --- Query Images by Collection (id)
|
||||
|
||||
(def images-by-collection-sql
|
||||
"select * from images
|
||||
where (user_id = $1 or
|
||||
user_id = '00000000-0000-0000-0000-000000000000'::uuid)
|
||||
and deleted_at is null
|
||||
and case when $2::uuid is null then collection_id is null
|
||||
else collection_id = $2::uuid
|
||||
end
|
||||
order by created_at desc;")
|
||||
|
||||
(s/def ::images-by-collection-query
|
||||
(s/keys :req-un [::user]
|
||||
:opt-un [::collection-id]))
|
||||
|
||||
(sq/defquery ::images-by-collection
|
||||
[{:keys [user collection-id] :as params}]
|
||||
(let [sqlv [images-by-collection-sql user collection-id]]
|
||||
(-> (db/query db/pool sqlv)
|
||||
(p/then populate-thumbnails)
|
||||
(p/then #(mapv populate-urls %)))))
|
||||
|
92
backend/src/uxbox/services/queries/pages.clj
Normal file
92
backend/src/uxbox/services/queries/pages.clj
Normal file
|
@ -0,0 +1,92 @@
|
|||
;; This Source Code Form is subject to the terms of the Mozilla Public
|
||||
;; License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
;;
|
||||
;; Copyright (c) 2019 Andrey Antukh <niwi@niwi.nz>
|
||||
|
||||
(ns uxbox.services.queries.pages
|
||||
(:require
|
||||
[clojure.spec.alpha :as s]
|
||||
[promesa.core :as p]
|
||||
[uxbox.db :as db]
|
||||
[uxbox.services.queries :as sq]
|
||||
[uxbox.util.blob :as blob]
|
||||
[uxbox.util.spec :as us]
|
||||
[uxbox.util.sql :as sql]))
|
||||
|
||||
;; --- Helpers & Specs
|
||||
|
||||
(declare decode-row)
|
||||
|
||||
(s/def ::id ::us/uuid)
|
||||
(s/def ::user ::us/uuid)
|
||||
(s/def ::project-id ::us/uuid)
|
||||
|
||||
;; --- Query: Pages by Project
|
||||
|
||||
(s/def ::pages-by-project
|
||||
(s/keys :req-un [::user ::project-id]))
|
||||
|
||||
(sq/defquery ::pages-by-project
|
||||
[{:keys [user project-id] :as params}]
|
||||
(let [sql "select pg.*,
|
||||
pg.data,
|
||||
pg.metadata
|
||||
from pages as pg
|
||||
where pg.user_id = $2
|
||||
and pg.project_id = $1
|
||||
and pg.deleted_at is null
|
||||
order by pg.created_at asc;"]
|
||||
(-> (db/query db/pool [sql project-id user])
|
||||
(p/then #(mapv decode-row %)))))
|
||||
|
||||
;; --- Query: Page by Id
|
||||
|
||||
(s/def ::page
|
||||
(s/keys :req-un [::user ::id]))
|
||||
|
||||
(sq/defquery ::page
|
||||
[{:keys [user id] :as params}]
|
||||
(let [sql "select pg.*,
|
||||
pg.data,
|
||||
pg.metadata
|
||||
from pages as pg
|
||||
where pg.user_id = $2
|
||||
and pg.id = $1
|
||||
and pg.deleted_at is null"]
|
||||
(-> (db/query-one db/pool [sql id user])
|
||||
(p/then' decode-row))))
|
||||
|
||||
;; --- Query: Page History
|
||||
|
||||
(s/def ::page-id ::us/uuid)
|
||||
(s/def ::max ::us/integer)
|
||||
(s/def ::pinned ::us/boolean)
|
||||
(s/def ::since ::us/integer)
|
||||
|
||||
(s/def ::page-history
|
||||
(s/keys :req-un [::page-id ::user]
|
||||
:opt-un [::max ::pinned ::since]))
|
||||
|
||||
(sq/defquery ::page-history
|
||||
[{:keys [page-id user since max pinned] :or {since Long/MAX_VALUE max 10}}]
|
||||
(let [sql (-> (sql/from ["pages_history" "ph"])
|
||||
(sql/select "ph.*")
|
||||
(sql/where ["ph.user_id = ?" user]
|
||||
["ph.page_id = ?" page-id]
|
||||
["ph.version < ?" since]
|
||||
(when pinned
|
||||
["ph.pinned = ?" true]))
|
||||
(sql/order "ph.version desc")
|
||||
(sql/limit max))]
|
||||
(-> (db/query db/pool (sql/fmt sql))
|
||||
(p/then (partial mapv decode-row)))))
|
||||
|
||||
;; --- Helpers
|
||||
|
||||
(defn decode-row
|
||||
[{:keys [data metadata] :as row}]
|
||||
(when row
|
||||
(cond-> row
|
||||
data (assoc :data (blob/decode data))
|
||||
metadata (assoc :metadata (blob/decode metadata)))))
|
73
backend/src/uxbox/services/queries/profiles.clj
Normal file
73
backend/src/uxbox/services/queries/profiles.clj
Normal file
|
@ -0,0 +1,73 @@
|
|||
;; This Source Code Form is subject to the terms of the Mozilla Public
|
||||
;; License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
;;
|
||||
;; Copyright (c) 2016 Andrey Antukh <niwi@niwi.nz>
|
||||
|
||||
(ns uxbox.services.queries.profiles
|
||||
(:require
|
||||
[clojure.spec.alpha :as s]
|
||||
[promesa.core :as p]
|
||||
[promesa.exec :as px]
|
||||
[uxbox.db :as db]
|
||||
[uxbox.images :as images]
|
||||
[uxbox.services.queries :as sq]
|
||||
[uxbox.services.util :as su]
|
||||
[uxbox.util.blob :as blob]
|
||||
[uxbox.util.spec :as us]))
|
||||
|
||||
;; --- Helpers & Specs
|
||||
|
||||
(declare decode-profile-row)
|
||||
(declare strip-private-attrs)
|
||||
|
||||
(s/def ::email ::us/email)
|
||||
(s/def ::fullname ::us/string)
|
||||
(s/def ::metadata any?)
|
||||
(s/def ::old-password ::us/string)
|
||||
(s/def ::password ::us/string)
|
||||
(s/def ::path ::us/string)
|
||||
(s/def ::user ::us/uuid)
|
||||
(s/def ::username ::us/string)
|
||||
|
||||
;; --- Query: Profile (own)
|
||||
|
||||
(defn resolve-thumbnail
|
||||
[user]
|
||||
(let [opts {:src :photo
|
||||
:dst :photo
|
||||
:size [100 100]
|
||||
:quality 90
|
||||
:format "jpg"}]
|
||||
(-> (px/submit! #(images/populate-thumbnails user opts))
|
||||
(su/handle-on-context))))
|
||||
|
||||
(defn get-profile
|
||||
[conn id]
|
||||
(let [sql "select * from users where id=$1 and deleted_at is null"]
|
||||
(-> (db/query-one db/pool [sql id])
|
||||
(p/then' decode-profile-row))))
|
||||
|
||||
(s/def ::profile
|
||||
(s/keys :req-un [::user]))
|
||||
|
||||
(sq/defquery :profile
|
||||
{:doc "Retrieve the user profile."
|
||||
:spec ::profile}
|
||||
[{:keys [user] :as params}]
|
||||
(-> (get-profile db/pool user)
|
||||
(p/then' strip-private-attrs)))
|
||||
|
||||
;; --- Attrs Helpers
|
||||
|
||||
(defn decode-profile-row
|
||||
[{:keys [metadata] :as row}]
|
||||
(when row
|
||||
(cond-> row
|
||||
metadata (assoc :metadata (blob/decode metadata)))))
|
||||
|
||||
(defn strip-private-attrs
|
||||
"Only selects a publicy visible user attrs."
|
||||
[profile]
|
||||
(select-keys profile [:id :username :fullname :metadata
|
||||
:email :created-at :photo]))
|
48
backend/src/uxbox/services/queries/projects.clj
Normal file
48
backend/src/uxbox/services/queries/projects.clj
Normal file
|
@ -0,0 +1,48 @@
|
|||
;; This Source Code Form is subject to the terms of the Mozilla Public
|
||||
;; License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
;;
|
||||
;; Copyright (c) 2019 Andrey Antukh <niwi@niwi.nz>
|
||||
|
||||
(ns uxbox.services.queries.projects
|
||||
(:require
|
||||
[clojure.spec.alpha :as s]
|
||||
[promesa.core :as p]
|
||||
[uxbox.db :as db]
|
||||
[uxbox.services.queries :as sq]
|
||||
[uxbox.util.blob :as blob]
|
||||
[uxbox.util.spec :as us]))
|
||||
|
||||
;; --- Helpers & Specs
|
||||
|
||||
(s/def ::id ::us/uuid)
|
||||
(s/def ::name ::us/string)
|
||||
(s/def ::token ::us/string)
|
||||
(s/def ::user ::us/uuid)
|
||||
|
||||
;; --- Query: Projects
|
||||
|
||||
(def ^:private projects-sql
|
||||
"select distinct on (p.id, p.created_at)
|
||||
p.*,
|
||||
array_agg(pg.id) over (
|
||||
partition by p.id
|
||||
order by pg.created_at
|
||||
range between unbounded preceding and unbounded following
|
||||
) as pages
|
||||
from projects as p
|
||||
left join pages as pg
|
||||
on (pg.project_id = p.id)
|
||||
where p.user_id = $1
|
||||
order by p.created_at asc")
|
||||
|
||||
(s/def ::projects-query
|
||||
(s/keys :req-un [::user]))
|
||||
|
||||
(sq/defquery :projects
|
||||
{:doc "Query all projects"
|
||||
:spec ::projects-query}
|
||||
[{:keys [user] :as params}]
|
||||
(-> (db/query db/pool [projects-sql user])
|
||||
(p/then (fn [rows]
|
||||
(mapv #(update % :pages vec) rows)))))
|
34
backend/src/uxbox/services/queries/user_storage.clj
Normal file
34
backend/src/uxbox/services/queries/user_storage.clj
Normal file
|
@ -0,0 +1,34 @@
|
|||
;; This Source Code Form is subject to the terms of the Mozilla Public
|
||||
;; License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
;;
|
||||
;; Copyright (c) 2019 Andrey Antukh <niwi@niwi.nz>
|
||||
|
||||
(ns uxbox.services.queries.user-storage
|
||||
(:require
|
||||
[clojure.spec.alpha :as s]
|
||||
[promesa.core :as p]
|
||||
[uxbox.db :as db]
|
||||
[uxbox.services.queries :as sq]
|
||||
[uxbox.services.util :as su]
|
||||
[uxbox.util.blob :as blob]
|
||||
[uxbox.util.spec :as us]))
|
||||
|
||||
(defn decode-row
|
||||
[{:keys [val] :as row}]
|
||||
(when row
|
||||
(cond-> row
|
||||
val (assoc :val (blob/decode val)))))
|
||||
|
||||
(s/def ::user-storage-item
|
||||
(s/keys :req-un [::key ::user]))
|
||||
|
||||
(sq/defquery ::user-storage-entry
|
||||
[{:keys [key user]}]
|
||||
(let [sql "select kv.*
|
||||
from user_storage as kv
|
||||
where kv.user_id = $2
|
||||
and kv.key = $1"]
|
||||
(-> (db/query-one db/pool [sql key user])
|
||||
(p/then' su/raise-not-found-if-nil)
|
||||
(p/then' decode-row))))
|
39
backend/src/uxbox/services/util.clj
Normal file
39
backend/src/uxbox/services/util.clj
Normal file
|
@ -0,0 +1,39 @@
|
|||
;; This Source Code Form is subject to the terms of the Mozilla Public
|
||||
;; License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
;;
|
||||
;; Copyright (c) 2019 Andrey Antukh <niwi@niwi.nz>
|
||||
|
||||
(ns uxbox.services.util
|
||||
(:require
|
||||
[clojure.tools.logging :as log]
|
||||
[vertx.core :as vc]
|
||||
[uxbox.core :refer [system]]
|
||||
[uxbox.util.uuid :as uuid]
|
||||
[uxbox.util.dispatcher :as uds]
|
||||
[uxbox.util.exceptions :as ex]))
|
||||
|
||||
;; (def logging-interceptor
|
||||
;; {:enter (fn [data]
|
||||
;; (let [type (get-in data [:request ::type])]
|
||||
;; (assoc data ::start-time (System/nanoTime))))
|
||||
;; :leave (fn [data]
|
||||
;; (let [elapsed (- (System/nanoTime) (::start-time data))
|
||||
;; elapsed (str (quot elapsed 1000000) "ms")
|
||||
;; type (get-in data [:request ::type])]
|
||||
;; (log/info "service" type "processed in" elapsed)
|
||||
;; data))})
|
||||
|
||||
(defn raise-not-found-if-nil
|
||||
[v]
|
||||
(if (nil? v)
|
||||
(ex/raise :type :not-found
|
||||
:hint "Object doest not exists.")
|
||||
v))
|
||||
|
||||
(def constantly-nil (constantly nil))
|
||||
|
||||
(defn handle-on-context
|
||||
[p]
|
||||
(->> (vc/get-or-create-context system)
|
||||
(vc/handle-on-context p)))
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
(ns uxbox.util.migrations
|
||||
(:require
|
||||
[clojure.tools.logging :as log]
|
||||
[clojure.java.io :as io]
|
||||
[clojure.spec.alpha :as s]
|
||||
[cuerdas.core :as str]
|
||||
|
@ -56,7 +57,7 @@
|
|||
(-> (registered? pool modname (:name migration))
|
||||
(p/then (fn [registered?]
|
||||
(when-not registered?
|
||||
(println (str/format "applying migration - %s: %s" modname name))
|
||||
(log/info (str/format "applying migration %s/%s" modname name))
|
||||
(execute)))))))
|
||||
|
||||
(defn- impl-migrate
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue