mirror of
https://github.com/penpot/penpot.git
synced 2025-05-20 00:06:10 +02:00
🚧 Add project roles table.
This commit is contained in:
parent
04464da1c8
commit
f21ac8afbe
10 changed files with 160 additions and 39 deletions
|
@ -17,9 +17,10 @@
|
||||||
;; vertx-clojure/vertx {:mvn/version "0.0.0-SNAPSHOT"}
|
;; vertx-clojure/vertx {:mvn/version "0.0.0-SNAPSHOT"}
|
||||||
metosin/reitit-core {:mvn/version "0.3.10"}
|
metosin/reitit-core {:mvn/version "0.3.10"}
|
||||||
metosin/sieppari {:mvn/version "0.0.0-alpha8"}
|
metosin/sieppari {:mvn/version "0.0.0-alpha8"}
|
||||||
io.vertx/vertx-core {:mvn/version "3.8.1"}
|
io.vertx/vertx-core {:mvn/version "3.8.4"}
|
||||||
io.vertx/vertx-web {:mvn/version "3.8.1"}
|
io.vertx/vertx-web {:mvn/version "3.8.4"}
|
||||||
io.vertx/vertx-pg-client {:mvn/version "3.8.1"}
|
io.vertx/vertx-web-client {:mvn/version "3.8.4"}
|
||||||
|
io.vertx/vertx-pg-client {:mvn/version "3.8.4"}
|
||||||
;; end verx deps
|
;; end verx deps
|
||||||
|
|
||||||
lambdaisland/uri {:mvn/version "1.1.0"}
|
lambdaisland/uri {:mvn/version "1.1.0"}
|
||||||
|
|
|
@ -11,43 +11,37 @@ CREATE TABLE IF NOT EXISTS projects (
|
||||||
name text NOT NULL
|
name text NOT NULL
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS project_shares (
|
CREATE TABLE IF NOT EXISTS projects_roles (
|
||||||
project uuid PRIMARY KEY REFERENCES projects(id) ON DELETE CASCADE,
|
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(),
|
created_at timestamptz NOT NULL DEFAULT clock_timestamp(),
|
||||||
modified_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
|
-- Indexes
|
||||||
|
|
||||||
CREATE INDEX projects_user_idx
|
CREATE INDEX projects_user_idx ON projects(user_id);
|
||||||
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
|
CREATE UNIQUE INDEX projects_shares_token_idx ON project_shares(token);
|
||||||
ON project_shares(token);
|
|
||||||
|
|
||||||
-- Triggers
|
-- 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
|
CREATE TRIGGER projects_modified_at_tgr
|
||||||
BEFORE UPDATE ON projects
|
BEFORE UPDATE ON projects
|
||||||
FOR EACH ROW EXECUTE PROCEDURE update_modified_at();
|
FOR EACH ROW EXECUTE PROCEDURE update_modified_at();
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
[buddy.core.nonce :as nonce]
|
[buddy.core.nonce :as nonce]
|
||||||
[mount.core :as mount]
|
[mount.core :as mount]
|
||||||
[uxbox.main]
|
[uxbox.main]
|
||||||
|
[uxbox.util.sql :as sql]
|
||||||
[uxbox.util.blob :as blob])
|
[uxbox.util.blob :as blob])
|
||||||
(:gen-class))
|
(:gen-class))
|
||||||
|
|
||||||
|
|
|
@ -36,6 +36,7 @@
|
||||||
{:http-server-port (lookup-env env :uxbox-http-server-port 6060)
|
{:http-server-port (lookup-env env :uxbox-http-server-port 6060)
|
||||||
:http-server-debug (lookup-env env :uxbox-http-server-debug true)
|
:http-server-debug (lookup-env env :uxbox-http-server-debug true)
|
||||||
:http-server-cors (lookup-env env :uxbox-http-server-cors "http://localhost:3449")
|
:http-server-cors (lookup-env env :uxbox-http-server-cors "http://localhost:3449")
|
||||||
|
|
||||||
:database-username (lookup-env env :uxbox-database-username nil)
|
:database-username (lookup-env env :uxbox-database-username nil)
|
||||||
:database-password (lookup-env env :uxbox-database-password nil)
|
:database-password (lookup-env env :uxbox-database-password nil)
|
||||||
:database-uri (lookup-env env :uxbox-database-uri "postgresql://127.0.0.1/uxbox")
|
: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-directory (lookup-env env :uxbox-assets-directory "resources/public/static")
|
||||||
:assets-uri (lookup-env env :uxbox-assets-uri "http://localhost:6060/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-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")
|
:email-from (lookup-env env :uxbox-email-from "no-reply@uxbox.io")
|
||||||
|
|
||||||
|
|
|
@ -48,13 +48,21 @@
|
||||||
values ($1, $2, $3)
|
values ($1, $2, $3)
|
||||||
returning *;")
|
returning *;")
|
||||||
|
|
||||||
|
(def create-project-role-sql
|
||||||
|
"insert into projects_roles (project_id, user_id, role)
|
||||||
|
values ($1, $2, 'owner');")
|
||||||
|
|
||||||
(defn create-project
|
(defn create-project
|
||||||
[conn [pjid uid]]
|
[conn [pjid uid]]
|
||||||
(println "create project" pjid "(for user=" uid ")")
|
(println "create project" pjid "(for user=" uid ")")
|
||||||
(db/query-one conn [create-project-sql
|
(p/do!
|
||||||
(mk-uuid "project" pjid uid)
|
(db/query-one conn [create-project-sql
|
||||||
(mk-uuid "user" uid)
|
(mk-uuid "project" pjid uid)
|
||||||
(str "sample project " pjid)]))
|
(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
|
;; --- Pages creation
|
||||||
|
|
||||||
|
|
63
backend/src/uxbox/services/fonts.clj
Normal file
63
backend/src/uxbox/services/fonts.clj
Normal file
|
@ -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 <niwi@niwi.nz>
|
||||||
|
|
||||||
|
(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)))))))
|
|
@ -113,7 +113,6 @@
|
||||||
(-> (db/query-one db/pool [sql id user project-id name data mdata])
|
(-> (db/query-one db/pool [sql id user project-id name data mdata])
|
||||||
(p/then' decode-row))))
|
(p/then' decode-row))))
|
||||||
|
|
||||||
|
|
||||||
;; --- Mutation: Update Page
|
;; --- Mutation: Update Page
|
||||||
|
|
||||||
(s/def ::update-page
|
(s/def ::update-page
|
||||||
|
|
|
@ -34,9 +34,8 @@
|
||||||
(declare -handle-body)
|
(declare -handle-body)
|
||||||
|
|
||||||
(defn ->headers
|
(defn ->headers
|
||||||
[^HttpServerRequest request]
|
[^MultiMap headers]
|
||||||
(let [headers (.headers request)
|
(let [it (.iterator ^MultiMap headers)]
|
||||||
it (.iterator ^MultiMap headers)]
|
|
||||||
(loop [m (transient {})]
|
(loop [m (transient {})]
|
||||||
(if (.hasNext it)
|
(if (.hasNext it)
|
||||||
(let [^Map$Entry me (.next it)
|
(let [^Map$Entry me (.next it)
|
||||||
|
@ -49,7 +48,7 @@
|
||||||
[^HttpServerRequest request]
|
[^HttpServerRequest request]
|
||||||
{:method (-> request .rawMethod .toLowerCase keyword)
|
{:method (-> request .rawMethod .toLowerCase keyword)
|
||||||
:path (.path request)
|
:path (.path request)
|
||||||
:headers (->headers request)
|
:headers (->headers (.headers request))
|
||||||
::request request
|
::request request
|
||||||
::response (.response request)})
|
::response (.response request)})
|
||||||
|
|
||||||
|
|
|
@ -47,7 +47,7 @@
|
||||||
^Vertx system (.vertx routing-context)]
|
^Vertx system (.vertx routing-context)]
|
||||||
{:body (.getBody routing-context)
|
{:body (.getBody routing-context)
|
||||||
:path (.path request)
|
:path (.path request)
|
||||||
:headers (vh/->headers request)
|
:headers (vh/->headers (.headers request))
|
||||||
:method (-> request .rawMethod .toLowerCase keyword)
|
:method (-> request .rawMethod .toLowerCase keyword)
|
||||||
::vh/request request
|
::vh/request request
|
||||||
::vh/response response
|
::vh/response response
|
||||||
|
|
53
backend/src/vertx/web/client.clj
Normal file
53
backend/src/vertx/web/client.clj
Normal file
|
@ -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 <niwi@niwi.nz>
|
||||||
|
|
||||||
|
(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))})))))
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue