From f21ac8afbeee7f6ddc83bdbcadd299837328d294 Mon Sep 17 00:00:00 2001 From: Andrey Antukh Date: Sun, 1 Dec 2019 12:07:51 +0100 Subject: [PATCH] :construction: Add project roles table. --- backend/deps.edn | 7 ++- .../resources/migrations/0003.projects.up.sql | 46 ++++++-------- backend/src/user.clj | 1 + backend/src/uxbox/config.clj | 3 + backend/src/uxbox/fixtures.clj | 16 +++-- backend/src/uxbox/services/fonts.clj | 63 +++++++++++++++++++ backend/src/uxbox/services/pages.clj | 1 - backend/src/vertx/http.clj | 7 +-- backend/src/vertx/web.clj | 2 +- backend/src/vertx/web/client.clj | 53 ++++++++++++++++ 10 files changed, 160 insertions(+), 39 deletions(-) create mode 100644 backend/src/uxbox/services/fonts.clj create mode 100644 backend/src/vertx/web/client.clj diff --git a/backend/deps.edn b/backend/deps.edn index 6874d3605..34931c8a1 100644 --- a/backend/deps.edn +++ b/backend/deps.edn @@ -17,9 +17,10 @@ ;; vertx-clojure/vertx {:mvn/version "0.0.0-SNAPSHOT"} metosin/reitit-core {:mvn/version "0.3.10"} metosin/sieppari {:mvn/version "0.0.0-alpha8"} - io.vertx/vertx-core {:mvn/version "3.8.1"} - io.vertx/vertx-web {:mvn/version "3.8.1"} - io.vertx/vertx-pg-client {:mvn/version "3.8.1"} + io.vertx/vertx-core {:mvn/version "3.8.4"} + io.vertx/vertx-web {:mvn/version "3.8.4"} + io.vertx/vertx-web-client {:mvn/version "3.8.4"} + io.vertx/vertx-pg-client {:mvn/version "3.8.4"} ;; end verx deps lambdaisland/uri {:mvn/version "1.1.0"} diff --git a/backend/resources/migrations/0003.projects.up.sql b/backend/resources/migrations/0003.projects.up.sql index 57fdd8cdc..cc2b6b471 100644 --- a/backend/resources/migrations/0003.projects.up.sql +++ b/backend/resources/migrations/0003.projects.up.sql @@ -11,43 +11,37 @@ CREATE TABLE IF NOT EXISTS projects ( name text NOT NULL ); -CREATE TABLE IF NOT EXISTS project_shares ( - project uuid PRIMARY KEY REFERENCES projects(id) ON DELETE CASCADE, +CREATE TABLE IF NOT EXISTS projects_roles ( + user_id uuid NOT NULL REFERENCES users(id) ON DELETE CASCADE, + project_id uuid NOT NULL REFERENCES projects(id) ON DELETE CASCADE, created_at timestamptz NOT NULL DEFAULT clock_timestamp(), modified_at timestamptz NOT NULL DEFAULT clock_timestamp(), - token text + + role text NOT NULL, + + PRIMARY KEY (user_id, project_id) +); + +CREATE TABLE IF NOT EXISTS project_shares ( + project_id uuid PRIMARY KEY REFERENCES projects(id) ON DELETE CASCADE, + + created_at timestamptz NOT NULL DEFAULT clock_timestamp(), + modified_at timestamptz NOT NULL DEFAULT clock_timestamp(), + + token text NOT NULL ); -- Indexes -CREATE INDEX projects_user_idx - ON projects(user_id); +CREATE INDEX projects_user_idx ON projects(user_id); +CREATE INDEX projects_roles_user_id_idx ON projects_roles(project_id); +CREATE INDEX projects_roles_project_id_idx ON projects_roles(user_id); -CREATE UNIQUE INDEX projects_shares_token_idx - ON project_shares(token); +CREATE UNIQUE INDEX projects_shares_token_idx ON project_shares(token); -- Triggers -CREATE OR REPLACE FUNCTION handle_project_create() - RETURNS TRIGGER AS $$ - DECLARE - token text; - BEGIN - SELECT encode(digest(gen_random_bytes(128), 'sha256'), 'hex') - INTO token; - - INSERT INTO project_shares (project, token) - VALUES (NEW.id, token); - - RETURN NEW; - END; -$$ LANGUAGE plpgsql; - -CREATE TRIGGER project_on_create_tgr - AFTER INSERT ON projects - FOR EACH ROW EXECUTE PROCEDURE handle_project_create(); - CREATE TRIGGER projects_modified_at_tgr BEFORE UPDATE ON projects FOR EACH ROW EXECUTE PROCEDURE update_modified_at(); diff --git a/backend/src/user.clj b/backend/src/user.clj index 43ebae5ab..7c98b0bb8 100644 --- a/backend/src/user.clj +++ b/backend/src/user.clj @@ -22,6 +22,7 @@ [buddy.core.nonce :as nonce] [mount.core :as mount] [uxbox.main] + [uxbox.util.sql :as sql] [uxbox.util.blob :as blob]) (:gen-class)) diff --git a/backend/src/uxbox/config.clj b/backend/src/uxbox/config.clj index d6edd0b4d..20f8eb01d 100644 --- a/backend/src/uxbox/config.clj +++ b/backend/src/uxbox/config.clj @@ -36,6 +36,7 @@ {:http-server-port (lookup-env env :uxbox-http-server-port 6060) :http-server-debug (lookup-env env :uxbox-http-server-debug true) :http-server-cors (lookup-env env :uxbox-http-server-cors "http://localhost:3449") + :database-username (lookup-env env :uxbox-database-username nil) :database-password (lookup-env env :uxbox-database-password nil) :database-uri (lookup-env env :uxbox-database-uri "postgresql://127.0.0.1/uxbox") @@ -44,6 +45,8 @@ :assets-directory (lookup-env env :uxbox-assets-directory "resources/public/static") :assets-uri (lookup-env env :uxbox-assets-uri "http://localhost:6060/static/") + :google-api-key (lookup-env env :uxbox-google-api-key nil) + :email-reply-to (lookup-env env :uxbox-email-reply-to "no-reply@uxbox.io") :email-from (lookup-env env :uxbox-email-from "no-reply@uxbox.io") diff --git a/backend/src/uxbox/fixtures.clj b/backend/src/uxbox/fixtures.clj index 7cf776b3d..fca3f3230 100644 --- a/backend/src/uxbox/fixtures.clj +++ b/backend/src/uxbox/fixtures.clj @@ -48,13 +48,21 @@ values ($1, $2, $3) returning *;") +(def create-project-role-sql + "insert into projects_roles (project_id, user_id, role) + values ($1, $2, 'owner');") + (defn create-project [conn [pjid uid]] (println "create project" pjid "(for user=" uid ")") - (db/query-one conn [create-project-sql - (mk-uuid "project" pjid uid) - (mk-uuid "user" uid) - (str "sample project " pjid)])) + (p/do! + (db/query-one conn [create-project-sql + (mk-uuid "project" pjid uid) + (mk-uuid "user" uid) + (str "sample project " pjid)]) + (db/query-one conn [create-project-role-sql + (mk-uuid "project" pjid uid) + (mk-uuid "user" uid)]))) ;; --- Pages creation diff --git a/backend/src/uxbox/services/fonts.clj b/backend/src/uxbox/services/fonts.clj new file mode 100644 index 000000000..0ebc2417d --- /dev/null +++ b/backend/src/uxbox/services/fonts.clj @@ -0,0 +1,63 @@ +;; 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 + +(ns uxbox.services.fonts + (:require + [clojure.spec.alpha :as s] + [vertx.web.client :as vwc] + [mount.core :as mount :refer [defstate]] + [promesa.core :as p] + [uxbox.core :refer [system]] + [uxbox.config :as cfg] + [uxbox.db :as db] + [uxbox.services.core :as sv] + [uxbox.services.users :as users] + [uxbox.util.transit :as t] + [uxbox.util.spec :as us] + [uxbox.util.uuid :as uuid] + [uxbox.util.data :as data] + [uxbox.util.exceptions :as ex])) + +(defstate webclient + :start (vwc/create system) + :stop (.close webclient)) + +(s/def ::google-fonts any?) + +(def fonts-url + "https://www.googleapis.com/webfonts/v1/webfonts?key=") + +(def api-key + "AIzaSyD3iPn7K0sJp5oOi3BRohLDuAA2SKOFJw4") + +(defn fetch-and-store + [] + (let [ses (vwc/session webclient)] + (-> (vwc/get ses (str fonts-url api-key)) + (p/then' (fn [{:keys [status body]}] + (get (t/decode body) "items" []))) + (p/then' data/normalize-attrs) + (p/then (fn [result] + (-> (sv/mutation {::sv/type :upsert-kvstore + :key "google-fonts" + :user uuid/zero + :value result}) + (p/catch (fn [err] + (prn "KAKA" err) + (throw err))) + (p/then (constantly result)))))))) + +(sv/defquery ::google-fonts + "Returns a cached version of google fonts." + [params] + (-> (sv/query {::sv/type :kvstore-entry + :key "google-fonts" + :user uuid/zero}) + (p/catch (fn [err] + (let [edata (ex-data err)] + (if (= :not-found (:type edata)) + (fetch-and-store) + (p/rejected err))))))) diff --git a/backend/src/uxbox/services/pages.clj b/backend/src/uxbox/services/pages.clj index cc6ce915c..4bfcd5abf 100644 --- a/backend/src/uxbox/services/pages.clj +++ b/backend/src/uxbox/services/pages.clj @@ -113,7 +113,6 @@ (-> (db/query-one db/pool [sql id user project-id name data mdata]) (p/then' decode-row)))) - ;; --- Mutation: Update Page (s/def ::update-page diff --git a/backend/src/vertx/http.clj b/backend/src/vertx/http.clj index 2f4bf031d..86ee729d0 100644 --- a/backend/src/vertx/http.clj +++ b/backend/src/vertx/http.clj @@ -34,9 +34,8 @@ (declare -handle-body) (defn ->headers - [^HttpServerRequest request] - (let [headers (.headers request) - it (.iterator ^MultiMap headers)] + [^MultiMap headers] + (let [it (.iterator ^MultiMap headers)] (loop [m (transient {})] (if (.hasNext it) (let [^Map$Entry me (.next it) @@ -49,7 +48,7 @@ [^HttpServerRequest request] {:method (-> request .rawMethod .toLowerCase keyword) :path (.path request) - :headers (->headers request) + :headers (->headers (.headers request)) ::request request ::response (.response request)}) diff --git a/backend/src/vertx/web.clj b/backend/src/vertx/web.clj index 9a06f49a6..eafa876f5 100644 --- a/backend/src/vertx/web.clj +++ b/backend/src/vertx/web.clj @@ -47,7 +47,7 @@ ^Vertx system (.vertx routing-context)] {:body (.getBody routing-context) :path (.path request) - :headers (vh/->headers request) + :headers (vh/->headers (.headers request)) :method (-> request .rawMethod .toLowerCase keyword) ::vh/request request ::vh/response response diff --git a/backend/src/vertx/web/client.clj b/backend/src/vertx/web/client.clj new file mode 100644 index 000000000..8765bb586 --- /dev/null +++ b/backend/src/vertx/web/client.clj @@ -0,0 +1,53 @@ +;; 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 + +(ns vertx.web.client + "High level http client." + (:refer-clojure :exclude [get]) + (:require + [clojure.tools.logging :as log] + [clojure.spec.alpha :as s] + [promesa.core :as p] + [sieppari.core :as sp] + [reitit.core :as rt] + [vertx.http :as vh] + [vertx.util :as vu]) + (:import + clojure.lang.IPersistentMap + clojure.lang.Keyword + io.vertx.core.Future + io.vertx.core.Handler + io.vertx.core.Vertx + io.vertx.core.buffer.Buffer + io.vertx.core.http.HttpMethod + io.vertx.ext.web.client.HttpRequest + io.vertx.ext.web.client.HttpResponse + io.vertx.ext.web.client.WebClientSession + io.vertx.ext.web.client.WebClient)) + +;; TODO: accept options + +(defn create + ([vsm] (create vsm {})) + ([vsm opts] + (let [^Vertx system (vu/resolve-system vsm)] + (WebClient/create system)))) + +(defn session + [client] + (WebClientSession/create client)) + +(defn get + ([session url] (get session url {})) + ([session url opts] + (let [^HttpRequest req (.getAbs session url) + d (p/deferred)] + (.send req (vu/deferred->handler d)) + (p/then d (fn [^HttpResponse res] + {:body (.bodyAsBuffer res) + :status (.statusCode res) + :headers (vh/->headers (.headers res))}))))) +