0
Fork 0
mirror of https://github.com/penpot/penpot.git synced 2025-02-23 07:16:07 -05:00

feat(backend): remove catacumba dependency

This commit is contained in:
Andrey Antukh 2019-06-16 00:57:30 +02:00
parent 910e076e73
commit f64bb74a3e
24 changed files with 91 additions and 1233 deletions

View file

@ -5,7 +5,6 @@
funcool/promesa {:mvn/version "2.0.0"}
funcool/cuerdas {:mvn/version "2.2.0"}
funcool/datoteka {:mvn/version "1.0.0"}
funcool/catacumba {:mvn/version "2.1.0"}
funcool/struct {:mvn/version "1.4.0"}
ring/ring {:mvn/version "1.7.1"}
@ -15,6 +14,7 @@
metosin/reitit-spec {:mvn/version "0.3.7"}
metosin/reitit-dev {:mvn/version "0.3.7"}
danlentz/clj-uuid {:mvn/version "0.1.9"}
org.jsoup/jsoup {:mvn/version "1.10.2"}
hiccup/hiccup {:mvn/version "1.0.5"}
org.im4java/im4java {:mvn/version "1.4.0"}

View file

@ -0,0 +1,67 @@
;; 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.api.debug-emails
"A helper namespace for just render emails."
(:require [clojure.edn :as edn]
[hiccup.page :refer (html5)]
[uxbox.emails :as emails]
[uxbox.emails.core :as emails-core]))
;; (def +available-emails+
;; {:users/register
;; {:name "Cirilla"}
;; :users/password-recovery
;; {:name "Cirilla"
;; :token "agNFhA6SolcFb4Us2NOTNWh0cfFDquVLAav400xQPjw"}})
;; (defn- render-emails-list
;; []
;; (html5
;; [:section {:style "font-family: Monoid, monospace; font-size: 14px;"}
;; [:h1 "Available emails"]
;; [:table {:style "width: 500px;"}
;; [:tbody
;; [:tr
;; (for [[type email] @emails-core/emails]
;; [:tr
;; [:td (pr-str type)]
;; [:td
;; [:a {:href (str "/debug/emails/email?id="
;; (pr-str type)
;; "&type=:text/html")}
;; "(html)"]]
;; [:td
;; [:a {:href (str "/debug/emails/email?id="
;; (pr-str type)
;; "&type=:text/plain")}
;; "(text)"]]])]]]]))
;; (defn list-emails
;; [context]
;; (http/ok (render-emails-list)
;; {:content-type "text/html; charset=utf-8"}))
;; (defn- render-email
;; [type content]
;; (if (= type :text/html)
;; content
;; (html5
;; [:pre content])))
;; (defn show-email
;; [{params :query-params}]
;; (let [id (edn/read-string (:id params))
;; type (or (edn/read-string (:type params)) :text/html)
;; params (-> (get +available-emails+ id)
;; (assoc :email/name id))
;; email (emails/render params)
;; content (->> (:body email)
;; (filter #(= (:uxbox.emails.core/type %) type))
;; (first)
;; (:content))]
;; (-> (render-email type content)
;; (http/ok {:content-type "text/html; charset=utf-8"}))))

View file

@ -6,6 +6,9 @@
(ns uxbox.api.middleware
(:require [promesa.core :as p]
[buddy.core.hash :as hash]
[buddy.core.codecs :as codecs]
[buddy.core.codecs.base64 :as b64]
[reitit.core :as rc]
[reitit.ring.middleware.multipart :as multipart]
[reitit.ring.middleware.muuntaja :as muuntaja]
@ -115,6 +118,18 @@
;; "content-type"
;; "authorization"])})
;; (defn digest
;; [^bytes data]
;; (-> (hash/blake2b-256 data)
;; (b64/encode true)
;; (codecs/bytes->str)))
;; (defn- etag-match?
;; [^Request request ^String new-tag]
;; (let [^Headers headers (.getHeaders request)]
;; (when-let [etag (.get headers "if-none-match")]
;; (= etag new-tag))))
(def ^:private exception-middleware
(exception/create-exception-middleware
(assoc exception/default-handlers

View file

@ -8,7 +8,6 @@
"A initial fixtures."
(:require [buddy.hashers :as hashers]
[buddy.core.codecs :as codecs]
[catacumba.serializers :as sz]
[mount.core :as mount]
[clj-uuid :as uuid]
[suricatta.core :as sc]

View file

@ -1,163 +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-2017 Andrey Antukh <niwi@niwi.nz>
(ns uxbox.frontend
(:require [mount.core :refer [defstate]]
[catacumba.core :as ct]
[catacumba.http :as http]
[catacumba.serializers :as sz]
[catacumba.handlers.auth :as cauth]
[catacumba.handlers.parse :as cparse]
[catacumba.handlers.misc :as cmisc]
[uxbox.config :as cfg]
[uxbox.frontend.auth :as auth]
[uxbox.frontend.users :as users]
[uxbox.frontend.errors :as errors]
[uxbox.frontend.projects :as projects]
[uxbox.frontend.pages :as pages]
[uxbox.frontend.images :as images]
[uxbox.frontend.icons :as icons]
[uxbox.frontend.kvstore :as kvstore]
[uxbox.frontend.svgparse :as svgparse]
[uxbox.frontend.debug-emails :as dbgemails]
[uxbox.util.response :refer [rsp]]
[uxbox.util.uuid :as uuid]))
;; --- Top Level Handlers
(defn- welcome-api
"A GET entry point for the api that shows
a welcome message."
[context]
(let [body {:message "Welcome to UXBox api."}]
(-> (sz/encode body :json)
(http/ok {:content-type "application/json"}))))
(defn- debug-only
[context]
(if (-> cfg/config :server :debug)
(ct/delegate)
(http/not-found "")))
;; --- Config
(def cors-conf
{:origin "*"
:max-age 3600
:allow-methods #{:post :put :get :delete :trace}
:allow-headers #{:x-requested-with :content-type :authorization}})
;; --- Routes
(def auth-opts
{:alg :a256kw :enc :a128cbc-hs256})
(defn routes
([] (routes cfg/config))
([config]
(let [auth-opts {:secret cfg/secret
:options auth-opts}]
(ct/routes
[[:any (cauth/auth (cauth/jwe-backend auth-opts))]
[:any (cmisc/autoreloader)]
[:get "api" #'welcome-api]
;; Serve assets on development server
[:assets "media" {:dir "media"}]
[:assets "static" {:dir "static"}]
[:prefix "debug"
[:any debug-only]
[:get "emails" #'dbgemails/list-emails]
[:get "emails/email" #'dbgemails/show-email]]
[:prefix "api"
[:any (cmisc/cors cors-conf)]
[:any (cparse/body-params)]
[:error #'errors/handler]
[:post "auth/token" #'auth/login]
[:post "auth/register" #'users/register-user]
[:get "auth/recovery/:token" #'users/validate-recovery-token]
[:post "auth/recovery" #'users/request-recovery]
[:put "auth/recovery" #'users/recover-password]
[:get "library/images/:id" #'images/retrieve-image]
[:get "projects-by-token/:token" #'projects/retrieve-project-by-share-token]
;; SVG Parse
[:post "svg/parse" #'svgparse/parse]
[:any #'auth/authorization]
;; KVStore
[:put "kvstore" #'kvstore/update]
[:get "kvstore/:key" #'kvstore/retrieve]
[:delete "kvstore/:key" #'kvstore/delete]
;; Projects
[:get "projects/:id/pages" #'pages/list-pages-by-project]
[:put "projects/:id" #'projects/update-project]
[:delete "projects/:id" #'projects/delete-project]
[:post "projects" #'projects/create-project]
[:get "projects" #'projects/list-projects]
;; Image Collections
[:put "library/image-collections/:id" #'images/update-collection]
[:delete "library/image-collections/:id" #'images/delete-collection]
[:get "library/image-collections" #'images/list-collections]
[:post "library/image-collections" #'images/create-collection]
[:get "library/image-collections/:id/images" #'images/list-images]
[:get "library/image-collections/images" #'images/list-images]
;; Images
[:put "library/images/copy" #'images/copy-image]
[:delete "library/images/:id" #'images/delete-image]
[:put "library/images/:id" #'images/update-image]
[:post "library/images" #'images/create-image]
;; Icon Collections
[:put "library/icon-collections/:id" #'icons/update-collection]
[:delete "library/icon-collections/:id" #'icons/delete-collection]
[:get "library/icon-collections" #'icons/list-collections]
[:post "library/icon-collections" #'icons/create-collection]
[:get "library/icon-collections/:id/icons" #'icons/list-icons]
[:get "library/icon-collections/icons" #'icons/list-icons]
;; Icons
[:put "library/icons/copy" #'icons/copy-icon]
[:delete "library/icons/:id" #'icons/delete-icon]
[:put "library/icons/:id" #'icons/update-icon]
[:post "library/icons" #'icons/create-icon]
;; Pages
[:put "pages/:id/metadata" #'pages/update-page-metadata]
[:get "pages/:id/history" #'pages/retrieve-page-history]
[:put "pages/:id/history/:hid" #'pages/update-page-history]
[:put "pages/:id" #'pages/update-page]
[:delete "pages/:id" #'pages/delete-page]
[:post "pages" #'pages/create-page]
;; Profile
[:get "profile/me" #'users/retrieve-profile]
[:put "profile/me" #'users/update-profile]
[:put "profile/me/password" #'users/update-password]
[:post "profile/me/photo" #'users/update-photo]]]))))
;; --- State Initialization
(defn- start-server
[config]
(let [config {:port (:http-server-port config)
:debug (:http-server-debug config)
:marker-file "basedir"
:max-body-size 52428800}]
(ct/run-server (routes config) config)))
;; (defstate server
;; :start (start-server cfg/config)
;; :stop (.stop server))

View file

@ -1,33 +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.frontend.auth
(:require [clojure.spec.alpha :as s]
[catacumba.core :as ct]
[catacumba.http :as http]
[promesa.core :as p]
[uxbox.util.spec :as us]
[uxbox.services :as sv]
[uxbox.util.uuid :as uuid]
[uxbox.util.response :refer (rsp)]))
(s/def ::scope string?)
(s/def ::login (s/keys :req-un [::us/username ::us/password ::scope]))
(defn login
[{data :data}]
(let [data (us/conform ::login data)
message (assoc data :type :login)]
(->> (sv/novelty message)
(p/map #(http/ok (rsp %))))))
;; TODO: improve authorization
(defn authorization
[{:keys [identity] :as context}]
(if identity
(ct/delegate {:identity (uuid/from-string (:id identity))})
(http/forbidden (rsp nil))))

View file

@ -1,68 +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.frontend.debug-emails
"A helper namespace for just render emails."
(:require [clojure.edn :as edn]
[catacumba.http :as http]
[hiccup.page :refer (html5)]
[uxbox.emails :as emails]
[uxbox.emails.core :as emails-core]))
(def +available-emails+
{:users/register
{:name "Cirilla"}
:users/password-recovery
{:name "Cirilla"
:token "agNFhA6SolcFb4Us2NOTNWh0cfFDquVLAav400xQPjw"}})
(defn- render-emails-list
[]
(html5
[:section {:style "font-family: Monoid, monospace; font-size: 14px;"}
[:h1 "Available emails"]
[:table {:style "width: 500px;"}
[:tbody
[:tr
(for [[type email] @emails-core/emails]
[:tr
[:td (pr-str type)]
[:td
[:a {:href (str "/debug/emails/email?id="
(pr-str type)
"&type=:text/html")}
"(html)"]]
[:td
[:a {:href (str "/debug/emails/email?id="
(pr-str type)
"&type=:text/plain")}
"(text)"]]])]]]]))
(defn list-emails
[context]
(http/ok (render-emails-list)
{:content-type "text/html; charset=utf-8"}))
(defn- render-email
[type content]
(if (= type :text/html)
content
(html5
[:pre content])))
(defn show-email
[{params :query-params}]
(let [id (edn/read-string (:id params))
type (or (edn/read-string (:type params)) :text/html)
params (-> (get +available-emails+ id)
(assoc :email/name id))
email (emails/render params)
content (->> (:body email)
(filter #(= (:uxbox.emails.core/type %) type))
(first)
(:content))]
(-> (render-email type content)
(http/ok {:content-type "text/html; charset=utf-8"}))))

View file

@ -1,74 +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.frontend.errors
"A errors handling for frontend api."
(:require [catacumba.core :as ct]
[catacumba.http :as http]
[uxbox.util.response :refer (rsp)]))
(defmulti handle-exception #(:type (ex-data %)))
(defmethod handle-exception :validation
[err]
(println "\n*********** stack trace ***********")
(.printStackTrace err)
(println "\n********* end stack trace *********")
(let [response (ex-data err)]
(http/bad-request (rsp response))))
(defmethod handle-exception :default
[err]
(println "\n*********** stack trace ***********")
(.printStackTrace err)
(println "\n********* end stack trace *********")
(let [response (ex-data err)]
(http/internal-server-error (rsp response))))
;; --- Entry Point
(defn- handle-data-access-exception
[err]
(let [err (.getCause err)
state (.getSQLState err)
message (.getMessage err)]
(case state
"P0002"
(-> (rsp {:message message
:payload nil
:type :occ})
(http/precondition-failed))
(do
(.printStackTrace err)
(-> (rsp {:message message
:type :unexpected
:payload nil})
(http/internal-server-error))))))
(defn- handle-unexpected-exception
[err]
(.printStackTrace err)
(let [message (.getMessage err)]
(-> (rsp {:message message
:type :unexpected
:payload nil})
(http/internal-server-error))))
(defn handler
[context err]
(cond
(instance? clojure.lang.ExceptionInfo err)
(handle-exception err)
(instance? java.util.concurrent.CompletionException err)
(handler context (.getCause err))
(instance? org.jooq.exception.DataAccessException err)
(handle-data-access-exception err)
:else
(handle-unexpected-exception err)))

View file

@ -1,155 +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.frontend.icons
(:require [clojure.spec.alpha :as s]
[promesa.core :as p]
[catacumba.http :as http]
[uxbox.util.spec :as us]
[uxbox.services :as sv]
[uxbox.util.response :refer (rsp)]
[uxbox.util.uuid :as uuid]))
;; --- Constants & Config
(s/def ::collection (s/nilable ::us/uuid-string))
(s/def ::width (s/and number? pos?))
(s/def ::height (s/and number? pos?))
(s/def ::view-box (s/and (s/coll-of number?)
#(= 4 (count %))
vector?))
(s/def ::mimetype string?)
(s/def ::metadata
(s/keys :opt-un [::width ::height ::view-box ::mimetype]))
(s/def ::content string?)
;; --- Create Collection
(s/def ::create-collection
(s/keys :req-un [::us/name] :opt-un [::us/id]))
(defn create-collection
[{user :identity data :data}]
(let [data (us/conform ::create-collection data)
message (assoc data
:type :create-icon-collection
:user user)]
(->> (sv/novelty message)
(p/map (fn [result]
(let [loc (str "/api/library/icons/" (:id result))]
(http/created loc (rsp result))))))))
;; --- Update Collection
(s/def ::update-collection
(s/merge ::create-collection (s/keys :req-un [::us/version])))
(defn update-collection
[{user :identity params :route-params data :data}]
(let [data (us/conform ::update-collection data)
message (assoc data
:id (uuid/from-string (:id params))
:type :update-icon-collection
:user user)]
(-> (sv/novelty message)
(p/then #(http/ok (rsp %))))))
;; --- Delete Collection
(defn delete-collection
[{user :identity params :route-params}]
(let [message {:id (uuid/from-string (:id params))
:type :delete-icon-collection
:user user}]
(-> (sv/novelty message)
(p/then (fn [v] (http/no-content))))))
;; --- List collections
(defn list-collections
[{user :identity}]
(let [params {:user user
:type :list-icon-collections}]
(-> (sv/query params)
(p/then #(http/ok (rsp %))))))
;; --- Create Icon
(s/def ::create-icon
(s/keys :req-un [::metadata ::us/name ::metadata ::content]
:opt-un [::us/id ::collection]))
(defn create-icon
[{user :identity data :data :as request}]
(let [{:keys [id name content metadata collection]} (us/conform ::create-icon data)
id (or id (uuid/random))]
(->> (sv/novelty {:id id
:type :create-icon
:user user
:name name
:collection collection
:metadata metadata
:content content})
(p/map (fn [entry]
(let [loc (str "/api/library/icons/" (:id entry))]
(http/created loc (rsp entry))))))))
;; --- Update Icon
(s/def ::update-icon
(s/keys :req-un [::us/name ::us/version ::collection] :opt-un [::us/id]))
(defn update-icon
[{user :identity params :route-params data :data}]
(let [data (us/conform ::update-icon data)
message (assoc data
:id (uuid/from-string (:id params))
:type :update-icon
:user user)]
(->> (sv/novelty message)
(p/map #(http/ok (rsp %))))))
;; --- Copy Icon
(s/def ::copy-icon
(s/keys :req-un [:us/id ::collection]))
(defn copy-icon
[{user :identity data :data}]
(let [data (us/conform ::copy-icon data)
message (assoc data
:user user
:type :copy-icon)]
(->> (sv/novelty message)
(p/map #(http/ok (rsp %))))))
;; --- Delete Icon
(defn delete-icon
[{user :identity params :route-params}]
(let [message {:id (uuid/from-string (:id params))
:type :delete-icon
:user user}]
(->> (sv/novelty message)
(p/map (fn [v] (http/no-content))))))
;; --- List collections
(s/def ::list-icons
(s/keys :opt-un [::us/id]))
(defn list-icons
[{user :identity route-params :route-params}]
(let [{:keys [id]} (us/conform ::list-icons route-params)
params {:collection id
:type :list-icons
:user user}]
(->> (sv/query params)
(p/map rsp)
(p/map http/ok))))

View file

@ -1,197 +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.frontend.images
(:require [clojure.spec.alpha :as s]
[promesa.core :as p]
[catacumba.http :as http]
[datoteka.storages :as st]
[datoteka.core :as fs]
[uxbox.media :as media]
[uxbox.images :as images]
[uxbox.util.spec :as us]
[uxbox.services :as sv]
[uxbox.util.response :refer (rsp)]
[uxbox.util.uuid :as uuid]))
;; --- Constants & Config
(s/def ::file ::us/uploaded-file)
(s/def ::width ::us/integer-string)
(s/def ::height ::us/integer-string)
(s/def ::collection (s/nilable ::us/uuid-string))
(s/def ::mimetype string?)
(def +thumbnail-options+ {:src :path
:dst :thumbnail
:width 300
:height 100
:quality 92
:format "webp"})
(def populate-thumbnails
#(images/populate-thumbnails % +thumbnail-options+))
(def populate-urls
#(images/populate-urls % media/images-storage :path :url))
;; --- Create Collection
(s/def ::create-collection
(s/keys :req-un [::us/name] :opt-un [::us/id]))
(defn create-collection
[{user :identity data :data}]
(let [data (us/conform ::create-collection data)
message (assoc data
:type :create-image-collection
:user user)]
(->> (sv/novelty message)
(p/map (fn [result]
(let [loc (str "/api/library/images/" (:id result))]
(http/created loc (rsp result))))))))
;; --- Update Collection
(s/def ::update-collection
(s/merge ::create-collection (s/keys :req-un [::us/version])))
(defn update-collection
[{user :identity params :route-params data :data}]
(let [data (us/conform ::update-collection data)
message (assoc data
:id (uuid/from-string (:id params))
:type :update-image-collection
:user user)]
(-> (sv/novelty message)
(p/then #(http/ok (rsp %))))))
;; --- Delete Collection
(defn delete-collection
[{user :identity params :route-params}]
(let [message {:id (uuid/from-string (:id params))
:type :delete-image-collection
:user user}]
(-> (sv/novelty message)
(p/then (fn [v] (http/no-content))))))
;; --- List collections
(defn list-collections
[{user :identity}]
(let [params {:user user
:type :list-image-collections}]
(-> (sv/query params)
(p/then #(http/ok (rsp %))))))
;; --- Retrieve Image
(s/def ::retrieve-image
(s/keys :req-un [::us/id]))
(defn retrieve-image
[{user :identity params :route-params}]
(let [params (us/conform ::retrieve-image params)
params (assoc params :user user :type :retrieve-image)]
(->> (sv/query params)
(p/map (fn [result]
(if result
(-> (populate-thumbnails result)
(populate-urls)
(rsp)
(http/ok))
(http/not-found "")))))))
;; --- Create Image
(s/def ::create-image
(s/keys :req-un [::file ::width ::height ::mimetype]
:opt-un [::us/id ::collection]))
(defn create-image
[{user :identity data :data}]
(let [{:keys [file id width height
mimetype collection]} (us/conform ::create-image data)
id (or id (uuid/random))
filename (fs/name file)
storage media/images-storage]
(letfn [(persist-image-entry [path]
(sv/novelty {:id id
:type :create-image
:user user
:width width
:height height
:mimetype mimetype
:collection collection
:name filename
:path (str path)}))
(create-response [entry]
(let [loc (str "/api/library/images/" (:id entry))]
(http/created loc (rsp entry))))]
(->> (st/save storage filename file)
(p/mapcat persist-image-entry)
(p/map populate-thumbnails)
(p/map populate-urls)
(p/map create-response)))))
;; --- Update Image
(s/def ::update-image
(s/keys :req-un [::us/name ::us/version ::collection] :opt-un [::us/id]))
(defn update-image
[{user :identity params :route-params data :data}]
(let [data (us/conform ::update-image data)
message (assoc data
:id (uuid/from-string (:id params))
:type :update-image
:user user)]
(->> (sv/novelty message)
(p/map populate-thumbnails)
(p/map populate-urls)
(p/map #(http/ok (rsp %))))))
;; --- Copy Image
(s/def ::copy-image
(s/keys :req-un [::us/id ::collection]))
(defn copy-image
[{user :identity data :data}]
(let [data (us/conform ::copy-image data)
params (assoc data :user user :type :copy-image)]
(->> (sv/novelty params)
(p/map populate-thumbnails)
(p/map populate-urls)
(p/map #(http/ok (rsp %))))))
;; --- Delete Image
(defn delete-image
[{user :identity params :route-params}]
(let [message {:id (uuid/from-string (:id params))
:type :delete-image
:user user}]
(->> (sv/novelty message)
(p/map (fn [v] (http/no-content))))))
;; --- List collections
(s/def ::list-images
(s/keys :opt-un [::us/id]))
(defn list-images
[{user :identity route-params :route-params}]
(let [{:keys [id]} (us/conform ::list-images route-params)
params {:collection id
:type :list-images
:user user}]
(->> (sv/query params)
(p/map (partial map populate-thumbnails))
(p/map (partial map populate-urls))
(p/map rsp)
(p/map http/ok))))

View file

@ -1,60 +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.frontend.kvstore
(:refer-clojure :exclude [update])
(:require [clojure.spec.alpha :as s]
[promesa.core :as p]
[catacumba.http :as http]
[uxbox.media :as media]
[uxbox.util.spec :as us]
[uxbox.services :as sv]
[uxbox.util.response :refer (rsp)]
[uxbox.util.uuid :as uuid]))
(s/def ::version integer?)
(s/def ::key string?)
(s/def ::value any?)
;; --- Retrieve
(s/def ::retrieve (s/keys :req-un [::key]))
(defn retrieve
[{user :identity params :route-params}]
(let [data (us/conform ::retrieve params)
params (assoc data
:type :retrieve-kvstore
:user user)]
(->> (sv/query params)
(p/map #(http/ok (rsp %))))))
;; --- Update (or Create)
(s/def ::update (s/keys :req-un [::key ::value]
:opt-un [::version]))
(defn update
[{user :identity data :data}]
(let [data (us/conform ::update data)
params (assoc data
:type :update-kvstore
:user user)]
(->> (sv/novelty params)
(p/map #(http/ok (rsp %))))))
;; --- Delete
(s/def ::delete (s/keys :req-un [::key]))
(defn delete
[{user :identity params :route-params}]
(let [data (us/conform ::delete params)
params (assoc data
:type :delete-kvstore
:user user)]
(->> (sv/novelty params)
(p/map (fn [_] (http/no-content))))))

View file

@ -1,119 +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.frontend.pages
(:require [clojure.spec.alpha :as s]
[promesa.core :as p]
[catacumba.http :as http]
[uxbox.util.spec :as us]
[uxbox.services :as sv]
[uxbox.util.response :refer (rsp)]
[uxbox.util.uuid :as uuid]))
;; --- List Pages
(defn list-pages-by-project
[{user :identity params :route-params}]
(let [params {:user user
:project (uuid/from-string (:id params))
:type :list-pages-by-project}]
(-> (sv/query params)
(p/then #(http/ok (rsp %))))))
;; --- Create Page
(s/def ::data any?)
(s/def ::metadata any?)
(s/def ::project ::us/id)
(s/def ::create-page
(s/keys :req-un [::data ::metadata ::project ::us/name]
:opt-un [::us/id]))
(defn create-page
[{user :identity data :data}]
(let [data (us/conform ::create-page data)
message (assoc data
:type :create-page
:user user)]
(->> (sv/novelty message)
(p/map (fn [result]
(let [loc (str "/api/pages/" (:id result))]
(http/created loc (rsp result))))))))
;; --- Update Page
(s/def ::update-page
(s/merge ::create-page (s/keys :req-un [::us/version])))
(defn update-page
[{user :identity params :route-params data :data}]
(let [data (us/conform ::update-page data)
message (assoc data
:id (uuid/from-string (:id params))
:type :update-page
:user user)]
(->> (sv/novelty message)
(p/map #(http/ok (rsp %))))))
;; --- Update Page Metadata
(s/def ::update-page-metadata
(s/keys :req-un [::us/id ::metadata ::project ::us/name]))
(defn update-page-metadata
[{user :identity params :route-params data :data}]
(let [data (us/conform ::update-page-metadata data)
message (assoc data
:id (uuid/from-string (:id params))
:type :update-page-metadata
:user user)]
(->> (sv/novelty message)
(p/map #(http/ok (rsp %))))))
;; --- Delete Page
(defn delete-page
[{user :identity params :route-params}]
(let [message {:id (uuid/from-string (:id params))
:type :delete-page
:user user}]
(-> (sv/novelty message)
(p/then (fn [v] (http/no-content))))))
;; --- Retrieve Page History
(s/def ::max (s/and ::us/integer-string ::us/positive-integer))
(s/def ::since ::us/integer-string)
(s/def ::pinned ::us/boolean-string)
(s/def ::retrieve-page-history
(s/keys :opt-un [::max ::since ::pinned]))
(defn retrieve-page-history
[{user :identity params :route-params query :query-params}]
(let [query (us/conform ::retrieve-page-history query)
message (assoc query
:id (uuid/from-string (:id params))
:type :list-page-history
:user user)]
(->> (sv/query message)
(p/map #(http/ok (rsp %))))))
;; --- Update Page History
(s/def ::label string?)
(s/def ::update-page-history
(s/keys :req-un [::label ::pinned]))
(defn update-page-history
[{user :identity params :route-params data :data}]
(let [data (us/conform ::update-page-history data)
message (assoc data
:type :update-page-history
:id (uuid/from-string (:hid params))
:user user)]
(->> (sv/novelty message)
(p/map #(http/ok (rsp %))))))

View file

@ -1,73 +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.frontend.projects
(:require [clojure.spec.alpha :as s]
[promesa.core :as p]
[catacumba.http :as http]
[uxbox.util.spec :as us]
[uxbox.services :as sv]
[uxbox.util.response :refer (rsp)]
[uxbox.util.uuid :as uuid]))
;; --- List Projects
(defn list-projects
[{user :identity}]
(let [message {:user user :type :list-projects}]
(->> (sv/query message)
(p/map #(http/ok (rsp %))))))
;; --- Create Projects
(s/def ::create-project
(s/keys :req-un [::us/name] :opt-un [::us/id]))
(defn create-project
[{user :identity data :data}]
(let [data (us/conform ::create-project data)
message (assoc data
:type :create-project
:user user)]
(->> (sv/novelty message)
(p/map (fn [result]
(let [loc (str "/api/projects/" (:id result))]
(http/created loc (rsp result))))))))
;; --- Update Project
(s/def ::update-project
(s/keys :req-un [::us/name ::us/version]))
(defn update-project
[{user :identity params :route-params data :data}]
(let [data (us/conform ::update-project data)
message (assoc data
:id (uuid/from-string (:id params))
:type :update-project
:user user)]
(-> (sv/novelty message)
(p/then #(http/ok (rsp %))))))
;; --- Delete Project
(defn delete-project
[{user :identity params :route-params}]
(let [message {:id (uuid/from-string (:id params))
:type :delete-project
:user user}]
(-> (sv/novelty message)
(p/then (fn [v] (http/no-content))))))
;; --- Retrieve project
(defn retrieve-project-by-share-token
[{params :route-params}]
(let [message {:token (:token params)
:type :retrieve-project-by-share-token}]
(->> (sv/query message)
(p/map #(http/ok (rsp %))))))

View file

@ -1,22 +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.frontend.svgparse
"A frontend exposed endpoints for svgparse functionality."
(:require [clojure.spec.alpha :as s]
[promesa.core :as p]
[catacumba.http :as http]
[uxbox.util.spec :as us]
[uxbox.services :as sv]
[uxbox.util.response :refer (rsp)]
[uxbox.util.uuid :as uuid]))
(defn parse
[{body :body :as context}]
(let [message {:data (slurp body)
:type :parse-svg}]
(->> (sv/query message)
(p/map #(http/ok (rsp %))))))

View file

@ -1,149 +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.frontend.users
(:require [clojure.spec.alpha :as s]
[promesa.core :as p]
[catacumba.http :as http]
[datoteka.storages :as st]
[datoteka.core :as fs]
[uxbox.media :as media]
[uxbox.images :as images]
[uxbox.util.spec :as us]
[uxbox.services :as sv]
[uxbox.services.users :as svu]
[uxbox.util.response :refer (rsp)]
[uxbox.util.uuid :as uuid]))
;; --- Helpers
(defn- resolve-thumbnail
[user]
(let [opts {:src :photo
:dst :photo
:size [100 100]
:quality 90
:format "jpg"}]
(images/populate-thumbnails user opts)))
;; --- Retrieve Profile
(defn retrieve-profile
[{user :identity}]
(let [message {:user user
:type :retrieve-profile}]
(->> (sv/query message)
(p/map resolve-thumbnail)
(p/map #(http/ok (rsp %))))))
;; --- Update Profile
(s/def ::fullname string?)
(s/def ::metadata any?)
(s/def ::update-profile
(s/keys :req-un [::us/id ::us/username ::us/email
::fullname ::metadata]))
(defn update-profile
[{user :identity data :data}]
(let [data (us/conform ::update-profile data)
message (assoc data
:type :update-profile
:user user)]
(->> (sv/novelty message)
(p/map resolve-thumbnail)
(p/map #(http/ok (rsp %))))))
;; --- Update Password
(s/def ::old-password ::us/password)
(s/def ::update-password
(s/keys :req-un [::us/password ::old-password]))
(defn update-password
[{user :identity data :data}]
(let [data (us/conform ::update-password data)
message (assoc data
:type :update-profile-password
:user user)]
(-> (sv/novelty message)
(p/then #(http/ok (rsp %))))))
;; --- Update Profile Photo
(s/def ::file ::us/uploaded-file)
(s/def ::update-photo (s/keys :req-un [::file]))
(defn update-photo
[{user :identity data :data}]
(letfn [(store-photo [file]
(let [filename (fs/name file)
storage media/images-storage]
(st/save storage filename file)))
(assign-photo [path]
(sv/novelty {:user user
:path (str path)
:type :update-profile-photo}))
(create-response [_]
(http/no-content))]
(let [{:keys [file]} (us/conform ::update-photo data)]
(->> (store-photo file)
(p/mapcat assign-photo)
(p/map create-response)))))
;; --- Register User
(s/def ::register
(s/keys :req-un [::us/username ::us/email ::us/password ::fullname]))
(defn register-user
[{data :data}]
(let [data (us/conform ::register data)
message (assoc data :type :register-profile)]
(->> (sv/novelty message)
(p/map #(http/ok (rsp %))))))
;; --- Request Password Recovery
;; FIXME: rename for consistency
(s/def ::request-recovery
(s/keys :req-un [::us/username]))
(defn request-recovery
[{data :data}]
(let [data (us/conform ::request-recovery data)
message (assoc data :type :request-profile-password-recovery)]
(->> (sv/novelty message)
(p/map (fn [_] (http/no-content))))))
;; --- Password Recovery
;; FIXME: rename for consistency
(s/def ::token string?)
(s/def ::password-recovery
(s/keys :req-un [::token ::us/password]))
(defn recover-password
[{data :data}]
(let [data (us/conform ::password-recovery data)
message (assoc data :type :recover-profile-password)]
(->> (sv/novelty message)
(p/map (fn [_] (http/no-content))))))
;; --- Valiadate Recovery Token
(defn validate-recovery-token
[{params :route-params}]
(let [message {:type :validate-profile-password-recovery-token
:token (:token params)}]
(->> (sv/query message)
(p/map (fn [v]
(if v
(http/no-content)
(http/not-found "")))))))

View file

@ -16,8 +16,6 @@
[uxbox.util.data :refer (dissoc-in)])
(:import java.io.InputStream
java.io.ByteArrayInputStream
ratpack.form.UploadedFile
ratpack.http.TypedData
org.im4java.core.IMOperation
org.im4java.core.ConvertCmd))
@ -107,27 +105,3 @@
(dissoc-in src)
(assoc-in dst url))))))
;; --- Impl
(extend-type UploadedFile
pt/IPath
(-path [this]
(pt/-path (.getFileName ^UploadedFile this))))
(extend-type TypedData
pt/IContent
(-input-stream [this]
(.getInputStream this))
io/IOFactory
(make-reader [td opts]
(let [^InputStream is (.getInputStream td)]
(io/make-reader is opts)))
(make-writer [path opts]
(throw (UnsupportedOperationException. "read only object")))
(make-input-stream [td opts]
(let [^InputStream is (.getInputStream td)]
(io/make-input-stream is opts)))
(make-output-stream [path opts]
(throw (UnsupportedOperationException. "read only object"))))

View file

@ -9,7 +9,7 @@
[uxbox.config :as cfg]
[uxbox.migrations]
[uxbox.db]
[uxbox.frontend]
[uxbox.api]
[uxbox.scheduled-jobs])
(:gen-class))

View file

@ -17,8 +17,7 @@
[uxbox.util.uuid :as uuid]
[uxbox.util.blob :as blob]
[uxbox.util.data :as data])
(:import ratpack.form.UploadedFile
org.apache.commons.io.FilenameUtils))
(:import org.apache.commons.io.FilenameUtils))
;; --- Helpers & Specs

View file

@ -21,8 +21,7 @@
[uxbox.util.transit :as t]
[uxbox.util.uuid :as uuid]
[uxbox.util.data :as data])
(:import ratpack.form.UploadedFile
org.apache.commons.io.FilenameUtils))
(:import org.apache.commons.io.FilenameUtils))
(s/def ::width integer?)
(s/def ::height integer?)

View file

@ -1,73 +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.util.response
"A lightweigt reponse type definition.
At first instance it allows set the appropriate
content-type headers and encode the body using
the builtin transit abstraction.
In future it will allow easy adapt for the content
negotiation that is coming to catacumba."
(:require [catacumba.impl.handlers :as ch]
[catacumba.impl.context :as ctx]
[buddy.core.hash :as hash]
[buddy.core.codecs :as codecs]
[buddy.core.codecs.base64 :as b64]
[uxbox.util.transit :as t])
(:import ratpack.handling.Context
ratpack.http.Response
ratpack.http.Request
ratpack.http.Headers
ratpack.http.MutableHeaders))
(defn digest
[^bytes data]
(-> (hash/blake2b-256 data)
(b64/encode true)
(codecs/bytes->str)))
(defn- etag-match?
[^Request request ^String new-tag]
(let [^Headers headers (.getHeaders request)]
(when-let [etag (.get headers "if-none-match")]
(= etag new-tag))))
(deftype Rsp [data]
clojure.lang.IDeref
(deref [_] data)
ch/ISend
(-send [_ ctx]
(let [^Response response (ctx/get-response* ctx)
^Request request (ctx/get-request* ctx)
^MutableHeaders headers (.getHeaders response)
^String method (.. request getMethod getName toLowerCase)
data (t/encode data)]
(if (= method "get")
(let [etag (digest data)]
(if (etag-match? request etag)
(do
(.set headers "etag" etag)
(.status response 304)
(.send response))
(do
(.set headers "content-type" "application/transit+json")
(.set headers "etag" etag)
(ch/-send data ctx))))
(do
(.set headers "content-type" "application/transit+json")
(ch/-send data ctx))))))
(defn rsp
"A shortcut for create a response instance."
[data]
(Rsp. data))
(defn rsp?
[v]
(instance? Rsp v))

View file

@ -104,7 +104,7 @@
(s/def ::uuid-string (s/conformer uuid-conformer str))
(s/def ::boolean-string (s/conformer boolean-conformer boolean-unformer))
(s/def ::positive-integer #(< 0 % Long/MAX_VALUE))
(s/def ::uploaded-file #(instance? ratpack.form.UploadedFile %))
(s/def ::uploaded-file any?)
(s/def ::uuid uuid?)
(s/def ::bytes bytes?)
(s/def ::path path?)

View file

@ -0,0 +1,3 @@
(ns uxbox.util.struct
(:refer-clojure :exclude [keyword uuid vector boolean long map set])
(:require [struct.core :as st]))

View file

@ -6,11 +6,8 @@
(ns uxbox.util.transit
(:require [cognitect.transit :as t]
[catacumba.handlers.parse :as cparse]
[uxbox.util.time :as dt])
(:import ratpack.http.TypedData
ratpack.handling.Context
java.io.ByteArrayInputStream
(:import java.io.ByteArrayInputStream
java.io.ByteArrayOutputStream))
;; --- Handlers
@ -41,14 +38,6 @@
[writer data]
(t/write writer data))
;; --- Catacumba Extension
(defmethod cparse/parse-body :application/transit+json
[^Context ctx ^TypedData body]
(let [reader (reader (.getInputStream body) {:type :json})]
(read! reader)))
;; --- High-Level Api
(defn decode

View file

@ -4,7 +4,6 @@
[buddy.hashers :as hashers]
[buddy.core.codecs :as codecs]
[cuerdas.core :as str]
[catacumba.serializers :as sz]
[ring.adapter.jetty :as jetty]
[mount.core :as mount]
[datoteka.storages :as st]