mirror of
https://github.com/penpot/penpot.git
synced 2025-05-10 05:46:39 +02:00
♻️ Refactor dashboard (add teams)
This commit is contained in:
parent
47d347f357
commit
b3252ec2b2
52 changed files with 1842 additions and 1421 deletions
|
@ -2,12 +2,21 @@
|
|||
;; 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>
|
||||
;; This Source Code Form is "Incompatible With Secondary Licenses", as
|
||||
;; defined by the Mozilla Public License, v. 2.0.
|
||||
;;
|
||||
;; Copyright (c) 2020 UXBOX Labs SL
|
||||
|
||||
(ns app.db
|
||||
(:require
|
||||
[clojure.spec.alpha :as s]
|
||||
[app.common.exceptions :as ex]
|
||||
[app.config :as cfg]
|
||||
[app.metrics :as mtx]
|
||||
[app.util.data :as data]
|
||||
[app.util.time :as dt]
|
||||
[app.util.transit :as t]
|
||||
[clojure.data.json :as json]
|
||||
[clojure.spec.alpha :as s]
|
||||
[clojure.string :as str]
|
||||
[clojure.tools.logging :as log]
|
||||
[lambdaisland.uri :refer [uri]]
|
||||
|
@ -17,19 +26,13 @@
|
|||
[next.jdbc.optional :as jdbc-opt]
|
||||
[next.jdbc.result-set :as jdbc-rs]
|
||||
[next.jdbc.sql :as jdbc-sql]
|
||||
[next.jdbc.sql.builder :as jdbc-bld]
|
||||
[app.common.exceptions :as ex]
|
||||
[app.config :as cfg]
|
||||
[app.metrics :as mtx]
|
||||
[app.util.time :as dt]
|
||||
[app.util.transit :as t]
|
||||
[app.util.data :as data])
|
||||
[next.jdbc.sql.builder :as jdbc-bld])
|
||||
(:import
|
||||
org.postgresql.util.PGobject
|
||||
org.postgresql.util.PGInterval
|
||||
com.zaxxer.hikari.metrics.prometheus.PrometheusMetricsTrackerFactory
|
||||
com.zaxxer.hikari.HikariConfig
|
||||
com.zaxxer.hikari.HikariDataSource))
|
||||
com.zaxxer.hikari.HikariDataSource
|
||||
com.zaxxer.hikari.metrics.prometheus.PrometheusMetricsTrackerFactory
|
||||
org.postgresql.util.PGInterval
|
||||
org.postgresql.util.PGobject))
|
||||
|
||||
(def initsql
|
||||
(str "SET statement_timeout = 10000;\n"
|
||||
|
|
|
@ -98,6 +98,16 @@
|
|||
|
||||
{:name "0027-mod-file-table-ignore-sync"
|
||||
:fn (mg/resource "app/migrations/sql/0027-mod-file-table-ignore-sync.sql")}
|
||||
|
||||
{:name "0028-add-team-project-profile-rel-table"
|
||||
:fn (mg/resource "app/migrations/sql/0028-add-team-project-profile-rel-table.sql")}
|
||||
|
||||
{:name "0029-del-project-profile-rel-indexes"
|
||||
:fn (mg/resource "app/migrations/sql/0029-del-project-profile-rel-indexes.sql")}
|
||||
|
||||
{:name "0030-mod-file-table-add-missing-index"
|
||||
:fn (mg/resource "app/migrations/sql/0030-mod-file-table-add-missing-index.sql")}
|
||||
|
||||
]})
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
|
|
@ -78,7 +78,6 @@ VALUES ('00000000-0000-0000-0000-000000000000'::uuid,
|
|||
true);
|
||||
|
||||
|
||||
|
||||
CREATE TABLE team_profile_rel (
|
||||
team_id uuid NOT NULL REFERENCES team(id) ON DELETE CASCADE,
|
||||
profile_id uuid NOT NULL REFERENCES profile(id) ON DELETE RESTRICT,
|
||||
|
|
|
@ -28,9 +28,6 @@ CREATE TABLE project_profile_rel (
|
|||
PRIMARY KEY (profile_id, project_id)
|
||||
);
|
||||
|
||||
COMMENT ON TABLE project_profile_rel
|
||||
IS 'Relation between projects and profiles (NM)';
|
||||
|
||||
CREATE INDEX project_profile_rel__profile_id__idx
|
||||
ON project_profile_rel(profile_id);
|
||||
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
CREATE TABLE team_project_profile_rel (
|
||||
team_id uuid NOT NULL REFERENCES team(id) ON DELETE CASCADE,
|
||||
profile_id uuid NOT NULL REFERENCES profile(id) ON DELETE CASCADE,
|
||||
project_id uuid NOT NULL REFERENCES project(id) ON DELETE CASCADE,
|
||||
|
||||
created_at timestamptz NOT NULL DEFAULT clock_timestamp(),
|
||||
modified_at timestamptz NOT NULL DEFAULT clock_timestamp(),
|
||||
|
||||
is_pinned boolean NOT NULL DEFAULT false,
|
||||
|
||||
PRIMARY KEY (team_id, profile_id, project_id)
|
||||
);
|
|
@ -0,0 +1,4 @@
|
|||
--- Drop duplicate indexes
|
||||
|
||||
DROP INDEX IF EXISTS project_profile_rel__project_id__idx;
|
||||
DROP INDEX IF EXISTS project_profile_rel__profile_id__idx;
|
|
@ -0,0 +1 @@
|
|||
CREATE INDEX IF NOT EXISTS file__project_id__idx ON file (project_id);
|
|
@ -61,6 +61,7 @@
|
|||
|
||||
(declare create-project)
|
||||
(declare create-project-profile)
|
||||
(declare create-team-project-profile)
|
||||
|
||||
(s/def ::team-id ::us/uuid)
|
||||
(s/def ::create-project
|
||||
|
@ -70,9 +71,11 @@
|
|||
(sm/defmutation ::create-project
|
||||
[params]
|
||||
(db/with-atomic [conn db/pool]
|
||||
(let [proj (create-project conn params)]
|
||||
(create-project-profile conn (assoc params :project-id (:id proj)))
|
||||
proj)))
|
||||
(let [proj (create-project conn params)
|
||||
params (assoc params :project-id (:id proj))]
|
||||
(create-project-profile conn params)
|
||||
(create-team-project-profile conn params)
|
||||
(assoc proj :is-pinned true))))
|
||||
|
||||
(defn create-project
|
||||
[conn {:keys [id profile-id team-id name default?] :as params}]
|
||||
|
@ -93,8 +96,31 @@
|
|||
:is-admin true
|
||||
:can-edit true}))
|
||||
|
||||
(defn create-team-project-profile
|
||||
[conn {:keys [team-id project-id profile-id] :as params}]
|
||||
(db/insert! conn :team-project-profile-rel
|
||||
{:project-id project-id
|
||||
:profile-id profile-id
|
||||
:team-id team-id
|
||||
:is-pinned true}))
|
||||
|
||||
|
||||
;; --- Mutation: Toggle Project Pin
|
||||
|
||||
(s/def ::is-pinned ::us/boolean)
|
||||
(s/def ::toggle-project-pin
|
||||
(s/keys :req-un [::profile-id ::id ::team-id ::is-pinned]))
|
||||
|
||||
(sm/defmutation ::toggle-project-pin
|
||||
[{:keys [id profile-id team-id is-pinned] :as params}]
|
||||
(db/with-atomic [conn db/pool]
|
||||
(db/update! conn :team-project-profile-rel
|
||||
{:is-pinned is-pinned}
|
||||
{:profile-id profile-id
|
||||
:project-id id
|
||||
:team-id team-id})
|
||||
nil))
|
||||
|
||||
;; --- Mutation: Rename Project
|
||||
|
||||
(declare rename-project)
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
[app.common.uuid :as uuid]
|
||||
[app.db :as db]
|
||||
[app.services.mutations :as sm]
|
||||
[app.services.mutations.projects :as projects]
|
||||
[app.util.blob :as blob]))
|
||||
|
||||
;; --- Helpers & Specs
|
||||
|
@ -27,6 +28,7 @@
|
|||
|
||||
(declare create-team)
|
||||
(declare create-team-profile)
|
||||
(declare create-team-default-project)
|
||||
|
||||
(s/def ::create-team
|
||||
(s/keys :req-un [::profile-id ::name]
|
||||
|
@ -35,8 +37,10 @@
|
|||
(sm/defmutation ::create-team
|
||||
[params]
|
||||
(db/with-atomic [conn db/pool]
|
||||
(let [team (create-team conn params)]
|
||||
(create-team-profile conn (assoc params :team-id (:id team)))
|
||||
(let [team (create-team conn params)
|
||||
params (assoc params :team-id (:id team))]
|
||||
(create-team-profile conn params)
|
||||
(create-team-default-project conn params)
|
||||
team)))
|
||||
|
||||
(defn create-team
|
||||
|
@ -57,3 +61,11 @@
|
|||
:is-owner true
|
||||
:is-admin true
|
||||
:can-edit true}))
|
||||
|
||||
(defn create-team-default-project
|
||||
[conn {:keys [team-id profile-id] :as params}]
|
||||
(let [proj (projects/create-project conn {:team-id team-id
|
||||
:name "Drafts"
|
||||
:default? true})]
|
||||
(projects/create-project-profile conn {:project-id (:id proj)
|
||||
:profile-id profile-id})))
|
||||
|
|
|
@ -53,16 +53,16 @@
|
|||
(def ^:private sql:default-team-and-project
|
||||
"select t.id
|
||||
from team as t
|
||||
inner join team_profile_rel as tpr on (tpr.team_id = t.id)
|
||||
where tpr.profile_id = ?
|
||||
and tpr.is_owner is true
|
||||
inner join team_profile_rel as tp on (tp.team_id = t.id)
|
||||
where tp.profile_id = ?
|
||||
and tp.is_owner is true
|
||||
and t.is_default is true
|
||||
union all
|
||||
select p.id
|
||||
from project as p
|
||||
inner join project_profile_rel as tpr on (tpr.project_id = p.id)
|
||||
where tpr.profile_id = ?
|
||||
and tpr.is_owner is true
|
||||
inner join project_profile_rel as tp on (tp.project_id = p.id)
|
||||
where tp.profile_id = ?
|
||||
and tp.is_owner is true
|
||||
and p.is_default is true")
|
||||
|
||||
(defn retrieve-additional-data
|
||||
|
|
|
@ -60,7 +60,6 @@
|
|||
|
||||
(s/def ::team-id ::us/uuid)
|
||||
(s/def ::profile-id ::us/uuid)
|
||||
|
||||
(s/def ::projects
|
||||
(s/keys :req-un [::profile-id ::team-id]))
|
||||
|
||||
|
@ -68,31 +67,37 @@
|
|||
[{:keys [profile-id team-id]}]
|
||||
(with-open [conn (db/open)]
|
||||
(teams/check-read-permissions! conn profile-id team-id)
|
||||
(retrieve-projects conn team-id)))
|
||||
(retrieve-projects conn profile-id team-id)))
|
||||
|
||||
(def sql:projects
|
||||
"select p.*,
|
||||
tpp.is_pinned,
|
||||
(select count(*) from file as f
|
||||
where f.project_id = p.id
|
||||
and deleted_at is null)
|
||||
where f.project_id = p.id
|
||||
and deleted_at is null) as count
|
||||
from project as p
|
||||
left join team_project_profile_rel as tpp
|
||||
on (tpp.project_id = p.id and
|
||||
tpp.team_id = p.team_id and
|
||||
tpp.profile_id = ?)
|
||||
where p.team_id = ?
|
||||
and p.deleted_at is null
|
||||
order by p.modified_at desc")
|
||||
|
||||
(defn retrieve-projects
|
||||
[conn team-id]
|
||||
(db/exec! conn [sql:projects team-id]))
|
||||
[conn profile-id team-id]
|
||||
(db/exec! conn [sql:projects profile-id team-id]))
|
||||
|
||||
;; --- Query: Projec by ID
|
||||
|
||||
(s/def ::project-id ::us/uuid)
|
||||
(s/def ::project-by-id
|
||||
(s/keys :req-un [::profile-id ::project-id]))
|
||||
;; --- Query: Project
|
||||
|
||||
(sq/defquery ::project-by-id
|
||||
[{:keys [profile-id project-id]}]
|
||||
(s/def ::id ::us/uuid)
|
||||
(s/def ::project
|
||||
(s/keys :req-un [::profile-id ::id]))
|
||||
|
||||
(sq/defquery ::project
|
||||
[{:keys [profile-id id]}]
|
||||
(with-open [conn (db/open)]
|
||||
(let [project (db/get-by-id conn :project project-id)]
|
||||
(let [project (db/get-by-id conn :project id)]
|
||||
(check-edition-permissions! conn profile-id project)
|
||||
project)))
|
||||
|
|
|
@ -18,19 +18,17 @@
|
|||
[app.services.queries.projects :as projects :refer [retrieve-projects]]
|
||||
[app.services.queries.files :refer [decode-row-xf]]))
|
||||
|
||||
(def sql:project-recent-files
|
||||
"select f.*
|
||||
from file as f
|
||||
where f.project_id = ?
|
||||
and f.deleted_at is null
|
||||
order by f.modified_at desc
|
||||
limit 5")
|
||||
|
||||
(defn recent-by-project
|
||||
[conn profile-id project]
|
||||
(let [project-id (:id project)]
|
||||
(projects/check-edition-permissions! conn profile-id project)
|
||||
(into [] decode-row-xf (db/exec! conn [sql:project-recent-files project-id]))))
|
||||
(def sql:recent-files
|
||||
"with recent_files as (
|
||||
select f.*, row_number() over w as row_num
|
||||
from file as f
|
||||
join project as p on (p.id = f.project_id)
|
||||
where p.team_id = ?
|
||||
and p.deleted_at is null
|
||||
window w as (partition by f.project_id order by f.modified_at desc)
|
||||
order by f.modified_at desc
|
||||
)
|
||||
select * from recent_files where row_num <= 6;")
|
||||
|
||||
(s/def ::team-id ::us/uuid)
|
||||
(s/def ::profile-id ::us/uuid)
|
||||
|
@ -42,9 +40,7 @@
|
|||
[{:keys [profile-id team-id]}]
|
||||
(with-open [conn (db/open)]
|
||||
(teams/check-read-permissions! conn profile-id team-id)
|
||||
(->> (retrieve-projects conn team-id)
|
||||
;; Retrieve for each proyect the 5 more recent files
|
||||
(map (partial recent-by-project conn profile-id))
|
||||
;; Change the structure so it's a map with project-id as keys
|
||||
(flatten)
|
||||
(group-by :project-id))))
|
||||
(let [files (db/exec! conn [sql:recent-files team-id])]
|
||||
(into [] decode-row-xf files))))
|
||||
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
[app.common.uuid :as uuid]
|
||||
[app.db :as db]
|
||||
[app.services.queries :as sq]
|
||||
[app.services.queries.profile :as profile]
|
||||
[app.util.blob :as blob]))
|
||||
|
||||
;; --- Team Edition Permissions
|
||||
|
@ -43,3 +44,54 @@
|
|||
(when-not row
|
||||
(ex/raise :type :validation
|
||||
:code :not-authorized))))
|
||||
|
||||
|
||||
;; --- Query: Teams
|
||||
|
||||
(declare retrieve-teams)
|
||||
|
||||
(s/def ::profile-id ::us/uuid)
|
||||
(s/def ::teams
|
||||
(s/keys :req-un [::profile-id]))
|
||||
|
||||
(sq/defquery ::teams
|
||||
[{:keys [profile-id]}]
|
||||
(with-open [conn (db/open)]
|
||||
(retrieve-teams conn profile-id)))
|
||||
|
||||
(def sql:teams
|
||||
"select t.*,
|
||||
tp.is_owner,
|
||||
tp.is_admin,
|
||||
tp.can_edit,
|
||||
(t.id = ?) as is_default
|
||||
from team_profile_rel as tp
|
||||
join team as t on (t.id = tp.team_id)
|
||||
where t.deleted_at is null
|
||||
and tp.profile_id = ?
|
||||
order by t.created_at asc")
|
||||
|
||||
(defn retrieve-teams
|
||||
[conn profile-id]
|
||||
(let [defaults (profile/retrieve-additional-data conn profile-id)]
|
||||
(db/exec! conn [sql:teams (:default-team-id defaults) profile-id])))
|
||||
|
||||
;; --- Query: Projec by ID
|
||||
|
||||
(declare retrieve-team-projects)
|
||||
(declare retrieve-team)
|
||||
|
||||
(s/def ::id ::us/uuid)
|
||||
(s/def ::team
|
||||
(s/keys :req-un [::profile-id ::id]))
|
||||
|
||||
(sq/defquery ::team
|
||||
[{:keys [profile-id id]}]
|
||||
(with-open [conn (db/open)]
|
||||
(retrieve-team conn profile-id id)))
|
||||
|
||||
(defn- retrieve-team
|
||||
[conn profile-id team-id]
|
||||
(let [defaults (profile/retrieve-additional-data conn profile-id)
|
||||
sql (str "WITH teams AS (" sql:teams ") SELECT * FROM teams WHERE id=?")]
|
||||
(db/exec-one! conn [sql (:default-team-id defaults) profile-id team-id])))
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue