mirror of
https://github.com/penpot/penpot.git
synced 2025-03-19 03:01:27 -05:00
Merge pull request #2686 from penpot/niwinz-enhancements-5
General enhancements & Bugfixes
This commit is contained in:
commit
fe11b37b8f
66 changed files with 1355 additions and 717 deletions
|
@ -2,7 +2,7 @@
|
|||
|
||||
export PENPOT_HOST=devenv
|
||||
export PENPOT_TENANT=dev
|
||||
export PENPOT_FLAGS="$PENPOT_FLAGS enable-backend-asserts enable-audit-log enable-transit-readable-response enable-demo-users disable-secure-session-cookies enable-smtp"
|
||||
export PENPOT_FLAGS="$PENPOT_FLAGS enable-backend-asserts enable-audit-log enable-transit-readable-response enable-demo-users disable-secure-session-cookies enable-smtp enable-webhooks"
|
||||
|
||||
# export PENPOT_DATABASE_URI="postgresql://172.17.0.1:5432/penpot"
|
||||
# export PENPOT_DATABASE_USERNAME="penpot"
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
export PENPOT_HOST=devenv
|
||||
export PENPOT_TENANT=dev
|
||||
export PENPOT_FLAGS="$PENPOT_FLAGS enable-backend-asserts enable-audit-log enable-transit-readable-response enable-demo-users disable-secure-session-cookies enable-smtp"
|
||||
export PENPOT_FLAGS="$PENPOT_FLAGS enable-backend-asserts enable-audit-log enable-transit-readable-response enable-demo-users disable-secure-session-cookies enable-smtp enable-webhooks"
|
||||
|
||||
set -ex
|
||||
|
||||
|
|
26
backend/src/app/auth.clj
Normal file
26
backend/src/app/auth.clj
Normal file
|
@ -0,0 +1,26 @@
|
|||
;; 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) KALEIDOS INC
|
||||
|
||||
(ns app.auth
|
||||
(:require
|
||||
[buddy.hashers :as hashers]))
|
||||
|
||||
(defn derive-password
|
||||
[password]
|
||||
(hashers/derive password
|
||||
{:alg :argon2id
|
||||
:memory 16384
|
||||
:iterations 20
|
||||
:parallelism 2}))
|
||||
|
||||
(defn verify-password
|
||||
[attempt password]
|
||||
(try
|
||||
(hashers/verify attempt password)
|
||||
(catch Throwable _
|
||||
{:update false
|
||||
:valid false})))
|
||||
|
|
@ -106,6 +106,9 @@
|
|||
(s/def ::file-change-snapshot-every ::us/integer)
|
||||
(s/def ::file-change-snapshot-timeout ::dt/duration)
|
||||
|
||||
(s/def ::setup-admin-email ::us/email)
|
||||
(s/def ::setup-admin-password ::us/not-empty-string)
|
||||
|
||||
(s/def ::default-executor-parallelism ::us/integer)
|
||||
(s/def ::scheduled-executor-parallelism ::us/integer)
|
||||
|
||||
|
@ -295,6 +298,9 @@
|
|||
::srepl-host
|
||||
::srepl-port
|
||||
|
||||
::setup-admin-email
|
||||
::setup-admin-password
|
||||
|
||||
::assets-storage-backend
|
||||
::storage-assets-fs-directory
|
||||
::storage-assets-s3-bucket
|
||||
|
|
|
@ -290,7 +290,11 @@
|
|||
{:pool (ig/ref ::db/pool)
|
||||
:executor (ig/ref ::wrk/executor)
|
||||
:storage (ig/ref ::sto/storage)
|
||||
:session (ig/ref :app.http.session/manager)}
|
||||
:session (ig/ref :app.http.session/manager)
|
||||
|
||||
::db/pool (ig/ref ::db/pool)
|
||||
::wrk/executor (ig/ref ::wrk/executor)
|
||||
::sto/storage (ig/ref ::sto/storage)}
|
||||
|
||||
:app.http.websocket/handler
|
||||
{:pool (ig/ref ::db/pool)
|
||||
|
@ -385,8 +389,8 @@
|
|||
:max-age cf/deletion-delay}
|
||||
|
||||
:app.tasks.objects-gc/handler
|
||||
{:pool (ig/ref ::db/pool)
|
||||
:storage (ig/ref ::sto/storage)}
|
||||
{::db/pool (ig/ref ::db/pool)
|
||||
::sto/storage (ig/ref ::sto/storage)}
|
||||
|
||||
:app.tasks.file-gc/handler
|
||||
{:pool (ig/ref ::db/pool)}
|
||||
|
@ -403,6 +407,9 @@
|
|||
{:port (cf/get :srepl-port)
|
||||
:host (cf/get :srepl-host)}
|
||||
|
||||
:app.setup/initial-profile
|
||||
{::db/pool (ig/ref ::db/pool)}
|
||||
|
||||
:app.setup/builtin-templates
|
||||
{::http.client/client (ig/ref ::http.client/client)}
|
||||
|
||||
|
|
|
@ -132,7 +132,7 @@
|
|||
|
||||
(defmethod run-collector! :counter
|
||||
[{:keys [::mdef/instance]} {:keys [inc labels] :or {inc 1 labels default-empty-labels}}]
|
||||
(let [instance (.labels instance (if (is-array? labels) labels (into-array String labels)))]
|
||||
(let [instance (.labels ^Counter instance (if (is-array? labels) labels (into-array String labels)))]
|
||||
(.inc ^Counter$Child instance (double inc))))
|
||||
|
||||
(defmethod run-collector! :gauge
|
||||
|
|
|
@ -271,6 +271,37 @@
|
|||
|
||||
{:name "0087-mod-task-table"
|
||||
:fn (mg/resource "app/migrations/sql/0087-mod-task-table.sql")}
|
||||
|
||||
{:name "0088-mod-team-profile-rel-table"
|
||||
:fn (mg/resource "app/migrations/sql/0088-mod-team-profile-rel-table.sql")}
|
||||
|
||||
{:name "0089-mod-project-profile-rel-table"
|
||||
:fn (mg/resource "app/migrations/sql/0089-mod-project-profile-rel-table.sql")}
|
||||
|
||||
{:name "0090-mod-http-session-table"
|
||||
:fn (mg/resource "app/migrations/sql/0090-mod-http-session-table.sql")}
|
||||
|
||||
{:name "0091-mod-team-project-profile-rel-table"
|
||||
:fn (mg/resource "app/migrations/sql/0091-mod-team-project-profile-rel-table.sql")}
|
||||
|
||||
{:name "0092-mod-team-invitation-table"
|
||||
:fn (mg/resource "app/migrations/sql/0092-mod-team-invitation-table.sql")}
|
||||
|
||||
{:name "0093-del-file-share-tokens-table"
|
||||
:fn (mg/resource "app/migrations/sql/0093-del-file-share-tokens-table.sql")}
|
||||
|
||||
{:name "0094-del-profile-attr-table"
|
||||
:fn (mg/resource "app/migrations/sql/0094-del-profile-attr-table.sql")}
|
||||
|
||||
{:name "0095-del-storage-data-table"
|
||||
:fn (mg/resource "app/migrations/sql/0095-del-storage-data-table.sql")}
|
||||
|
||||
{:name "0096-del-storage-pending-table"
|
||||
:fn (mg/resource "app/migrations/sql/0096-del-storage-pending-table.sql")}
|
||||
|
||||
{:name "0097-mod-profile-table"
|
||||
:fn (mg/resource "app/migrations/sql/0097-mod-profile-table.sql")}
|
||||
|
||||
])
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
ALTER TABLE team_profile_rel DROP CONSTRAINT team_profile_rel_pkey;
|
||||
ALTER TABLE team_profile_rel ADD COLUMN id uuid DEFAULT uuid_generate_v4() PRIMARY KEY;
|
||||
ALTER TABLE team_profile_rel ADD CONSTRAINT team_profile_rel_unique UNIQUE (team_id, profile_id);
|
|
@ -0,0 +1,3 @@
|
|||
ALTER TABLE project_profile_rel DROP CONSTRAINT project_profile_rel_pkey;
|
||||
ALTER TABLE project_profile_rel ADD COLUMN id uuid DEFAULT uuid_generate_v4() PRIMARY KEY;
|
||||
ALTER TABLE project_profile_rel ADD CONSTRAINT project_profile_rel_unique UNIQUE (project_id, profile_id);
|
|
@ -0,0 +1,2 @@
|
|||
ALTER TABLE http_session DROP CONSTRAINT http_session_pkey;
|
||||
ALTER TABLE http_session ADD CONSTRAINT http_session_pkey PRIMARY KEY (id);
|
|
@ -0,0 +1,3 @@
|
|||
ALTER TABLE team_project_profile_rel DROP CONSTRAINT team_project_profile_rel_pkey;
|
||||
ALTER TABLE team_project_profile_rel ADD COLUMN id uuid DEFAULT uuid_generate_v4() PRIMARY KEY;
|
||||
ALTER TABLE team_project_profile_rel ADD CONSTRAINT team_project_profile_rel_unique UNIQUE (team_id, project_id, profile_id);
|
|
@ -0,0 +1,3 @@
|
|||
ALTER TABLE team_invitation DROP CONSTRAINT team_invitation_pkey;
|
||||
ALTER TABLE team_invitation ADD COLUMN id uuid DEFAULT uuid_generate_v4() PRIMARY KEY;
|
||||
ALTER TABLE team_invitation ADD CONSTRAINT team_invitation_unique UNIQUE (team_id, email_to);
|
|
@ -0,0 +1 @@
|
|||
DROP TABLE file_share_token;
|
|
@ -0,0 +1 @@
|
|||
DROP TABLE profile_attr;
|
|
@ -0,0 +1 @@
|
|||
DROP TABLE storage_data;
|
|
@ -0,0 +1 @@
|
|||
DROP TABLE storage_pending;
|
|
@ -0,0 +1,2 @@
|
|||
ALTER TABLE profile
|
||||
ADD COLUMN is_admin boolean DEFAULT false;
|
|
@ -35,6 +35,8 @@
|
|||
[yetti.request :as yrq]
|
||||
[yetti.response :as yrs]))
|
||||
|
||||
(s/def ::profile-id ::us/uuid)
|
||||
|
||||
(defn- default-handler
|
||||
[_]
|
||||
(p/rejected (ex/error :type :not-found)))
|
||||
|
@ -72,8 +74,11 @@
|
|||
(let [type (keyword (:type params))
|
||||
data (into {::http/request request} params)
|
||||
data (if profile-id
|
||||
(assoc data :profile-id profile-id ::session-id session-id)
|
||||
(dissoc data :profile-id))
|
||||
(assoc data
|
||||
:profile-id profile-id
|
||||
::profile-id profile-id
|
||||
::session-id session-id)
|
||||
(dissoc data :profile-id ::profile-id))
|
||||
method (get methods type default-handler)]
|
||||
|
||||
(-> (method data)
|
||||
|
@ -90,8 +95,11 @@
|
|||
(let [type (keyword (:type params))
|
||||
data (into {::http/request request} params)
|
||||
data (if profile-id
|
||||
(assoc data :profile-id profile-id ::session-id session-id)
|
||||
(dissoc data :profile-id))
|
||||
(assoc data
|
||||
:profile-id profile-id
|
||||
::profile-id profile-id
|
||||
::session-id session-id)
|
||||
(dissoc data :profile-id ::profile-id))
|
||||
|
||||
method (get methods type default-handler)]
|
||||
(-> (method data)
|
||||
|
@ -109,9 +117,8 @@
|
|||
etag (yrq/get-header request "if-none-match")
|
||||
data (into {::http/request request ::cond/key etag} params)
|
||||
data (if profile-id
|
||||
(assoc data :profile-id profile-id ::session-id session-id)
|
||||
(dissoc data :profile-id))
|
||||
|
||||
(assoc data ::profile-id profile-id ::session-id session-id)
|
||||
(dissoc data ::profile-id))
|
||||
method (get methods cmd default-handler)]
|
||||
(binding [cond/*enabled* true]
|
||||
(-> (method data)
|
||||
|
@ -152,9 +159,12 @@
|
|||
(letfn [(handle-audit [params result]
|
||||
(let [resultm (meta result)
|
||||
request (::http/request params)
|
||||
|
||||
profile-id (or (::audit/profile-id resultm)
|
||||
(:profile-id result)
|
||||
(:profile-id params)
|
||||
(if (= (::type cfg) "command")
|
||||
(::profile-id params)
|
||||
(:profile-id params))
|
||||
uuid/zero)
|
||||
|
||||
props (-> (or (::audit/replace-props resultm)
|
||||
|
@ -209,21 +219,24 @@
|
|||
(wrap-audit cfg $ mdata))
|
||||
|
||||
spec (or (::sv/spec mdata) (s/spec any?))
|
||||
auth? (:auth mdata true)]
|
||||
auth? (::auth mdata true)]
|
||||
|
||||
|
||||
(l/debug :hint "register method" :name (::sv/name mdata))
|
||||
(with-meta
|
||||
(fn [params]
|
||||
;; Raise authentication error when rpc method requires auth but
|
||||
;; no profile-id is found in the request.
|
||||
|
||||
(p/do!
|
||||
(if (and auth? (not (uuid? (:profile-id params))))
|
||||
(ex/raise :type :authentication
|
||||
:code :authentication-required
|
||||
:hint "authentication required for this endpoint")
|
||||
(let [params (us/conform spec params)]
|
||||
(f cfg params)))))
|
||||
(let [profile-id (if (= "command" (::type cfg))
|
||||
(::profile-id params)
|
||||
(:profile-id params))]
|
||||
(p/do!
|
||||
(if (and auth? (not (uuid? profile-id)))
|
||||
(ex/raise :type :authentication
|
||||
:code :authentication-required
|
||||
:hint "authentication required for this endpoint")
|
||||
(let [params (us/conform spec params)]
|
||||
(f cfg params))))))
|
||||
mdata)))
|
||||
|
||||
(defn- process-method
|
||||
|
@ -262,6 +275,7 @@
|
|||
(let [cfg (assoc cfg ::type "command" ::metrics-id :rpc-command-timing)]
|
||||
(->> (sv/scan-ns 'app.rpc.commands.binfile
|
||||
'app.rpc.commands.comments
|
||||
'app.rpc.commands.profile
|
||||
'app.rpc.commands.management
|
||||
'app.rpc.commands.verify-token
|
||||
'app.rpc.commands.search
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
[app.db :as db]
|
||||
[app.http :as-alias http]
|
||||
[app.loggers.audit :as audit]
|
||||
[app.rpc :as-alias rpc]
|
||||
[app.rpc.climit :as-alias climit]
|
||||
[app.rpc.doc :as-alias doc]
|
||||
[app.rpc.helpers :as rph]
|
||||
|
@ -41,7 +42,7 @@
|
|||
:profile-id :ip-addr :props :context])
|
||||
|
||||
(defn- handle-events
|
||||
[{:keys [::db/pool]} {:keys [profile-id events ::http/request] :as params}]
|
||||
[{:keys [::db/pool]} {:keys [::rpc/profile-id events ::http/request] :as params}]
|
||||
(let [ip-addr (audit/parse-client-ip request)
|
||||
xform (comp
|
||||
(map #(assoc % :profile-id profile-id))
|
||||
|
@ -53,7 +54,6 @@
|
|||
(when (seq events)
|
||||
(db/insert-multi! pool :audit-log event-columns events))))
|
||||
|
||||
(s/def ::profile-id ::us/uuid)
|
||||
(s/def ::name ::us/string)
|
||||
(s/def ::type ::us/string)
|
||||
(s/def ::props (s/map-of ::us/keyword any?))
|
||||
|
@ -67,7 +67,8 @@
|
|||
(s/def ::events (s/every ::event))
|
||||
|
||||
(s/def ::push-audit-events
|
||||
(s/keys :req-un [::events ::profile-id]))
|
||||
(s/keys :req [::rpc/profile-id]
|
||||
:req-un [::events]))
|
||||
|
||||
(sv/defmethod ::push-audit-events
|
||||
{::climit/queue :push-audit-events
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
(ns app.rpc.commands.auth
|
||||
(:require
|
||||
[app.auth :as auth]
|
||||
[app.common.data :as d]
|
||||
[app.common.exceptions :as ex]
|
||||
[app.common.spec :as us]
|
||||
|
@ -15,6 +16,8 @@
|
|||
[app.emails :as eml]
|
||||
[app.http.session :as session]
|
||||
[app.loggers.audit :as audit]
|
||||
[app.main :as-alias main]
|
||||
[app.rpc :as-alias rpc]
|
||||
[app.rpc.climit :as climit]
|
||||
[app.rpc.commands.teams :as teams]
|
||||
[app.rpc.doc :as-alias doc]
|
||||
|
@ -23,7 +26,6 @@
|
|||
[app.tokens :as tokens]
|
||||
[app.util.services :as sv]
|
||||
[app.util.time :as dt]
|
||||
[buddy.hashers :as hashers]
|
||||
[clojure.spec.alpha :as s]
|
||||
[cuerdas.core :as str]))
|
||||
|
||||
|
@ -31,7 +33,6 @@
|
|||
(s/def ::fullname ::us/not-empty-string)
|
||||
(s/def ::lang ::us/string)
|
||||
(s/def ::path ::us/string)
|
||||
(s/def ::profile-id ::us/uuid)
|
||||
(s/def ::password ::us/not-empty-string)
|
||||
(s/def ::old-password ::us/not-empty-string)
|
||||
(s/def ::theme ::us/string)
|
||||
|
@ -40,22 +41,6 @@
|
|||
|
||||
;; ---- HELPERS
|
||||
|
||||
(defn derive-password
|
||||
[password]
|
||||
(hashers/derive password
|
||||
{:alg :argon2id
|
||||
:memory 16384
|
||||
:iterations 20
|
||||
:parallelism 2}))
|
||||
|
||||
(defn verify-password
|
||||
[attempt password]
|
||||
(try
|
||||
(hashers/verify attempt password)
|
||||
(catch Exception _e
|
||||
{:update false
|
||||
:valid false})))
|
||||
|
||||
(defn email-domain-in-whitelist?
|
||||
"Returns true if email's domain is in the given whitelist or if
|
||||
given whitelist is an empty string."
|
||||
|
@ -84,9 +69,10 @@
|
|||
;; ---- COMMAND: login with password
|
||||
|
||||
(defn login-with-password
|
||||
[{:keys [pool session sprops] :as cfg} {:keys [email password] :as params}]
|
||||
[{:keys [::db/pool session] :as cfg} {:keys [email password scope] :as params}]
|
||||
|
||||
(when-not (contains? cf/flags :login)
|
||||
(when-not (or (contains? cf/flags :login)
|
||||
(contains? cf/flags :login-with-password))
|
||||
(ex/raise :type :restriction
|
||||
:code :login-disabled
|
||||
:hint "login is disabled in this instance"))
|
||||
|
@ -96,7 +82,7 @@
|
|||
(ex/raise :type :validation
|
||||
:code :account-without-password
|
||||
:hint "the current account does not have password"))
|
||||
(:valid (verify-password password (:password profile))))
|
||||
(:valid (auth/verify-password password (:password profile))))
|
||||
|
||||
(validate-profile [profile]
|
||||
(when-not profile
|
||||
|
@ -126,27 +112,37 @@
|
|||
(profile/decode-profile-row))
|
||||
|
||||
invitation (when-let [token (:invitation-token params)]
|
||||
(tokens/verify sprops {:token token :iss :team-invitation}))
|
||||
(tokens/verify (::main/props cfg) {:token token :iss :team-invitation}))
|
||||
|
||||
;; If invitation member-id does not matches the profile-id, we just proceed to ignore the
|
||||
;; invitation because invitations matches exactly; and user can't login with other email and
|
||||
;; accept invitation with other email
|
||||
response (if (and (some? invitation) (= (:id profile) (:member-id invitation)))
|
||||
{:invitation-token (:invitation-token params)}
|
||||
profile)]
|
||||
(update profile :is-admin (fn [admin?]
|
||||
(or admin?
|
||||
(let [admins (cf/get :admins)]
|
||||
(contains? admins (:email profile)))))))]
|
||||
|
||||
(when (and (nil? (:default-team-id profile))
|
||||
(not= scope "admin"))
|
||||
(ex/raise :type :restriction
|
||||
:code :admin-only-profile
|
||||
:hint "can't login with admin-only profile"))
|
||||
|
||||
(-> response
|
||||
(rph/with-transform (session/create-fn session (:id profile)))
|
||||
(rph/with-meta {::audit/props (audit/profile->props profile)
|
||||
::audit/profile-id (:id profile)}))))))
|
||||
|
||||
(s/def ::scope ::us/string)
|
||||
(s/def ::login-with-password
|
||||
(s/keys :req-un [::email ::password]
|
||||
:opt-un [::invitation-token]))
|
||||
:opt-un [::invitation-token ::scope]))
|
||||
|
||||
(sv/defmethod ::login-with-password
|
||||
"Performs authentication using penpot password."
|
||||
{:auth false
|
||||
{::rpc/auth false
|
||||
::climit/queue :auth
|
||||
::doc/added "1.15"}
|
||||
[cfg params]
|
||||
|
@ -155,11 +151,11 @@
|
|||
;; ---- COMMAND: Logout
|
||||
|
||||
(s/def ::logout
|
||||
(s/keys :opt-un [::profile-id]))
|
||||
(s/keys :opt [::rpc/profile-id]))
|
||||
|
||||
(sv/defmethod ::logout
|
||||
"Clears the authentication cookie and logout the current session."
|
||||
{:auth false
|
||||
{::rpc/auth false
|
||||
::doc/added "1.15"}
|
||||
[{:keys [session] :as cfg} _]
|
||||
(rph/with-transform {} (session/delete-fn session)))
|
||||
|
@ -167,13 +163,13 @@
|
|||
;; ---- COMMAND: Recover Profile
|
||||
|
||||
(defn recover-profile
|
||||
[{:keys [pool sprops] :as cfg} {:keys [token password]}]
|
||||
[{:keys [::db/pool] :as cfg} {:keys [token password]}]
|
||||
(letfn [(validate-token [token]
|
||||
(let [tdata (tokens/verify sprops {:token token :iss :password-recovery})]
|
||||
(let [tdata (tokens/verify (::main/props cfg) {:token token :iss :password-recovery})]
|
||||
(:profile-id tdata)))
|
||||
|
||||
(update-password [conn profile-id]
|
||||
(let [pwd (derive-password password)]
|
||||
(let [pwd (auth/derive-password password)]
|
||||
(db/update! conn :profile {:password pwd} {:id profile-id})))]
|
||||
|
||||
(db/with-atomic [conn pool]
|
||||
|
@ -186,7 +182,7 @@
|
|||
(s/keys :req-un [::token ::password]))
|
||||
|
||||
(sv/defmethod ::recover-profile
|
||||
{:auth false
|
||||
{::rpc/auth false
|
||||
::climit/queue :auth
|
||||
::doc/added "1.15"}
|
||||
[cfg params]
|
||||
|
@ -195,13 +191,13 @@
|
|||
;; ---- COMMAND: Prepare Register
|
||||
|
||||
(defn validate-register-attempt!
|
||||
[{:keys [pool sprops]} params]
|
||||
[{:keys [::db/pool] :as cfg} params]
|
||||
|
||||
(when-not (contains? cf/flags :registration)
|
||||
(if-not (contains? params :invitation-token)
|
||||
(ex/raise :type :restriction
|
||||
:code :registration-disabled)
|
||||
(let [invitation (tokens/verify sprops {:token (:invitation-token params) :iss :team-invitation})]
|
||||
(let [invitation (tokens/verify (::main/props cfg) {:token (:invitation-token params) :iss :team-invitation})]
|
||||
(when-not (= (:email params) (:member-email invitation))
|
||||
(ex/raise :type :restriction
|
||||
:code :email-does-not-match-invitation
|
||||
|
@ -235,7 +231,7 @@
|
|||
(pos? (compare elapsed register-retry-threshold))))
|
||||
|
||||
(defn prepare-register
|
||||
[{:keys [pool sprops] :as cfg} params]
|
||||
[{:keys [::db/pool] :as cfg} params]
|
||||
|
||||
(validate-register-attempt! cfg params)
|
||||
|
||||
|
@ -264,7 +260,7 @@
|
|||
|
||||
params (d/without-nils params)
|
||||
|
||||
token (tokens/generate sprops params)]
|
||||
token (tokens/generate (::main/props cfg) params)]
|
||||
(with-meta {:token token}
|
||||
{::audit/profile-id uuid/zero})))
|
||||
|
||||
|
@ -273,7 +269,7 @@
|
|||
:opt-un [::invitation-token]))
|
||||
|
||||
(sv/defmethod ::prepare-register-profile
|
||||
{:auth false
|
||||
{::rpc/auth false
|
||||
::doc/added "1.15"}
|
||||
[cfg params]
|
||||
(prepare-register cfg params))
|
||||
|
@ -293,7 +289,7 @@
|
|||
(db/tjson))
|
||||
|
||||
password (if-let [password (:password params)]
|
||||
(derive-password password)
|
||||
(auth/derive-password password)
|
||||
"!")
|
||||
|
||||
locale (:locale params)
|
||||
|
@ -339,15 +335,15 @@
|
|||
(assoc :default-project-id (:default-project-id team)))))
|
||||
|
||||
(defn send-email-verification!
|
||||
[conn sprops profile]
|
||||
(let [vtoken (tokens/generate sprops
|
||||
[conn props profile]
|
||||
(let [vtoken (tokens/generate props
|
||||
{:iss :verify-email
|
||||
:exp (dt/in-future "72h")
|
||||
:profile-id (:id profile)
|
||||
:email (:email profile)})
|
||||
;; NOTE: this token is mainly used for possible complains
|
||||
;; identification on the sns webhook
|
||||
ptoken (tokens/generate sprops
|
||||
ptoken (tokens/generate props
|
||||
{:iss :profile-identity
|
||||
:profile-id (:id profile)
|
||||
:exp (dt/in-future {:days 30})})]
|
||||
|
@ -360,8 +356,8 @@
|
|||
:extra-data ptoken})))
|
||||
|
||||
(defn register-profile
|
||||
[{:keys [conn sprops session] :as cfg} {:keys [token] :as params}]
|
||||
(let [claims (tokens/verify sprops {:token token :iss :prepared-register})
|
||||
[{:keys [conn session] :as cfg} {:keys [token] :as params}]
|
||||
(let [claims (tokens/verify (::main/props cfg) {:token token :iss :prepared-register})
|
||||
params (merge params claims)
|
||||
|
||||
is-active (or (:is-active params)
|
||||
|
@ -377,7 +373,7 @@
|
|||
(create-profile-relations conn)
|
||||
(profile/decode-profile-row)))
|
||||
invitation (when-let [token (:invitation-token params)]
|
||||
(tokens/verify sprops {:token token :iss :team-invitation}))]
|
||||
(tokens/verify (::main/props cfg) {:token token :iss :team-invitation}))]
|
||||
|
||||
;; If profile is filled in claims, means it tries to register
|
||||
;; again, so we proceed to update the modified-at attr
|
||||
|
@ -399,7 +395,7 @@
|
|||
;; email.
|
||||
(and (some? invitation) (= (:email profile) (:member-email invitation)))
|
||||
(let [claims (assoc invitation :member-id (:id profile))
|
||||
token (tokens/generate sprops claims)
|
||||
token (tokens/generate (::main/props cfg) claims)
|
||||
resp {:invitation-token token}]
|
||||
(-> resp
|
||||
(rph/with-transform (session/create-fn session (:id profile)))
|
||||
|
@ -426,7 +422,7 @@
|
|||
;; In all other cases, send a verification email.
|
||||
:else
|
||||
(do
|
||||
(send-email-verification! conn sprops profile)
|
||||
(send-email-verification! conn (::main/props cfg) profile)
|
||||
(rph/with-meta profile
|
||||
{::audit/replace-props (audit/profile->props profile)
|
||||
::audit/profile-id (:id profile)})))))
|
||||
|
@ -435,10 +431,10 @@
|
|||
(s/keys :req-un [::token ::fullname]))
|
||||
|
||||
(sv/defmethod ::register-profile
|
||||
{:auth false
|
||||
{::rpc/auth false
|
||||
::climit/queue :auth
|
||||
::doc/added "1.15"}
|
||||
[{:keys [pool] :as cfg} params]
|
||||
[{:keys [::db/pool] :as cfg} params]
|
||||
(db/with-atomic [conn pool]
|
||||
(-> (assoc cfg :conn conn)
|
||||
(register-profile params))))
|
||||
|
@ -446,16 +442,16 @@
|
|||
;; ---- COMMAND: Request Profile Recovery
|
||||
|
||||
(defn request-profile-recovery
|
||||
[{:keys [pool sprops] :as cfg} {:keys [email] :as params}]
|
||||
[{:keys [::db/pool] :as cfg} {:keys [email] :as params}]
|
||||
(letfn [(create-recovery-token [{:keys [id] :as profile}]
|
||||
(let [token (tokens/generate sprops
|
||||
(let [token (tokens/generate (::main/props cfg)
|
||||
{:iss :password-recovery
|
||||
:exp (dt/in-future "15m")
|
||||
:profile-id id})]
|
||||
(assoc profile :token token)))
|
||||
|
||||
(send-email-notification [conn profile]
|
||||
(let [ptoken (tokens/generate sprops
|
||||
(let [ptoken (tokens/generate (::main/props cfg)
|
||||
{:iss :profile-identity
|
||||
:profile-id (:id profile)
|
||||
:exp (dt/in-future {:days 30})})]
|
||||
|
@ -493,7 +489,7 @@
|
|||
(s/keys :req-un [::email]))
|
||||
|
||||
(sv/defmethod ::request-profile-recovery
|
||||
{:auth false
|
||||
{::rpc/auth false
|
||||
::doc/added "1.15"}
|
||||
[cfg params]
|
||||
(request-profile-recovery cfg params))
|
||||
|
|
|
@ -15,10 +15,13 @@
|
|||
[app.common.uuid :as uuid]
|
||||
[app.config :as cf]
|
||||
[app.db :as db]
|
||||
[app.loggers.audit :as-alias audit]
|
||||
[app.loggers.webhooks :as-alias webhooks]
|
||||
[app.media :as media]
|
||||
[app.rpc :as-alias rpc]
|
||||
[app.rpc.commands.files :as files]
|
||||
[app.rpc.doc :as-alias doc]
|
||||
[app.rpc.helpers :as rph]
|
||||
[app.rpc.queries.projects :as projects]
|
||||
[app.storage :as sto]
|
||||
[app.storage.tmp :as tmp]
|
||||
|
@ -291,7 +294,7 @@
|
|||
|
||||
(defn- retrieve-file
|
||||
[pool file-id]
|
||||
(with-open [conn (db/open pool)]
|
||||
(with-open [^AutoCloseable conn (db/open pool)]
|
||||
(binding [pmap/*load-fn* (partial files/load-pointer conn file-id)]
|
||||
(some-> (db/get* conn :file {:id file-id})
|
||||
(files/decode-row)
|
||||
|
@ -864,18 +867,17 @@
|
|||
;; --- Command: export-binfile
|
||||
|
||||
(s/def ::file-id ::us/uuid)
|
||||
(s/def ::profile-id ::us/uuid)
|
||||
(s/def ::include-libraries? ::us/boolean)
|
||||
(s/def ::embed-assets? ::us/boolean)
|
||||
|
||||
(s/def ::export-binfile
|
||||
(s/keys :req-un [::profile-id ::file-id ::include-libraries? ::embed-assets?]))
|
||||
(s/keys :req [::rpc/profile-id] :req-un [::file-id ::include-libraries? ::embed-assets?]))
|
||||
|
||||
(sv/defmethod ::export-binfile
|
||||
"Export a penpot file in a binary format."
|
||||
{::doc/added "1.15"
|
||||
::webhooks/event? true}
|
||||
[{:keys [pool] :as cfg} {:keys [profile-id file-id include-libraries? embed-assets?] :as params}]
|
||||
[{:keys [pool] :as cfg} {:keys [::rpc/profile-id file-id include-libraries? embed-assets?] :as params}]
|
||||
(files/check-read-permissions! pool profile-id file-id)
|
||||
(let [body (reify yrs/StreamableResponseBody
|
||||
(-write-body-to-stream [_ _ output-stream]
|
||||
|
@ -890,16 +892,18 @@
|
|||
|
||||
(s/def ::file ::media/upload)
|
||||
(s/def ::import-binfile
|
||||
(s/keys :req-un [::profile-id ::project-id ::file]))
|
||||
(s/keys :req [::rpc/profile-id] :req-un [::project-id ::file]))
|
||||
|
||||
(sv/defmethod ::import-binfile
|
||||
"Import a penpot file in a binary format."
|
||||
{::doc/added "1.15"
|
||||
::webhooks/event? true}
|
||||
[{:keys [pool] :as cfg} {:keys [profile-id project-id file] :as params}]
|
||||
[{:keys [pool] :as cfg} {:keys [::rpc/profile-id project-id file] :as params}]
|
||||
(db/with-atomic [conn pool]
|
||||
(projects/check-read-permissions! conn profile-id project-id)
|
||||
(import! (assoc cfg
|
||||
::input (:path file)
|
||||
::project-id project-id
|
||||
::ignore-index-errors? true))))
|
||||
(let [ids (import! (assoc cfg
|
||||
::input (:path file)
|
||||
::project-id project-id
|
||||
::ignore-index-errors? true))]
|
||||
(rph/with-meta ids
|
||||
{::audit/props {:file nil :file-ids ids}}))))
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
[app.db :as db]
|
||||
[app.loggers.audit :as-alias audit]
|
||||
[app.loggers.webhooks :as-alias webhooks]
|
||||
[app.rpc :as-alias rpc]
|
||||
[app.rpc.commands.files :as files]
|
||||
[app.rpc.commands.teams :as teams]
|
||||
[app.rpc.doc :as-alias doc]
|
||||
|
@ -41,7 +42,7 @@
|
|||
(s/def ::share-id (s/nilable ::us/uuid))
|
||||
|
||||
(s/def ::get-comment-threads
|
||||
(s/and (s/keys :req-un [::profile-id]
|
||||
(s/and (s/keys :req [::rpc/profile-id]
|
||||
:opt-un [::file-id ::share-id ::team-id])
|
||||
#(or (:file-id %) (:team-id %))))
|
||||
|
||||
|
@ -74,7 +75,7 @@
|
|||
window w as (partition by c.thread_id order by c.created_at asc)")
|
||||
|
||||
(defn retrieve-comment-threads
|
||||
[conn {:keys [profile-id file-id share-id]}]
|
||||
[conn {:keys [::rpc/profile-id file-id share-id]}]
|
||||
(files/check-comment-permissions! conn profile-id file-id share-id)
|
||||
(->> (db/exec! conn [sql:comment-threads profile-id file-id])
|
||||
(into [] (map decode-row))))
|
||||
|
@ -85,11 +86,12 @@
|
|||
|
||||
(s/def ::team-id ::us/uuid)
|
||||
(s/def ::get-unread-comment-threads
|
||||
(s/keys :req-un [::profile-id ::team-id]))
|
||||
(s/keys :req [::rpc/profile-id]
|
||||
:req-un [::team-id]))
|
||||
|
||||
(sv/defmethod ::get-unread-comment-threads
|
||||
{::doc/added "1.15"}
|
||||
[{:keys [pool] :as cfg} {:keys [profile-id team-id] :as params}]
|
||||
[{:keys [pool] :as cfg} {:keys [::rpc/profile-id team-id] :as params}]
|
||||
(with-open [conn (db/open pool)]
|
||||
(teams/check-read-permissions! conn profile-id team-id)
|
||||
(retrieve-unread-comment-threads conn params)))
|
||||
|
@ -122,7 +124,7 @@
|
|||
"select * from threads where count_unread_comments > 0"))
|
||||
|
||||
(defn retrieve-unread-comment-threads
|
||||
[conn {:keys [profile-id team-id]}]
|
||||
[conn {:keys [::rpc/profile-id team-id]}]
|
||||
(->> (db/exec! conn [sql:unread-comment-threads-by-team profile-id team-id])
|
||||
(into [] (map decode-row))))
|
||||
|
||||
|
@ -132,12 +134,13 @@
|
|||
(s/def ::id ::us/uuid)
|
||||
(s/def ::share-id (s/nilable ::us/uuid))
|
||||
(s/def ::get-comment-thread
|
||||
(s/keys :req-un [::profile-id ::file-id ::id]
|
||||
(s/keys :req [::rpc/profile-id]
|
||||
:req-un [::file-id ::id]
|
||||
:opt-un [::share-id]))
|
||||
|
||||
(sv/defmethod ::get-comment-thread
|
||||
{::doc/added "1.15"}
|
||||
[{:keys [pool] :as cfg} {:keys [profile-id file-id id share-id] :as params}]
|
||||
[{:keys [pool] :as cfg} {:keys [::rpc/profile-id file-id id share-id] :as params}]
|
||||
(with-open [conn (db/open pool)]
|
||||
(files/check-comment-permissions! conn profile-id file-id share-id)
|
||||
(let [sql (str "with threads as (" sql:comment-threads ")"
|
||||
|
@ -146,7 +149,7 @@
|
|||
(decode-row)))))
|
||||
|
||||
(defn get-comment-thread
|
||||
[conn {:keys [profile-id file-id id] :as params}]
|
||||
[conn {:keys [::rpc/profile-id file-id id] :as params}]
|
||||
(let [sql (str "with threads as (" sql:comment-threads ")"
|
||||
"select * from threads where id = ?")]
|
||||
(-> (db/exec-one! conn [sql profile-id file-id id])
|
||||
|
@ -160,12 +163,13 @@
|
|||
(s/def ::share-id (s/nilable ::us/uuid))
|
||||
(s/def ::thread-id ::us/uuid)
|
||||
(s/def ::get-comments
|
||||
(s/keys :req-un [::profile-id ::thread-id]
|
||||
(s/keys :req [::rpc/profile-id]
|
||||
:req-un [::thread-id]
|
||||
:opt-un [::share-id]))
|
||||
|
||||
(sv/defmethod ::get-comments
|
||||
{::doc/added "1.15"}
|
||||
[{:keys [pool] :as cfg} {:keys [profile-id thread-id share-id] :as params}]
|
||||
[{:keys [pool] :as cfg} {:keys [::rpc/profile-id thread-id share-id] :as params}]
|
||||
(with-open [conn (db/open pool)]
|
||||
(let [thread (db/get-by-id conn :comment-thread thread-id)]
|
||||
(files/check-comment-permissions! conn profile-id (:file-id thread) share-id))
|
||||
|
@ -191,7 +195,8 @@
|
|||
(s/def ::share-id (s/nilable ::us/uuid))
|
||||
|
||||
(s/def ::get-profiles-for-file-comments
|
||||
(s/keys :req-un [::profile-id ::file-id]
|
||||
(s/keys :req [::rpc/profile-id]
|
||||
:req-un [::file-id]
|
||||
:opt-un [::share-id]))
|
||||
|
||||
(sv/defmethod ::get-profiles-for-file-comments
|
||||
|
@ -199,7 +204,7 @@
|
|||
participants on comment threads of the file."
|
||||
{::doc/added "1.15"
|
||||
::doc/changes ["1.15" "Imported from queries and renamed."]}
|
||||
[{:keys [pool] :as cfg} {:keys [profile-id file-id share-id]}]
|
||||
[{:keys [pool] :as cfg} {:keys [::rpc/profile-id file-id share-id]}]
|
||||
(with-open [conn (db/open pool)]
|
||||
(files/check-comment-permissions! conn profile-id file-id share-id)
|
||||
(get-file-comments-users conn file-id profile-id)))
|
||||
|
@ -240,19 +245,19 @@
|
|||
(s/def ::page-id ::us/uuid)
|
||||
(s/def ::file-id ::us/uuid)
|
||||
(s/def ::share-id (s/nilable ::us/uuid))
|
||||
(s/def ::profile-id ::us/uuid)
|
||||
(s/def ::position ::gpt/point)
|
||||
(s/def ::content ::us/string)
|
||||
(s/def ::frame-id ::us/uuid)
|
||||
|
||||
(s/def ::create-comment-thread
|
||||
(s/keys :req-un [::profile-id ::file-id ::position ::content ::page-id ::frame-id]
|
||||
(s/keys :req [::rpc/profile-id]
|
||||
:req-un [::file-id ::position ::content ::page-id ::frame-id]
|
||||
:opt-un [::share-id]))
|
||||
|
||||
(sv/defmethod ::create-comment-thread
|
||||
{::doc/added "1.15"
|
||||
::webhooks/event? true}
|
||||
[{:keys [pool] :as cfg} {:keys [profile-id file-id share-id] :as params}]
|
||||
[{:keys [pool] :as cfg} {:keys [::rpc/profile-id file-id share-id] :as params}]
|
||||
(db/with-atomic [conn pool]
|
||||
(files/check-comment-permissions! conn profile-id file-id share-id)
|
||||
|
||||
|
@ -268,7 +273,7 @@
|
|||
(:next-seqn res)))
|
||||
|
||||
(defn create-comment-thread
|
||||
[conn {:keys [profile-id file-id page-id position content frame-id] :as params}]
|
||||
[conn {:keys [::rpc/profile-id file-id page-id position content frame-id] :as params}]
|
||||
(let [seqn (retrieve-next-seqn conn file-id)
|
||||
now (dt/now)
|
||||
pname (retrieve-page-name conn params)
|
||||
|
@ -316,12 +321,13 @@
|
|||
(s/def ::share-id (s/nilable ::us/uuid))
|
||||
|
||||
(s/def ::update-comment-thread-status
|
||||
(s/keys :req-un [::profile-id ::id]
|
||||
(s/keys :req [::rpc/profile-id]
|
||||
:req-un [::id]
|
||||
:opt-un [::share-id]))
|
||||
|
||||
(sv/defmethod ::update-comment-thread-status
|
||||
{::doc/added "1.15"}
|
||||
[{:keys [pool] :as cfg} {:keys [profile-id id share-id] :as params}]
|
||||
[{:keys [pool] :as cfg} {:keys [::rpc/profile-id id share-id] :as params}]
|
||||
(db/with-atomic [conn pool]
|
||||
(let [cthr (db/get-by-id conn :comment-thread id {:for-update true})]
|
||||
(when-not cthr
|
||||
|
@ -346,12 +352,13 @@
|
|||
|
||||
(s/def ::is-resolved ::us/boolean)
|
||||
(s/def ::update-comment-thread
|
||||
(s/keys :req-un [::profile-id ::id ::is-resolved]
|
||||
(s/keys :req [::rpc/profile-id]
|
||||
:req-un [::id ::is-resolved]
|
||||
:opt-un [::share-id]))
|
||||
|
||||
(sv/defmethod ::update-comment-thread
|
||||
{::doc/added "1.15"}
|
||||
[{:keys [pool] :as cfg} {:keys [profile-id id is-resolved share-id] :as params}]
|
||||
[{:keys [pool] :as cfg} {:keys [::rpc/profile-id id is-resolved share-id] :as params}]
|
||||
(db/with-atomic [conn pool]
|
||||
(let [thread (db/get-by-id conn :comment-thread id {:for-update true})]
|
||||
(when-not thread
|
||||
|
@ -370,7 +377,8 @@
|
|||
(declare create-comment)
|
||||
|
||||
(s/def ::create-comment
|
||||
(s/keys :req-un [::profile-id ::thread-id ::content]
|
||||
(s/keys :req [::rpc/profile-id]
|
||||
:req-un [::thread-id ::content]
|
||||
:opt-un [::share-id]))
|
||||
|
||||
(sv/defmethod ::create-comment
|
||||
|
@ -381,7 +389,7 @@
|
|||
(create-comment conn params)))
|
||||
|
||||
(defn create-comment
|
||||
[conn {:keys [profile-id thread-id content share-id] :as params}]
|
||||
[conn {:keys [::rpc/profile-id thread-id content share-id] :as params}]
|
||||
(let [thread (-> (db/get-by-id conn :comment-thread thread-id {:for-update true})
|
||||
(decode-row))
|
||||
pname (retrieve-page-name conn thread)]
|
||||
|
@ -437,7 +445,8 @@
|
|||
(declare update-comment)
|
||||
|
||||
(s/def ::update-comment
|
||||
(s/keys :req-un [::profile-id ::id ::content]
|
||||
(s/keys :req [::rpc/profile-id]
|
||||
:req-un [::id ::content]
|
||||
:opt-un [::share-id]))
|
||||
|
||||
(sv/defmethod ::update-comment
|
||||
|
@ -447,7 +456,7 @@
|
|||
(update-comment conn params)))
|
||||
|
||||
(defn update-comment
|
||||
[conn {:keys [profile-id id content share-id] :as params}]
|
||||
[conn {:keys [::rpc/profile-id id content share-id] :as params}]
|
||||
(let [comment (db/get-by-id conn :comment id {:for-update true})
|
||||
_ (when-not comment (ex/raise :type :not-found))
|
||||
thread (db/get-by-id conn :comment-thread (:thread-id comment) {:for-update true})
|
||||
|
@ -476,11 +485,12 @@
|
|||
;; --- COMMAND: Delete Comment Thread
|
||||
|
||||
(s/def ::delete-comment-thread
|
||||
(s/keys :req-un [::profile-id ::id]))
|
||||
(s/keys :req [::rpc/profile-id]
|
||||
:req-un [::id]))
|
||||
|
||||
(sv/defmethod ::delete-comment-thread
|
||||
{::doc/added "1.15"}
|
||||
[{:keys [pool] :as cfg} {:keys [profile-id id] :as params}]
|
||||
[{:keys [pool] :as cfg} {:keys [::rpc/profile-id id] :as params}]
|
||||
(db/with-atomic [conn pool]
|
||||
(let [thread (db/get-by-id conn :comment-thread id {:for-update true})]
|
||||
(when-not (= (:owner-id thread) profile-id)
|
||||
|
@ -493,11 +503,12 @@
|
|||
;; --- COMMAND: Delete comment
|
||||
|
||||
(s/def ::delete-comment
|
||||
(s/keys :req-un [::profile-id ::id]))
|
||||
(s/keys :req [::rpc/profile-id]
|
||||
:req-un [::id]))
|
||||
|
||||
(sv/defmethod ::delete-comment
|
||||
{::doc/added "1.15"}
|
||||
[{:keys [pool] :as cfg} {:keys [profile-id id] :as params}]
|
||||
[{:keys [pool] :as cfg} {:keys [::rpc/profile-id id] :as params}]
|
||||
(db/with-atomic [conn pool]
|
||||
(let [comment (db/get-by-id conn :comment id {:for-update true})]
|
||||
(when-not (= (:owner-id comment) profile-id)
|
||||
|
@ -509,12 +520,13 @@
|
|||
;; --- COMMAND: Update comment thread position
|
||||
|
||||
(s/def ::update-comment-thread-position
|
||||
(s/keys :req-un [::profile-id ::id ::position ::frame-id]
|
||||
(s/keys :req [::rpc/profile-id]
|
||||
:req-un [::id ::position ::frame-id]
|
||||
:opt-un [::share-id]))
|
||||
|
||||
(sv/defmethod ::update-comment-thread-position
|
||||
{::doc/added "1.15"}
|
||||
[{:keys [pool] :as cfg} {:keys [profile-id id position frame-id share-id] :as params}]
|
||||
[{:keys [pool] :as cfg} {:keys [::rpc/profile-id id position frame-id share-id] :as params}]
|
||||
(db/with-atomic [conn pool]
|
||||
(let [thread (db/get-by-id conn :comment-thread id {:for-update true})]
|
||||
(files/check-comment-permissions! conn profile-id (:file-id thread) share-id)
|
||||
|
@ -528,12 +540,13 @@
|
|||
;; --- COMMAND: Update comment frame
|
||||
|
||||
(s/def ::update-comment-thread-frame
|
||||
(s/keys :req-un [::profile-id ::id ::frame-id]
|
||||
(s/keys :req [::rpc/profile-id]
|
||||
:req-un [::id ::frame-id]
|
||||
:opt-un [::share-id]))
|
||||
|
||||
(sv/defmethod ::update-comment-thread-frame
|
||||
{::doc/added "1.15"}
|
||||
[{:keys [pool] :as cfg} {:keys [profile-id id frame-id share-id] :as params}]
|
||||
[{:keys [pool] :as cfg} {:keys [::rpc/profile-id id frame-id share-id] :as params}]
|
||||
(db/with-atomic [conn pool]
|
||||
(let [thread (db/get-by-id conn :comment-thread id {:for-update true})]
|
||||
(files/check-comment-permissions! conn profile-id (:file-id thread) share-id)
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
[app.config :as cf]
|
||||
[app.db :as db]
|
||||
[app.loggers.audit :as audit]
|
||||
[app.rpc :as-alias rpc]
|
||||
[app.rpc.commands.auth :as cmd.auth]
|
||||
[app.rpc.doc :as-alias doc]
|
||||
[app.util.services :as sv]
|
||||
|
@ -26,7 +27,7 @@
|
|||
"A command that is responsible of creating a demo purpose
|
||||
profile. It only works if the `demo-users` flag is enabled in the
|
||||
configuration."
|
||||
{:auth false
|
||||
{::rpc/auth false
|
||||
::doc/added "1.15"
|
||||
::doc/changes ["1.15" "This method is migrated from mutations to commands."]}
|
||||
[{:keys [pool] :as cfg} _]
|
||||
|
|
|
@ -17,7 +17,9 @@
|
|||
[app.common.types.shape-tree :as ctt]
|
||||
[app.db :as db]
|
||||
[app.db.sql :as sql]
|
||||
[app.loggers.audit :as-alias audit]
|
||||
[app.loggers.webhooks :as-alias webhooks]
|
||||
[app.rpc :as-alias rpc]
|
||||
[app.rpc.commands.files.thumbnails :as-alias thumbs]
|
||||
[app.rpc.commands.teams :as teams]
|
||||
[app.rpc.cond :as-alias cond]
|
||||
|
@ -51,7 +53,6 @@
|
|||
(s/def ::id ::us/uuid)
|
||||
(s/def ::is-shared ::us/boolean)
|
||||
(s/def ::name ::us/string)
|
||||
(s/def ::profile-id ::us/uuid)
|
||||
(s/def ::project-id ::us/uuid)
|
||||
(s/def ::search-term ::us/string)
|
||||
(s/def ::team-id ::us/uuid)
|
||||
|
@ -256,7 +257,8 @@
|
|||
(str (dt/format-instant modified-at :iso) "-" revn))
|
||||
|
||||
(s/def ::get-file
|
||||
(s/keys :req-un [::profile-id ::id]
|
||||
(s/keys :req [::rpc/profile-id]
|
||||
:req-un [::id]
|
||||
:opt-un [::features]))
|
||||
|
||||
(sv/defmethod ::get-file
|
||||
|
@ -264,7 +266,7 @@
|
|||
{::doc/added "1.17"
|
||||
::cond/get-object #(get-minimal-file %1 (:id %2))
|
||||
::cond/key-fn get-file-etag}
|
||||
[{:keys [pool] :as cfg} {:keys [profile-id id features] :as params}]
|
||||
[{:keys [pool] :as cfg} {:keys [::rpc/profile-id id features] :as params}]
|
||||
(with-open [conn (db/open pool)]
|
||||
(let [perms (get-permissions conn profile-id id)]
|
||||
(check-read-permissions! perms)
|
||||
|
@ -285,13 +287,14 @@
|
|||
|
||||
(s/def ::get-file-fragment
|
||||
(s/keys :req-un [::file-id ::fragment-id]
|
||||
:opt-un [::share-id ::profile-id]))
|
||||
:opt [::rpc/profile-id]
|
||||
:opt-un [::share-id]))
|
||||
|
||||
(sv/defmethod ::get-file-fragment
|
||||
"Retrieve a file by its ID. Only authenticated users."
|
||||
{::doc/added "1.17"
|
||||
:auth false}
|
||||
[{:keys [pool] :as cfg} {:keys [profile-id file-id fragment-id share-id] :as params}]
|
||||
::rpc/:auth false}
|
||||
[{:keys [pool] :as cfg} {:keys [::rpc/profile-id file-id fragment-id share-id] :as params}]
|
||||
(with-open [conn (db/open pool)]
|
||||
(let [perms (get-permissions conn profile-id file-id share-id)]
|
||||
(check-read-permissions! perms)
|
||||
|
@ -319,7 +322,7 @@
|
|||
(d/index-by :object-id :data)))))
|
||||
|
||||
(s/def ::get-file-object-thumbnails
|
||||
(s/keys :req-un [::profile-id ::file-id]))
|
||||
(s/keys :req [::rpc/profile-id] :req-un [::file-id]))
|
||||
|
||||
(sv/defmethod ::get-file-object-thumbnails
|
||||
"Retrieve a file object thumbnails."
|
||||
|
@ -327,7 +330,7 @@
|
|||
::cond/get-object #(get-minimal-file %1 (:file-id %2))
|
||||
::cond/reuse-key? true
|
||||
::cond/key-fn get-file-etag}
|
||||
[{:keys [pool] :as cfg} {:keys [profile-id file-id] :as params}]
|
||||
[{:keys [pool] :as cfg} {:keys [::rpc/profile-id file-id] :as params}]
|
||||
(with-open [conn (db/open pool)]
|
||||
(check-read-permissions! conn profile-id file-id)
|
||||
(get-object-thumbnails conn file-id)))
|
||||
|
@ -349,7 +352,7 @@
|
|||
order by f.modified_at desc")
|
||||
|
||||
(s/def ::get-project-files
|
||||
(s/keys :req-un [::profile-id ::project-id]))
|
||||
(s/keys :req [::rpc/profile-id] :req-un [::project-id]))
|
||||
|
||||
(defn get-project-files
|
||||
[conn project-id]
|
||||
|
@ -358,7 +361,7 @@
|
|||
(sv/defmethod ::get-project-files
|
||||
"Get all files for the specified project."
|
||||
{::doc/added "1.17"}
|
||||
[{:keys [pool] :as cfg} {:keys [profile-id project-id] :as params}]
|
||||
[{:keys [pool] :as cfg} {:keys [::rpc/profile-id project-id] :as params}]
|
||||
(with-open [conn (db/open pool)]
|
||||
(projects/check-read-permissions! conn profile-id project-id)
|
||||
(get-project-files conn project-id)))
|
||||
|
@ -369,15 +372,14 @@
|
|||
(declare get-has-file-libraries)
|
||||
|
||||
(s/def ::file-id ::us/uuid)
|
||||
(s/def ::profile-id ::us/uuid)
|
||||
|
||||
(s/def ::has-file-libraries
|
||||
(s/keys :req-un [::profile-id ::file-id]))
|
||||
(s/keys :req [::rpc/profile-id] :req-un [::file-id]))
|
||||
|
||||
(sv/defmethod ::has-file-libraries
|
||||
"Checks if the file has libraries. Returns a boolean"
|
||||
{::doc/added "1.15.1"}
|
||||
[{:keys [pool] :as cfg} {:keys [profile-id file-id] :as params}]
|
||||
[{:keys [pool] :as cfg} {:keys [::rpc/profile-id file-id] :as params}]
|
||||
(with-open [conn (db/open pool)]
|
||||
(check-read-permissions! pool profile-id file-id)
|
||||
(get-has-file-libraries conn params)))
|
||||
|
@ -425,7 +427,8 @@
|
|||
(s/def ::object-id ::us/uuid)
|
||||
(s/def ::get-page
|
||||
(s/and
|
||||
(s/keys :req-un [::profile-id ::file-id]
|
||||
(s/keys :req [::rpc/profile-id]
|
||||
:req-un [::file-id]
|
||||
:opt-un [::page-id ::object-id ::features])
|
||||
(fn [obj]
|
||||
(if (contains? obj :object-id)
|
||||
|
@ -443,7 +446,7 @@
|
|||
|
||||
Mainly used for rendering purposes."
|
||||
{::doc/added "1.17"}
|
||||
[{:keys [pool] :as cfg} {:keys [profile-id file-id] :as params}]
|
||||
[{:keys [pool] :as cfg} {:keys [::rpc/profile-id file-id] :as params}]
|
||||
(with-open [conn (db/open pool)]
|
||||
(check-read-permissions! conn profile-id file-id)
|
||||
(get-page conn params)))
|
||||
|
@ -492,7 +495,7 @@
|
|||
(into #{} xform (db/exec! conn [sql:team-shared-files team-id]))))
|
||||
|
||||
(s/def ::get-team-shared-files
|
||||
(s/keys :req-un [::profile-id ::team-id]))
|
||||
(s/keys :req [::rpc/profile-id] :req-un [::team-id]))
|
||||
|
||||
(sv/defmethod ::get-team-shared-files
|
||||
"Get all file (libraries) for the specified team."
|
||||
|
@ -541,13 +544,14 @@
|
|||
(handle-file-features client-features)))))))
|
||||
|
||||
(s/def ::get-file-libraries
|
||||
(s/keys :req-un [::profile-id ::file-id]
|
||||
(s/keys :req [::rpc/profile-id]
|
||||
:req-un [::file-id]
|
||||
:opt-un [::features]))
|
||||
|
||||
(sv/defmethod ::get-file-libraries
|
||||
"Get libraries used by the specified file."
|
||||
{::doc/added "1.17"}
|
||||
[{:keys [pool] :as cfg} {:keys [profile-id file-id features] :as params}]
|
||||
[{:keys [pool] :as cfg} {:keys [::rpc/profile-id file-id features] :as params}]
|
||||
(with-open [conn (db/open pool)]
|
||||
(check-read-permissions! conn profile-id file-id)
|
||||
(get-file-libraries conn file-id features)))
|
||||
|
@ -568,12 +572,12 @@
|
|||
(db/exec! conn [sql:library-using-files file-id]))
|
||||
|
||||
(s/def ::get-library-file-references
|
||||
(s/keys :req-un [::profile-id ::file-id]))
|
||||
(s/keys :req [::rpc/profile-id] :req-un [::file-id]))
|
||||
|
||||
(sv/defmethod ::get-library-file-references
|
||||
"Returns all the file references that use specified file (library) id."
|
||||
{::doc/added "1.17"}
|
||||
[{:keys [pool] :as cfg} {:keys [profile-id file-id] :as params}]
|
||||
[{:keys [pool] :as cfg} {:keys [::rpc/profile-id file-id] :as params}]
|
||||
(with-open [conn (db/open pool)]
|
||||
(check-read-permissions! conn profile-id file-id)
|
||||
(get-library-file-references conn file-id)))
|
||||
|
@ -606,11 +610,12 @@
|
|||
(db/exec! conn [sql:team-recent-files team-id]))
|
||||
|
||||
(s/def ::get-team-recent-files
|
||||
(s/keys :req-un [::profile-id ::team-id]))
|
||||
(s/keys :req [::rpc/profile-id]
|
||||
:req-un [::team-id]))
|
||||
|
||||
(sv/defmethod ::get-team-recent-files
|
||||
{::doc/added "1.17"}
|
||||
[{:keys [pool] :as cfg} {:keys [profile-id team-id]}]
|
||||
[{:keys [pool] :as cfg} {:keys [::rpc/profile-id team-id]}]
|
||||
(with-open [conn (db/open pool)]
|
||||
(teams/check-read-permissions! conn profile-id team-id)
|
||||
(get-team-recent-files conn team-id)))
|
||||
|
@ -638,12 +643,13 @@
|
|||
(s/def ::revn ::us/integer)
|
||||
|
||||
(s/def ::get-file-thumbnail
|
||||
(s/keys :req-un [::profile-id ::file-id]
|
||||
(s/keys :req [::rpc/profile-id]
|
||||
:req-un [::file-id]
|
||||
:opt-un [::revn]))
|
||||
|
||||
(sv/defmethod ::get-file-thumbnail
|
||||
{::doc/added "1.17"}
|
||||
[{:keys [pool]} {:keys [profile-id file-id revn]}]
|
||||
[{:keys [pool]} {:keys [::rpc/profile-id file-id revn]}]
|
||||
(with-open [conn (db/open pool)]
|
||||
(check-read-permissions! conn profile-id file-id)
|
||||
(-> (get-file-thumbnail conn file-id revn)
|
||||
|
@ -729,14 +735,15 @@
|
|||
(update :objects assoc-thumbnails page-id thumbs)))))
|
||||
|
||||
(s/def ::get-file-data-for-thumbnail
|
||||
(s/keys :req-un [::profile-id ::file-id]
|
||||
(s/keys :req [::rpc/profile-id]
|
||||
:req-un [::file-id]
|
||||
:opt-un [::features]))
|
||||
|
||||
(sv/defmethod ::get-file-data-for-thumbnail
|
||||
"Retrieves the data for generate the thumbnail of the file. Used
|
||||
mainly for render thumbnails on dashboard."
|
||||
{::doc/added "1.17"}
|
||||
[{:keys [pool] :as cfg} {:keys [profile-id file-id features] :as props}]
|
||||
[{:keys [pool] :as cfg} {:keys [::rpc/profile-id file-id features] :as props}]
|
||||
(with-open [conn (db/open pool)]
|
||||
(check-read-permissions! conn profile-id file-id)
|
||||
(let [file (get-file conn file-id features)]
|
||||
|
@ -753,23 +760,27 @@
|
|||
|
||||
(defn rename-file
|
||||
[conn {:keys [id name] :as params}]
|
||||
(-> (db/update! conn :file
|
||||
{:name name
|
||||
:modified-at (dt/now)}
|
||||
{:id id})
|
||||
(select-keys [:id :name :created-at :modified-at])))
|
||||
(db/update! conn :file
|
||||
{:name name
|
||||
:modified-at (dt/now)}
|
||||
{:id id}))
|
||||
|
||||
(s/def ::rename-file
|
||||
(s/keys :req-un [::profile-id ::name ::id]))
|
||||
(s/keys :req [::rpc/profile-id]
|
||||
:req-un [::name ::id]))
|
||||
|
||||
(sv/defmethod ::rename-file
|
||||
{::doc/added "1.17"
|
||||
::webhooks/event? true}
|
||||
[{:keys [pool] :as cfg} {:keys [id profile-id] :as params}]
|
||||
[{:keys [pool] :as cfg} {:keys [::rpc/profile-id id] :as params}]
|
||||
(db/with-atomic [conn pool]
|
||||
(check-edition-permissions! conn profile-id id)
|
||||
(rename-file conn params)))
|
||||
|
||||
(let [file (rename-file conn params)]
|
||||
(rph/with-meta
|
||||
(select-keys file [:id :name :created-at :modified-at])
|
||||
{::audit/props {:project-id (:project-id file)
|
||||
:created-at (:created-at file)
|
||||
:modified-at (:modified-at file)}}))))
|
||||
|
||||
;; --- MUTATION COMMAND: set-file-shared
|
||||
|
||||
|
@ -779,10 +790,9 @@
|
|||
|
||||
(defn set-file-shared
|
||||
[conn {:keys [id is-shared] :as params}]
|
||||
(-> (db/update! conn :file
|
||||
{:is-shared is-shared}
|
||||
{:id id})
|
||||
(select-keys [:id :name :is-shared])))
|
||||
(db/update! conn :file
|
||||
{:is-shared is-shared}
|
||||
{:id id}))
|
||||
|
||||
(defn absorb-library
|
||||
"Find all files using a shared library, and absorb all library assets
|
||||
|
@ -805,19 +815,25 @@
|
|||
{:id id})))))))))
|
||||
|
||||
(s/def ::set-file-shared
|
||||
(s/keys :req-un [::profile-id ::id ::is-shared]))
|
||||
(s/keys :req [::rpc/profile-id]
|
||||
:req-un [::id ::is-shared]))
|
||||
|
||||
(sv/defmethod ::set-file-shared
|
||||
{::doc/added "1.17"
|
||||
::webhooks/event? true}
|
||||
[{:keys [pool] :as cfg} {:keys [id profile-id is-shared] :as params}]
|
||||
[{:keys [pool] :as cfg} {:keys [::rpc/profile-id id is-shared] :as params}]
|
||||
(db/with-atomic [conn pool]
|
||||
(check-edition-permissions! conn profile-id id)
|
||||
(when-not is-shared
|
||||
(absorb-library conn params)
|
||||
(unlink-files conn params))
|
||||
(set-file-shared conn params)))
|
||||
|
||||
(let [file (set-file-shared conn params)]
|
||||
(rph/with-meta
|
||||
(select-keys file [:id :name :is-shared])
|
||||
{::audit/props {:name (:name file)
|
||||
:project-id (:project-id file)
|
||||
:is-shared (:is-shared file)}}))))
|
||||
|
||||
;; --- MUTATION COMMAND: delete-file
|
||||
|
||||
|
@ -825,20 +841,26 @@
|
|||
[conn {:keys [id] :as params}]
|
||||
(db/update! conn :file
|
||||
{:deleted-at (dt/now)}
|
||||
{:id id})
|
||||
nil)
|
||||
{:id id}))
|
||||
|
||||
(s/def ::delete-file
|
||||
(s/keys :req-un [::id ::profile-id]))
|
||||
(s/keys :req [::rpc/profile-id]
|
||||
:req-un [::id]))
|
||||
|
||||
(sv/defmethod ::delete-file
|
||||
{::doc/added "1.17"
|
||||
::webhooks/event? true}
|
||||
[{:keys [pool] :as cfg} {:keys [id profile-id] :as params}]
|
||||
[{:keys [pool] :as cfg} {:keys [::rpc/profile-id id] :as params}]
|
||||
(db/with-atomic [conn pool]
|
||||
(check-edition-permissions! conn profile-id id)
|
||||
(absorb-library conn params)
|
||||
(mark-file-deleted conn params)))
|
||||
(let [file (mark-file-deleted conn params)]
|
||||
|
||||
(rph/with-meta (rph/wrap)
|
||||
{::audit/props {:project-id (:project-id file)
|
||||
:name (:name file)
|
||||
:created-at (:created-at file)
|
||||
:modified-at (:modified-at file)}}))))
|
||||
|
||||
;; --- MUTATION COMMAND: link-file-to-library
|
||||
|
||||
|
@ -852,12 +874,13 @@
|
|||
(db/exec-one! conn [sql:link-file-to-library file-id library-id]))
|
||||
|
||||
(s/def ::link-file-to-library
|
||||
(s/keys :req-un [::profile-id ::file-id ::library-id]))
|
||||
(s/keys :req [::rpc/profile-id]
|
||||
:req-un [::file-id ::library-id]))
|
||||
|
||||
(sv/defmethod ::link-file-to-library
|
||||
{::doc/added "1.17"
|
||||
::webhooks/event? true}
|
||||
[{:keys [pool] :as cfg} {:keys [profile-id file-id library-id] :as params}]
|
||||
[{:keys [pool] :as cfg} {:keys [::rpc/profile-id file-id library-id] :as params}]
|
||||
(when (= file-id library-id)
|
||||
(ex/raise :type :validation
|
||||
:code :invalid-library
|
||||
|
@ -876,12 +899,13 @@
|
|||
:library-file-id library-id}))
|
||||
|
||||
(s/def ::unlink-file-from-library
|
||||
(s/keys :req-un [::profile-id ::file-id ::library-id]))
|
||||
(s/keys :req [::rpc/profile-id]
|
||||
:req-un [::file-id ::library-id]))
|
||||
|
||||
(sv/defmethod ::unlink-file-from-library
|
||||
{::doc/added "1.17"
|
||||
::webhooks/event? true}
|
||||
[{:keys [pool] :as cfg} {:keys [profile-id file-id] :as params}]
|
||||
[{:keys [pool] :as cfg} {:keys [::rpc/profile-id file-id] :as params}]
|
||||
(db/with-atomic [conn pool]
|
||||
(check-edition-permissions! conn profile-id file-id)
|
||||
(unlink-file-from-library conn params)))
|
||||
|
@ -897,14 +921,15 @@
|
|||
:library-file-id library-id}))
|
||||
|
||||
(s/def ::update-file-library-sync-status
|
||||
(s/keys :req-un [::profile-id ::file-id ::library-id]))
|
||||
(s/keys :req [::rpc/profile-id]
|
||||
:req-un [::file-id ::library-id]))
|
||||
|
||||
;; TODO: improve naming
|
||||
|
||||
(sv/defmethod ::update-file-library-sync-status
|
||||
"Update the synchronization statos of a file->library link"
|
||||
{::doc/added "1.17"}
|
||||
[{:keys [pool] :as cfg} {:keys [profile-id file-id] :as params}]
|
||||
[{:keys [pool] :as cfg} {:keys [::rpc/profile-id file-id] :as params}]
|
||||
(db/with-atomic [conn pool]
|
||||
(check-edition-permissions! conn profile-id file-id)
|
||||
(update-sync conn params)))
|
||||
|
@ -919,13 +944,14 @@
|
|||
{:id file-id}))
|
||||
|
||||
(s/def ::ignore-file-library-sync-status
|
||||
(s/keys :req-un [::profile-id ::file-id ::date]))
|
||||
(s/keys :req [::rpc/profile-id]
|
||||
:req-un [::file-id ::date]))
|
||||
|
||||
;; TODO: improve naming
|
||||
(sv/defmethod ::ignore-file-library-sync-status
|
||||
"Ignore updates in linked files"
|
||||
{::doc/added "1.17"}
|
||||
[{:keys [pool] :as cfg} {:keys [profile-id file-id] :as params}]
|
||||
[{:keys [pool] :as cfg} {:keys [::rpc/profile-id file-id] :as params}]
|
||||
(db/with-atomic [conn pool]
|
||||
(check-edition-permissions! conn profile-id file-id)
|
||||
(ignore-sync conn params)))
|
||||
|
@ -948,11 +974,13 @@
|
|||
(s/def ::data (s/nilable ::us/string))
|
||||
(s/def ::thumbs/object-id ::us/string)
|
||||
(s/def ::upsert-file-object-thumbnail
|
||||
(s/keys :req-un [::profile-id ::file-id ::thumbs/object-id ::data]))
|
||||
(s/keys :req [::rpc/profile-id]
|
||||
:req-un [::file-id ::thumbs/object-id]
|
||||
:opt-un [::data]))
|
||||
|
||||
(sv/defmethod ::upsert-file-object-thumbnail
|
||||
{::doc/added "1.17"}
|
||||
[{:keys [pool] :as cfg} {:keys [profile-id file-id] :as params}]
|
||||
[{:keys [pool] :as cfg} {:keys [::rpc/profile-id file-id] :as params}]
|
||||
(db/with-atomic [conn pool]
|
||||
(check-edition-permissions! conn profile-id file-id)
|
||||
(upsert-file-object-thumbnail! conn params)
|
||||
|
@ -975,13 +1003,14 @@
|
|||
(s/def ::revn ::us/integer)
|
||||
(s/def ::props map?)
|
||||
(s/def ::upsert-file-thumbnail
|
||||
(s/keys :req-un [::profile-id ::file-id ::revn ::data ::props]))
|
||||
(s/keys :req [::rpc/profile-id]
|
||||
:req-un [::file-id ::revn ::data ::props]))
|
||||
|
||||
(sv/defmethod ::upsert-file-thumbnail
|
||||
"Creates or updates the file thumbnail. Mainly used for paint the
|
||||
grid thumbnails."
|
||||
{::doc/added "1.17"}
|
||||
[{:keys [pool] :as cfg} {:keys [profile-id file-id] :as params}]
|
||||
[{:keys [pool] :as cfg} {:keys [::rpc/profile-id file-id] :as params}]
|
||||
(db/with-atomic [conn pool]
|
||||
(check-edition-permissions! conn profile-id file-id)
|
||||
(upsert-file-thumbnail conn params)
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
[app.db :as db]
|
||||
[app.loggers.audit :as-alias audit]
|
||||
[app.loggers.webhooks :as-alias webhooks]
|
||||
[app.rpc :as-alias rpc]
|
||||
[app.rpc.commands.files :as files]
|
||||
[app.rpc.doc :as-alias doc]
|
||||
[app.rpc.permissions :as perms]
|
||||
|
@ -68,8 +69,8 @@
|
|||
(files/decode-row file)))
|
||||
|
||||
(s/def ::create-file
|
||||
(s/keys :req-un [::files/profile-id
|
||||
::files/name
|
||||
(s/keys :req [::rpc/profile-id]
|
||||
:req-un [::files/name
|
||||
::files/project-id]
|
||||
:opt-un [::files/id
|
||||
::files/is-shared
|
||||
|
@ -78,10 +79,11 @@
|
|||
(sv/defmethod ::create-file
|
||||
{::doc/added "1.17"
|
||||
::webhooks/event? true}
|
||||
[{:keys [pool] :as cfg} {:keys [profile-id project-id] :as params}]
|
||||
[{:keys [pool] :as cfg} {:keys [::rpc/profile-id project-id] :as params}]
|
||||
(db/with-atomic [conn pool]
|
||||
(proj/check-edition-permissions! conn profile-id project-id)
|
||||
(let [team-id (files/get-team-id conn project-id)]
|
||||
(let [team-id (files/get-team-id conn project-id)
|
||||
params (assoc params :profile-id profile-id)]
|
||||
(-> (create-file conn params)
|
||||
(vary-meta assoc ::audit/props {:team-id team-id})))))
|
||||
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
[app.common.spec :as us]
|
||||
[app.common.uuid :as uuid]
|
||||
[app.db :as db]
|
||||
[app.rpc :as-alias rpc]
|
||||
[app.rpc.commands.files :as files]
|
||||
[app.rpc.commands.files.create :as files.create]
|
||||
[app.rpc.commands.files.update :as files.update]
|
||||
|
@ -26,8 +27,8 @@
|
|||
(s/def ::create-page ::us/boolean)
|
||||
|
||||
(s/def ::create-temp-file
|
||||
(s/keys :req-un [::files/profile-id
|
||||
::files/name
|
||||
(s/keys :req [::rpc/profile-id]
|
||||
:req-un [::files/name
|
||||
::files/project-id]
|
||||
:opt-un [::files/id
|
||||
::files/is-shared
|
||||
|
@ -36,7 +37,7 @@
|
|||
|
||||
(sv/defmethod ::create-temp-file
|
||||
{::doc/added "1.17"}
|
||||
[{:keys [pool] :as cfg} {:keys [profile-id project-id] :as params}]
|
||||
[{:keys [pool] :as cfg} {:keys [::rpc/profile-id project-id] :as params}]
|
||||
(db/with-atomic [conn pool]
|
||||
(proj/check-edition-permissions! conn profile-id project-id)
|
||||
(files.create/create-file conn (assoc params :deleted-at (dt/in-future {:days 1})))))
|
||||
|
@ -44,7 +45,7 @@
|
|||
;; --- MUTATION COMMAND: update-temp-file
|
||||
|
||||
(defn update-temp-file
|
||||
[conn {:keys [profile-id session-id id revn changes] :as params}]
|
||||
[conn {:keys [::rpc/profile-id session-id id revn changes] :as params}]
|
||||
(db/insert! conn :file-change
|
||||
{:id (uuid/next)
|
||||
:session-id session-id
|
||||
|
@ -95,12 +96,12 @@
|
|||
nil))
|
||||
|
||||
(s/def ::persist-temp-file
|
||||
(s/keys :req-un [::files/id
|
||||
::files/profile-id]))
|
||||
(s/keys :req [::rpc/profile-id]
|
||||
:req-un [::files/id]))
|
||||
|
||||
(sv/defmethod ::persist-temp-file
|
||||
{::doc/added "1.17"}
|
||||
[{:keys [pool] :as cfg} {:keys [id profile-id] :as params}]
|
||||
[{:keys [pool] :as cfg} {:keys [::rpc/profile-id id] :as params}]
|
||||
(db/with-atomic [conn pool]
|
||||
(files/check-edition-permissions! conn profile-id id)
|
||||
(persist-temp-file conn params)))
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
[app.loggers.webhooks :as-alias webhooks]
|
||||
[app.metrics :as mtx]
|
||||
[app.msgbus :as mbus]
|
||||
[app.rpc :as-alias rpc]
|
||||
[app.rpc.climit :as-alias climit]
|
||||
[app.rpc.commands.files :as files]
|
||||
[app.rpc.doc :as-alias doc]
|
||||
|
@ -52,7 +53,8 @@
|
|||
(s/def ::revn ::us/integer)
|
||||
(s/def ::update-file
|
||||
(s/and
|
||||
(s/keys :req-un [::files/id ::files/profile-id ::session-id ::revn]
|
||||
(s/keys :req [::rpc/profile-id]
|
||||
:req-un [::files/id ::session-id ::revn]
|
||||
:opt-un [::changes ::changes-with-metadata ::features])
|
||||
(fn [o]
|
||||
(or (contains? o :changes)
|
||||
|
@ -130,19 +132,20 @@
|
|||
::webhooks/batch-timeout (dt/duration "2m")
|
||||
::webhooks/batch-key :id
|
||||
::doc/added "1.17"}
|
||||
[{:keys [pool] :as cfg} {:keys [id profile-id] :as params}]
|
||||
[{:keys [pool] :as cfg} {:keys [::rpc/profile-id id] :as params}]
|
||||
(db/with-atomic [conn pool]
|
||||
(files/check-edition-permissions! conn profile-id id)
|
||||
(db/xact-lock! conn id)
|
||||
|
||||
(let [cfg (assoc cfg :conn conn)
|
||||
params (assoc params :profile-id profile-id)
|
||||
tpoint (dt/tpoint)]
|
||||
(-> (update-file cfg params)
|
||||
(rph/with-defer #(let [elapsed (tpoint)]
|
||||
(l/trace :hint "update-file" :time (dt/format-duration elapsed))))))))
|
||||
|
||||
(defn update-file
|
||||
[{:keys [conn metrics] :as cfg} {:keys [id profile-id changes changes-with-metadata] :as params}]
|
||||
[{:keys [conn metrics] :as cfg} {:keys [profile-id id changes changes-with-metadata] :as params}]
|
||||
(let [file (get-file conn id)
|
||||
features (->> (concat (:features file)
|
||||
(:features params))
|
||||
|
@ -184,7 +187,7 @@
|
|||
:team-id (:team-id file)}))))))
|
||||
|
||||
(defn- update-file*
|
||||
[{:keys [conn] :as cfg} {:keys [file changes session-id profile-id] :as params}]
|
||||
[{:keys [conn] :as cfg} {:keys [profile-id file changes session-id] :as params}]
|
||||
(when (> (:revn params)
|
||||
(:revn file))
|
||||
(ex/raise :type :validation
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
[app.common.uuid :as uuid]
|
||||
[app.db :as db]
|
||||
[app.loggers.webhooks :as-alias webhooks]
|
||||
[app.rpc :as-alias rpc]
|
||||
[app.rpc.commands.binfile :as binfile]
|
||||
[app.rpc.commands.files :as files]
|
||||
[app.rpc.commands.teams :as teams :refer [create-project-role create-project]]
|
||||
|
@ -31,14 +32,14 @@
|
|||
(declare duplicate-file)
|
||||
|
||||
(s/def ::id ::us/uuid)
|
||||
(s/def ::profile-id ::us/uuid)
|
||||
(s/def ::project-id ::us/uuid)
|
||||
(s/def ::file-id ::us/uuid)
|
||||
(s/def ::team-id ::us/uuid)
|
||||
(s/def ::name ::us/string)
|
||||
|
||||
(s/def ::duplicate-file
|
||||
(s/keys :req-un [::profile-id ::file-id]
|
||||
(s/keys :req [::rpc/profile-id]
|
||||
:req-un [::file-id]
|
||||
:opt-un [::name]))
|
||||
|
||||
(sv/defmethod ::duplicate-file
|
||||
|
@ -47,7 +48,7 @@
|
|||
::webhooks/event? true}
|
||||
[{:keys [pool] :as cfg} params]
|
||||
(db/with-atomic [conn pool]
|
||||
(duplicate-file conn params)))
|
||||
(duplicate-file conn (assoc params :profile-id (::rpc/profile-id params)))))
|
||||
|
||||
(defn- remap-id
|
||||
[item index key]
|
||||
|
@ -212,7 +213,8 @@
|
|||
(declare duplicate-project)
|
||||
|
||||
(s/def ::duplicate-project
|
||||
(s/keys :req-un [::profile-id ::project-id]
|
||||
(s/keys :req [::rpc/profile-id]
|
||||
:req-un [::project-id]
|
||||
:opt-un [::name]))
|
||||
|
||||
(sv/defmethod ::duplicate-project
|
||||
|
@ -221,7 +223,7 @@
|
|||
::webhooks/event? true}
|
||||
[{:keys [pool] :as cfg} params]
|
||||
(db/with-atomic [conn pool]
|
||||
(duplicate-project conn params)))
|
||||
(duplicate-project conn (assoc params :profile-id (::rpc/profile-id params)))))
|
||||
|
||||
(defn duplicate-project
|
||||
[conn {:keys [profile-id project-id name] :as params}]
|
||||
|
@ -249,9 +251,7 @@
|
|||
;; create the duplicated project and assign the current profile as
|
||||
;; a project owner
|
||||
(create-project conn project)
|
||||
(create-project-role conn {:project-id (:id project)
|
||||
:profile-id profile-id
|
||||
:role :owner})
|
||||
(create-project-role conn profile-id (:id project) :owner)
|
||||
|
||||
;; duplicate all files
|
||||
(let [index (reduce #(assoc %1 (:id %2) (uuid/next)) {} files)
|
||||
|
@ -322,7 +322,8 @@
|
|||
|
||||
(s/def ::ids (s/every ::us/uuid :kind set?))
|
||||
(s/def ::move-files
|
||||
(s/keys :req-un [::profile-id ::ids ::project-id]))
|
||||
(s/keys :req [::rpc/profile-id]
|
||||
:req-un [::ids ::project-id]))
|
||||
|
||||
(sv/defmethod ::move-files
|
||||
"Move a set of files from one project to other."
|
||||
|
@ -330,7 +331,7 @@
|
|||
::webhooks/event? true}
|
||||
[{:keys [pool] :as cfg} params]
|
||||
(db/with-atomic [conn pool]
|
||||
(move-files conn params)))
|
||||
(move-files conn (assoc params :profile-id (::rpc/profile-id params)))))
|
||||
|
||||
|
||||
;; --- COMMAND: Move project
|
||||
|
@ -362,7 +363,8 @@
|
|||
|
||||
|
||||
(s/def ::move-project
|
||||
(s/keys :req-un [::profile-id ::team-id ::project-id]))
|
||||
(s/keys :req [::rpc/profile-id]
|
||||
:req-un [::team-id ::project-id]))
|
||||
|
||||
(sv/defmethod ::move-project
|
||||
"Move projects between teams."
|
||||
|
@ -370,7 +372,7 @@
|
|||
::webhooks/event? true}
|
||||
[{:keys [pool] :as cfg} params]
|
||||
(db/with-atomic [conn pool]
|
||||
(move-project conn params)))
|
||||
(move-project conn (assoc params :profile-id (::rpc/profile-id params)))))
|
||||
|
||||
;; --- COMMAND: Clone Template
|
||||
|
||||
|
@ -378,7 +380,8 @@
|
|||
|
||||
(s/def ::template-id ::us/not-empty-string)
|
||||
(s/def ::clone-template
|
||||
(s/keys :req-un [::profile-id ::project-id ::template-id]))
|
||||
(s/keys :req [::rpc/profile-id]
|
||||
:req-un [::project-id ::template-id]))
|
||||
|
||||
(sv/defmethod ::clone-template
|
||||
"Clone into the specified project the template by its id."
|
||||
|
@ -387,7 +390,7 @@
|
|||
[{:keys [pool] :as cfg} params]
|
||||
(db/with-atomic [conn pool]
|
||||
(-> (assoc cfg :conn conn)
|
||||
(clone-template params))))
|
||||
(clone-template (assoc params :profile-id (::rpc/profile-id params))))))
|
||||
|
||||
(defn- clone-template
|
||||
[{:keys [conn templates] :as cfg} {:keys [profile-id template-id project-id]}]
|
||||
|
|
75
backend/src/app/rpc/commands/profile.clj
Normal file
75
backend/src/app/rpc/commands/profile.clj
Normal file
|
@ -0,0 +1,75 @@
|
|||
;; 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) KALEIDOS INC
|
||||
|
||||
(ns app.rpc.commands.profile
|
||||
(:require
|
||||
[app.auth :as auth]
|
||||
[app.common.exceptions :as ex]
|
||||
[app.common.spec :as us]
|
||||
[app.config :as cf]
|
||||
[app.db :as db]
|
||||
[app.rpc :as-alias rpc]
|
||||
[app.rpc.climit :as-alias climit]
|
||||
[app.rpc.doc :as-alias doc]
|
||||
[app.util.services :as sv]
|
||||
[clojure.spec.alpha :as s]))
|
||||
|
||||
;; --- MUTATION: Set profile password
|
||||
|
||||
(declare update-profile-password!)
|
||||
|
||||
(s/def ::profile-id ::us/uuid)
|
||||
(s/def ::password ::us/not-empty-string)
|
||||
|
||||
(s/def ::get-derived-password
|
||||
(s/keys :req [::rpc/profile-id]
|
||||
:req-un [::password]))
|
||||
|
||||
(sv/defmethod ::get-derived-password
|
||||
"Get derived password, only ADMINS allowed to call this RPC
|
||||
methods. Designed for administration pannel integration."
|
||||
{::climit/queue :auth
|
||||
::climit/key-fn ::rpc/profile-id
|
||||
::doc/added "1.18"}
|
||||
[{:keys [::db/pool]} {:keys [password] :as params}]
|
||||
(db/with-atomic [conn pool]
|
||||
(let [admins (cf/get :admins)
|
||||
profile (db/get-by-id conn :profile (::rpc/profile-id params))]
|
||||
|
||||
(if (or (:is-admin profile)
|
||||
(contains? admins (:email profile)))
|
||||
{:password (auth/derive-password password)}
|
||||
(ex/raise :type :authentication
|
||||
:code :only-admins-allowed
|
||||
:hint "only admins allowed to call this RPC method")))))
|
||||
|
||||
;; --- MUTATION: Check profile password
|
||||
|
||||
(s/def ::attempt ::us/not-empty-string)
|
||||
(s/def ::check-profile-password
|
||||
(s/keys :req [::rpc/profile-id]
|
||||
:req-un [::profile-id ::password]))
|
||||
|
||||
(sv/defmethod ::check-profile-password
|
||||
"Check profile password, only ADMINS allowed to call this RPC
|
||||
methods. Designed for administration pannel integration."
|
||||
{::climit/queue :auth
|
||||
::climit/key-fn ::rpc/profile-id
|
||||
::doc/added "1.18"}
|
||||
[{:keys [::db/pool]} {:keys [profile-id password] :as params}]
|
||||
(db/with-atomic [conn pool]
|
||||
(let [admins (cf/get :admins)
|
||||
profile (db/get-by-id pool :profile (::rpc/profile-id params))]
|
||||
|
||||
(if (or (:is-admin profile)
|
||||
(contains? admins (:email profile)))
|
||||
(let [profile (if (not= (::rpc/profile-id params) profile-id)
|
||||
(db/get-by-id conn :profile profile-id)
|
||||
profile)]
|
||||
(auth/verify-password password (:password profile)))
|
||||
(ex/raise :type :authentication
|
||||
:code :only-admins-allowed
|
||||
:hint "only admins allowed to call this RPC method")))))
|
|
@ -8,6 +8,7 @@
|
|||
(:require
|
||||
[app.common.spec :as us]
|
||||
[app.db :as db]
|
||||
[app.rpc :as-alias rpc]
|
||||
[app.rpc.doc :as-alias doc]
|
||||
[app.util.services :as sv]
|
||||
[clojure.spec.alpha :as s]))
|
||||
|
@ -47,18 +48,18 @@
|
|||
order by f.created_at asc")
|
||||
|
||||
(defn search-files
|
||||
[conn {:keys [profile-id team-id search-term] :as params}]
|
||||
[conn {:keys [::rpc/profile-id team-id search-term] :as params}]
|
||||
(db/exec! conn [sql:search-files
|
||||
profile-id team-id
|
||||
profile-id team-id
|
||||
search-term]))
|
||||
|
||||
(s/def ::profile-id ::us/uuid)
|
||||
(s/def ::team-id ::us/uuid)
|
||||
(s/def ::search-files ::us/string)
|
||||
|
||||
(s/def ::search-files
|
||||
(s/keys :req-un [::profile-id ::team-id]
|
||||
(s/keys :req [::rpc/profile-id]
|
||||
:req-un [::team-id]
|
||||
:opt-un [::search-term]))
|
||||
|
||||
(sv/defmethod ::search-files
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
[app.loggers.audit :as audit]
|
||||
[app.main :as-alias main]
|
||||
[app.media :as media]
|
||||
[app.rpc :as-alias rpc]
|
||||
[app.rpc.climit :as climit]
|
||||
[app.rpc.doc :as-alias doc]
|
||||
[app.rpc.helpers :as rph]
|
||||
|
@ -35,7 +36,6 @@
|
|||
|
||||
(s/def ::id ::us/uuid)
|
||||
(s/def ::name ::us/string)
|
||||
(s/def ::profile-id ::us/uuid)
|
||||
(s/def ::file-id ::us/uuid)
|
||||
(s/def ::team-id ::us/uuid)
|
||||
|
||||
|
@ -78,11 +78,11 @@
|
|||
(declare retrieve-teams)
|
||||
|
||||
(s/def ::get-teams
|
||||
(s/keys :req-un [::profile-id]))
|
||||
(s/keys :req [::rpc/profile-id]))
|
||||
|
||||
(sv/defmethod ::get-teams
|
||||
{::doc/added "1.17"}
|
||||
[{:keys [pool] :as cfg} {:keys [profile-id]}]
|
||||
[{:keys [pool] :as cfg} {:keys [::rpc/profile-id] :as params}]
|
||||
(with-open [conn (db/open pool)]
|
||||
(retrieve-teams conn profile-id)))
|
||||
|
||||
|
@ -122,11 +122,12 @@
|
|||
(declare retrieve-team)
|
||||
|
||||
(s/def ::get-team
|
||||
(s/keys :req-un [::profile-id ::id]))
|
||||
(s/keys :req [::rpc/profile-id]
|
||||
:req-un [::id]))
|
||||
|
||||
(sv/defmethod ::get-team
|
||||
{::doc/added "1.17"}
|
||||
[{:keys [pool] :as cfg} {:keys [profile-id id]}]
|
||||
[{:keys [pool] :as cfg} {:keys [::rpc/profile-id id]}]
|
||||
(with-open [conn (db/open pool)]
|
||||
(retrieve-team conn profile-id id)))
|
||||
|
||||
|
@ -161,11 +162,12 @@
|
|||
|
||||
(s/def ::team-id ::us/uuid)
|
||||
(s/def ::get-team-members
|
||||
(s/keys :req-un [::profile-id ::team-id]))
|
||||
(s/keys :req [::rpc/profile-id]
|
||||
:req-un [::team-id]))
|
||||
|
||||
(sv/defmethod ::get-team-members
|
||||
{::doc/added "1.17"}
|
||||
[{:keys [::db/pool] :as cfg} {:keys [profile-id team-id]}]
|
||||
[{:keys [::db/pool] :as cfg} {:keys [::rpc/profile-id team-id]}]
|
||||
(with-open [conn (db/open pool)]
|
||||
(check-read-permissions! conn profile-id team-id)
|
||||
(retrieve-team-members conn team-id)))
|
||||
|
@ -177,13 +179,13 @@
|
|||
(declare retrieve-team-for-file)
|
||||
|
||||
(s/def ::get-team-users
|
||||
(s/and (s/keys :req-un [::profile-id]
|
||||
(s/and (s/keys :req [::rpc/profile-id]
|
||||
:opt-un [::team-id ::file-id])
|
||||
#(or (:team-id %) (:file-id %))))
|
||||
|
||||
(sv/defmethod ::get-team-users
|
||||
{::doc/added "1.17"}
|
||||
[{:keys [::db/pool] :as cfg} {:keys [profile-id team-id file-id]}]
|
||||
[{:keys [::db/pool] :as cfg} {:keys [::rpc/profile-id team-id file-id]}]
|
||||
(with-open [conn (db/open pool)]
|
||||
(if team-id
|
||||
(do
|
||||
|
@ -236,11 +238,12 @@
|
|||
(declare retrieve-team-stats)
|
||||
|
||||
(s/def ::get-team-stats
|
||||
(s/keys :req-un [::profile-id ::team-id]))
|
||||
(s/keys :req [::rpc/profile-id]
|
||||
:req-un [::team-id]))
|
||||
|
||||
(sv/defmethod ::get-team-stats
|
||||
{::doc/added "1.17"}
|
||||
[{:keys [::db/pool] :as cfg} {:keys [profile-id team-id]}]
|
||||
[{:keys [::db/pool] :as cfg} {:keys [::rpc/profile-id team-id]}]
|
||||
(with-open [conn (db/open pool)]
|
||||
(check-read-permissions! conn profile-id team-id)
|
||||
(retrieve-team-stats conn team-id)))
|
||||
|
@ -257,7 +260,8 @@
|
|||
;; --- Query: Team invitations
|
||||
|
||||
(s/def ::get-team-invitations
|
||||
(s/keys :req-un [::profile-id ::team-id]))
|
||||
(s/keys :req [::rpc/profile-id]
|
||||
:req-un [::team-id]))
|
||||
|
||||
(def sql:team-invitations
|
||||
"select email_to as email, role, (valid_until < now()) as expired
|
||||
|
@ -270,7 +274,7 @@
|
|||
|
||||
(sv/defmethod ::get-team-invitations
|
||||
{::doc/added "1.17"}
|
||||
[{:keys [::db/pool] :as cfg} {:keys [profile-id team-id]}]
|
||||
[{:keys [::db/pool] :as cfg} {:keys [::rpc/profile-id team-id]}]
|
||||
(with-open [conn (db/open pool)]
|
||||
(check-read-permissions! conn profile-id team-id)
|
||||
(get-team-invitations conn team-id)))
|
||||
|
@ -285,14 +289,15 @@
|
|||
(declare ^:private create-team-default-project)
|
||||
|
||||
(s/def ::create-team
|
||||
(s/keys :req-un [::profile-id ::name]
|
||||
(s/keys :req [::rpc/profile-id]
|
||||
:req-un [::name]
|
||||
:opt-un [::id]))
|
||||
|
||||
(sv/defmethod ::create-team
|
||||
{::doc/added "1.17"}
|
||||
[{:keys [::db/pool] :as cfg} params]
|
||||
[{:keys [::db/pool] :as cfg} {:keys [::rpc/profile-id] :as params}]
|
||||
(db/with-atomic [conn pool]
|
||||
(create-team conn params)))
|
||||
(create-team conn (assoc params :profile-id profile-id))))
|
||||
|
||||
(defn create-team
|
||||
"This is a complete team creation process, it creates the team
|
||||
|
@ -316,22 +321,20 @@
|
|||
:is-default is-default})))
|
||||
|
||||
(defn- create-team-role
|
||||
[conn {:keys [team-id profile-id role] :as params}]
|
||||
[conn {:keys [profile-id team-id role] :as params}]
|
||||
(let [params {:team-id team-id
|
||||
:profile-id profile-id}]
|
||||
(->> (perms/assign-role-flags params role)
|
||||
(db/insert! conn :team-profile-rel))))
|
||||
|
||||
(defn- create-team-default-project
|
||||
[conn {:keys [team-id profile-id] :as params}]
|
||||
[conn {:keys [profile-id team-id] :as params}]
|
||||
(let [project {:id (uuid/next)
|
||||
:team-id team-id
|
||||
:name "Drafts"
|
||||
:is-default true}
|
||||
project (create-project conn project)]
|
||||
(create-project-role conn {:project-id (:id project)
|
||||
:profile-id profile-id
|
||||
:role :owner})
|
||||
(create-project-role conn profile-id (:id project) :owner)
|
||||
project))
|
||||
|
||||
;; NOTE: we have project creation here because there are cyclic
|
||||
|
@ -351,7 +354,7 @@
|
|||
:is-default is-default})))
|
||||
|
||||
(defn create-project-role
|
||||
[conn {:keys [project-id profile-id role]}]
|
||||
[conn profile-id project-id role]
|
||||
(let [params {:project-id project-id
|
||||
:profile-id profile-id}]
|
||||
(->> (perms/assign-role-flags params role)
|
||||
|
@ -360,11 +363,12 @@
|
|||
;; --- Mutation: Update Team
|
||||
|
||||
(s/def ::update-team
|
||||
(s/keys :req-un [::profile-id ::name ::id]))
|
||||
(s/keys :req [::rpc/profile-id]
|
||||
:req-un [::name ::id]))
|
||||
|
||||
(sv/defmethod ::update-team
|
||||
{::doc/added "1.17"}
|
||||
[{:keys [::db/pool] :as cfg} {:keys [id name profile-id] :as params}]
|
||||
[{:keys [::db/pool] :as cfg} {:keys [::rpc/profile-id id name] :as params}]
|
||||
(db/with-atomic [conn pool]
|
||||
(check-edition-permissions! conn profile-id id)
|
||||
(db/update! conn :team
|
||||
|
@ -379,11 +383,12 @@
|
|||
|
||||
(s/def ::reassign-to ::us/uuid)
|
||||
(s/def ::leave-team
|
||||
(s/keys :req-un [::profile-id ::id]
|
||||
(s/keys :req [::rpc/profile-id]
|
||||
:req-un [::id]
|
||||
:opt-un [::reassign-to]))
|
||||
|
||||
(defn leave-team
|
||||
[conn {:keys [id profile-id reassign-to]}]
|
||||
[conn {:keys [::rpc/profile-id id reassign-to]}]
|
||||
(let [perms (get-permissions conn profile-id id)
|
||||
members (retrieve-team-members conn id)]
|
||||
|
||||
|
@ -438,7 +443,8 @@
|
|||
;; --- Mutation: Delete Team
|
||||
|
||||
(s/def ::delete-team
|
||||
(s/keys :req-un [::profile-id ::id]))
|
||||
(s/keys :req [::rpc/profile-id]
|
||||
:req-un [::id]))
|
||||
|
||||
;; TODO: right now just don't allow delete default team, in future it
|
||||
;; should raise a specific exception for signal that this action is
|
||||
|
@ -446,7 +452,7 @@
|
|||
|
||||
(sv/defmethod ::delete-team
|
||||
{::doc/added "1.17"}
|
||||
[{:keys [pool] :as cfg} {:keys [id profile-id] :as params}]
|
||||
[{:keys [pool] :as cfg} {:keys [::rpc/profile-id id] :as params}]
|
||||
(db/with-atomic [conn pool]
|
||||
(let [perms (get-permissions conn profile-id id)]
|
||||
(when-not (:is-owner perms)
|
||||
|
@ -477,7 +483,7 @@
|
|||
:viewer {:is-owner false :is-admin false :can-edit false}))
|
||||
|
||||
(defn update-team-member-role
|
||||
[conn {:keys [team-id profile-id member-id role] :as params}]
|
||||
[conn {:keys [profile-id team-id member-id role] :as params}]
|
||||
;; We retrieve all team members instead of query the
|
||||
;; database for a single member. This is just for
|
||||
;; convenience, if this becomes a bottleneck or problematic,
|
||||
|
@ -524,23 +530,25 @@
|
|||
nil)))
|
||||
|
||||
(s/def ::update-team-member-role
|
||||
(s/keys :req-un [::profile-id ::team-id ::member-id ::role]))
|
||||
(s/keys :req [::rpc/profile-id]
|
||||
:req-un [::team-id ::member-id ::role]))
|
||||
|
||||
(sv/defmethod ::update-team-member-role
|
||||
{::doc/added "1.17"}
|
||||
[{:keys [::db/pool] :as cfg} params]
|
||||
(db/with-atomic [conn pool]
|
||||
(update-team-member-role conn params)))
|
||||
(update-team-member-role conn (assoc params :profile-id (::rpc/profile-id params)))))
|
||||
|
||||
|
||||
;; --- Mutation: Delete Team Member
|
||||
|
||||
(s/def ::delete-team-member
|
||||
(s/keys :req-un [::profile-id ::team-id ::member-id]))
|
||||
(s/keys :req [::rpc/profile-id]
|
||||
:req-un [::team-id ::member-id]))
|
||||
|
||||
(sv/defmethod ::delete-team-member
|
||||
{::doc/added "1.17"}
|
||||
[{:keys [pool] :as cfg} {:keys [team-id profile-id member-id] :as params}]
|
||||
[{:keys [pool] :as cfg} {:keys [::rpc/profile-id team-id member-id] :as params}]
|
||||
(db/with-atomic [conn pool]
|
||||
(let [perms (get-permissions conn profile-id team-id)]
|
||||
(when-not (or (:is-owner perms)
|
||||
|
@ -564,15 +572,16 @@
|
|||
|
||||
(s/def ::file ::media/upload)
|
||||
(s/def ::update-team-photo
|
||||
(s/keys :req-un [::profile-id ::team-id ::file]))
|
||||
(s/keys :req [::rpc/profile-id]
|
||||
:req-un [::team-id ::file]))
|
||||
|
||||
(sv/defmethod ::update-team-photo
|
||||
{::doc/added "1.17"}
|
||||
[cfg {:keys [file] :as params}]
|
||||
[cfg {:keys [::rpc/profile-id file] :as params}]
|
||||
;; Validate incoming mime type
|
||||
(media/validate-media-type! file #{"image/jpeg" "image/png" "image/webp"})
|
||||
(let [cfg (update cfg :storage media/configure-assets-storage)]
|
||||
(update-team-photo cfg params)))
|
||||
(update-team-photo cfg (assoc params :profile-id profile-id))))
|
||||
|
||||
(defn update-team-photo
|
||||
[{:keys [pool storage executor] :as cfg} {:keys [profile-id team-id] :as params}]
|
||||
|
@ -632,10 +641,10 @@
|
|||
update set role = ?, updated_at = now();")
|
||||
|
||||
(defn- create-invitation-token
|
||||
[cfg {:keys [expire profile-id team-id member-id member-email role]}]
|
||||
[cfg {:keys [profile-id valid-until team-id member-id member-email role]}]
|
||||
(tokens/generate (::main/props cfg)
|
||||
{:iss :team-invitation
|
||||
:exp expire
|
||||
:exp valid-until
|
||||
:profile-id profile-id
|
||||
:role role
|
||||
:team-id team-id
|
||||
|
@ -654,7 +663,7 @@
|
|||
(let [member (profile/retrieve-profile-data-by-email conn email)
|
||||
expire (dt/in-future "168h") ;; 7 days
|
||||
itoken (create-invitation-token cfg {:profile-id (:id profile)
|
||||
:expire expire
|
||||
:valid-until expire
|
||||
:team-id (:id team)
|
||||
:member-email (or (:email member) email)
|
||||
:member-id (:id member)
|
||||
|
@ -715,14 +724,15 @@
|
|||
(s/def ::email ::us/email)
|
||||
(s/def ::emails ::us/set-of-valid-emails)
|
||||
(s/def ::create-team-invitations
|
||||
(s/keys :req-un [::profile-id ::team-id ::role]
|
||||
(s/keys :req [::rpc/profile-id]
|
||||
:req-un [::team-id ::role]
|
||||
:opt-un [::email ::emails]))
|
||||
|
||||
(sv/defmethod ::create-team-invitations
|
||||
"A rpc call that allow to send a single or multiple invitations to
|
||||
join the team."
|
||||
{::doc/added "1.17"}
|
||||
[{:keys [pool] :as cfg} {:keys [profile-id team-id email emails role] :as params}]
|
||||
[{:keys [pool] :as cfg} {:keys [::rpc/profile-id team-id email emails role] :as params}]
|
||||
(db/with-atomic [conn pool]
|
||||
(let [perms (get-permissions conn profile-id team-id)
|
||||
profile (db/get-by-id conn :profile profile-id)
|
||||
|
@ -760,7 +770,7 @@
|
|||
|
||||
(sv/defmethod ::create-team-with-invitations
|
||||
{::doc/added "1.17"}
|
||||
[{:keys [pool] :as cfg} {:keys [profile-id emails role] :as params}]
|
||||
[{:keys [pool] :as cfg} {:keys [::rpc/profile-id emails role] :as params}]
|
||||
(db/with-atomic [conn pool]
|
||||
(let [team (create-team conn params)
|
||||
profile (db/get-by-id conn :profile profile-id)
|
||||
|
@ -791,11 +801,12 @@
|
|||
;; --- Query: get-team-invitation-token
|
||||
|
||||
(s/def ::get-team-invitation-token
|
||||
(s/keys :req-un [::profile-id ::team-id ::email]))
|
||||
(s/keys :req [::rpc/profile-id]
|
||||
:req-un [::team-id ::email]))
|
||||
|
||||
(sv/defmethod ::get-team-invitation-token
|
||||
{::doc/added "1.17"}
|
||||
[{:keys [::db/pool] :as cfg} {:keys [profile-id team-id email] :as params}]
|
||||
[{:keys [::db/pool] :as cfg} {:keys [::rpc/profile-id team-id email] :as params}]
|
||||
(check-read-permissions! pool profile-id team-id)
|
||||
(let [invit (-> (db/get pool :team-invitation
|
||||
{:team-id team-id
|
||||
|
@ -804,7 +815,7 @@
|
|||
member (profile/retrieve-profile-data-by-email pool (:email invit))
|
||||
token (create-invitation-token cfg {:team-id (:team-id invit)
|
||||
:profile-id profile-id
|
||||
:expire (:expire invit)
|
||||
:valid-until (:valid-until invit)
|
||||
:role (:role invit)
|
||||
:member-id (:id member)
|
||||
:member-email (or (:email member) (:email-to invit))})]
|
||||
|
@ -813,11 +824,12 @@
|
|||
;; --- Mutation: Update invitation role
|
||||
|
||||
(s/def ::update-team-invitation-role
|
||||
(s/keys :req-un [::profile-id ::team-id ::email ::role]))
|
||||
(s/keys :req [::rpc/profile-id]
|
||||
:req-un [::team-id ::email ::role]))
|
||||
|
||||
(sv/defmethod ::update-team-invitation-role
|
||||
{::doc/added "1.17"}
|
||||
[{:keys [pool] :as cfg} {:keys [profile-id team-id email role] :as params}]
|
||||
[{:keys [pool] :as cfg} {:keys [::rpc/profile-id team-id email role] :as params}]
|
||||
(db/with-atomic [conn pool]
|
||||
(let [perms (get-permissions conn profile-id team-id)]
|
||||
|
||||
|
@ -833,11 +845,12 @@
|
|||
;; --- Mutation: Delete invitation
|
||||
|
||||
(s/def ::delete-team-invitation
|
||||
(s/keys :req-un [::profile-id ::team-id ::email]))
|
||||
(s/keys :req [::rpc/profile-id]
|
||||
:req-un [::team-id ::email]))
|
||||
|
||||
(sv/defmethod ::delete-team-invitation
|
||||
{::doc/added "1.17"}
|
||||
[{:keys [pool] :as cfg} {:keys [profile-id team-id email] :as params}]
|
||||
[{:keys [pool] :as cfg} {:keys [::rpc/profile-id team-id email] :as params}]
|
||||
(db/with-atomic [conn pool]
|
||||
(let [perms (get-permissions conn profile-id team-id)]
|
||||
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
[app.db :as db]
|
||||
[app.http.session :as session]
|
||||
[app.loggers.audit :as audit]
|
||||
[app.rpc :as-alias rpc]
|
||||
[app.rpc.commands.teams :as teams]
|
||||
[app.rpc.doc :as-alias doc]
|
||||
[app.rpc.helpers :as rph]
|
||||
|
@ -27,10 +28,10 @@
|
|||
|
||||
(s/def ::verify-token
|
||||
(s/keys :req-un [::token]
|
||||
:opt-un [::profile-id]))
|
||||
:opt [::rpc/profile-id]))
|
||||
|
||||
(sv/defmethod ::verify-token
|
||||
{:auth false
|
||||
{::rpc/auth false
|
||||
::doc/added "1.15"}
|
||||
[{:keys [pool sprops] :as cfg} {:keys [token] :as params}]
|
||||
(db/with-atomic [conn pool]
|
||||
|
@ -126,7 +127,8 @@
|
|||
:opt-un [::spec.team-invitation/member-id]))
|
||||
|
||||
(defmethod process-token :team-invitation
|
||||
[{:keys [conn session] :as cfg} {:keys [profile-id token]}
|
||||
[{:keys [conn session] :as cfg}
|
||||
{:keys [::rpc/profile-id token]}
|
||||
{:keys [member-id team-id member-email] :as claims}]
|
||||
|
||||
(us/verify! ::team-invitation-claims claims)
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
(:require
|
||||
[app.common.exceptions :as ex]
|
||||
[app.db :as db]
|
||||
[app.rpc :as-alias rpc]
|
||||
[app.rpc.commands.comments :as comments]
|
||||
[app.rpc.commands.files :as files]
|
||||
[app.rpc.cond :as-alias cond]
|
||||
|
@ -73,16 +74,16 @@
|
|||
|
||||
(s/def ::get-view-only-bundle
|
||||
(s/keys :req-un [::files/file-id]
|
||||
:opt-un [::files/profile-id
|
||||
::files/share-id
|
||||
::files/features]))
|
||||
:opt-un [::files/share-id
|
||||
::files/features]
|
||||
:opt [::rpc/profile-id]))
|
||||
|
||||
(sv/defmethod ::get-view-only-bundle
|
||||
{:auth false
|
||||
{::rpc/auth false
|
||||
::cond/get-object #(files/get-minimal-file %1 (:file-id %2))
|
||||
::cond/key-fn files/get-file-etag
|
||||
::cond/reuse-key? true
|
||||
::doc/added "1.17"}
|
||||
[{:keys [pool]} params]
|
||||
(with-open [conn (db/open pool)]
|
||||
(get-view-only-bundle conn params)))
|
||||
(get-view-only-bundle conn (assoc params :profile-id (::rpc/profile-id params)))))
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
[app.db :as db]
|
||||
[app.http.client :as http]
|
||||
[app.loggers.webhooks :as webhooks]
|
||||
[app.rpc :as-alias rpc]
|
||||
[app.rpc.commands.teams :refer [check-edition-permissions! check-read-permissions!]]
|
||||
[app.rpc.doc :as-alias doc]
|
||||
[app.util.services :as sv]
|
||||
|
@ -23,7 +24,6 @@
|
|||
|
||||
;; --- Mutation: Create Webhook
|
||||
|
||||
(s/def ::profile-id ::us/uuid)
|
||||
(s/def ::team-id ::us/uuid)
|
||||
(s/def ::uri ::us/not-empty-string)
|
||||
(s/def ::is-active ::us/boolean)
|
||||
|
@ -33,7 +33,8 @@
|
|||
"application/transit+json"})
|
||||
|
||||
(s/def ::create-webhook
|
||||
(s/keys :req-un [::profile-id ::team-id ::uri ::mtype]
|
||||
(s/keys :req [::rpc/profile-id]
|
||||
:req-un [::team-id ::uri ::mtype]
|
||||
:opt-un [::is-active]))
|
||||
|
||||
;; NOTE: for now the quote is hardcoded but this need to be solved in
|
||||
|
@ -98,7 +99,7 @@
|
|||
|
||||
(sv/defmethod ::create-webhook
|
||||
{::doc/added "1.17"}
|
||||
[{:keys [::db/pool ::wrk/executor] :as cfg} {:keys [profile-id team-id] :as params}]
|
||||
[{:keys [::db/pool ::wrk/executor] :as cfg} {:keys [::rpc/profile-id team-id] :as params}]
|
||||
(check-edition-permissions! pool profile-id team-id)
|
||||
(validate-quotes! cfg params)
|
||||
(->> (validate-webhook! cfg nil params)
|
||||
|
@ -109,18 +110,19 @@
|
|||
|
||||
(sv/defmethod ::update-webhook
|
||||
{::doc/added "1.17"}
|
||||
[{:keys [::db/pool ::wrk/executor] :as cfg} {:keys [id profile-id] :as params}]
|
||||
[{:keys [::db/pool ::wrk/executor] :as cfg} {:keys [::rpc/profile-id id] :as params}]
|
||||
(let [whook (db/get pool :webhook {:id id})]
|
||||
(check-edition-permissions! pool profile-id (:team-id whook))
|
||||
(->> (validate-webhook! cfg whook params)
|
||||
(p/fmap executor (fn [_] (update-webhook! cfg whook params))))))
|
||||
|
||||
(s/def ::delete-webhook
|
||||
(s/keys :req-un [::profile-id ::id]))
|
||||
(s/keys :req [::rpc/profile-id]
|
||||
:req-un [::id]))
|
||||
|
||||
(sv/defmethod ::delete-webhook
|
||||
{::doc/added "1.17"}
|
||||
[{:keys [::db/pool] :as cfg} {:keys [profile-id id]}]
|
||||
[{:keys [::db/pool] :as cfg} {:keys [::rpc/profile-id id]}]
|
||||
(db/with-atomic [conn pool]
|
||||
(let [whook (db/get conn :webhook {:id id})]
|
||||
(check-edition-permissions! conn profile-id (:team-id whook))
|
||||
|
@ -131,14 +133,15 @@
|
|||
|
||||
(s/def ::team-id ::us/uuid)
|
||||
(s/def ::get-webhooks
|
||||
(s/keys :req-un [::profile-id ::team-id]))
|
||||
(s/keys :req [::rpc/profile-id]
|
||||
:req-un [::team-id]))
|
||||
|
||||
(def sql:get-webhooks
|
||||
"select id, uri, mtype, is_active, error_code, error_count
|
||||
from webhook where team_id = ? order by uri")
|
||||
|
||||
(sv/defmethod ::get-webhooks
|
||||
[{:keys [pool] :as cfg} {:keys [profile-id team-id]}]
|
||||
[{:keys [pool] :as cfg} {:keys [::rpc/profile-id team-id]}]
|
||||
(with-open [conn (db/open pool)]
|
||||
(check-read-permissions! conn profile-id team-id)
|
||||
(db/exec! conn [sql:get-webhooks team-id])))
|
||||
|
|
|
@ -81,7 +81,8 @@
|
|||
(db/with-atomic [conn pool]
|
||||
(cmd.files/check-edition-permissions! conn profile-id id)
|
||||
(cmd.files/absorb-library conn params)
|
||||
(cmd.files/mark-file-deleted conn params)))
|
||||
(cmd.files/mark-file-deleted conn params)
|
||||
nil))
|
||||
|
||||
;; --- Mutation: Link file to library
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
(ns app.rpc.mutations.profile
|
||||
(:require
|
||||
[app.auth :as auth]
|
||||
[app.common.data :as d]
|
||||
[app.common.exceptions :as ex]
|
||||
[app.common.spec :as us]
|
||||
|
@ -17,7 +18,7 @@
|
|||
[app.media :as media]
|
||||
[app.rpc :as-alias rpc]
|
||||
[app.rpc.climit :as-alias climit]
|
||||
[app.rpc.commands.auth :as auth]
|
||||
[app.rpc.commands.auth :as cmd.auth]
|
||||
[app.rpc.commands.teams :as teams]
|
||||
[app.rpc.doc :as-alias doc]
|
||||
[app.rpc.helpers :as rph]
|
||||
|
@ -182,7 +183,7 @@
|
|||
(defn- change-email-immediately
|
||||
[{:keys [conn]} {:keys [profile email] :as params}]
|
||||
(when (not= email (:email profile))
|
||||
(auth/check-profile-existence! conn params))
|
||||
(cmd.auth/check-profile-existence! conn params))
|
||||
(db/update! conn :profile
|
||||
{:email email}
|
||||
{:id (:id profile)})
|
||||
|
@ -201,7 +202,7 @@
|
|||
:exp (dt/in-future {:days 30})})]
|
||||
|
||||
(when (not= email (:email profile))
|
||||
(auth/check-profile-existence! conn params))
|
||||
(cmd.auth/check-profile-existence! conn params))
|
||||
|
||||
(when-not (eml/allow-send-emails? conn profile)
|
||||
(ex/raise :type :validation
|
||||
|
|
|
@ -26,8 +26,6 @@
|
|||
|
||||
;; --- Mutation: Create Project
|
||||
|
||||
(declare create-project-profile-state)
|
||||
|
||||
(s/def ::team-id ::us/uuid)
|
||||
(s/def ::create-project
|
||||
(s/keys :req-un [::profile-id ::team-id ::name]
|
||||
|
@ -39,21 +37,16 @@
|
|||
[{:keys [pool] :as cfg} {:keys [profile-id team-id] :as params}]
|
||||
(db/with-atomic [conn pool]
|
||||
(teams/check-edition-permissions! conn profile-id team-id)
|
||||
(let [project (teams/create-project conn params)
|
||||
params (assoc params
|
||||
:project-id (:id project)
|
||||
:role :owner)]
|
||||
(teams/create-project-role conn params)
|
||||
(create-project-profile-state conn params)
|
||||
(assoc project :is-pinned true))))
|
||||
(let [project (teams/create-project conn params)]
|
||||
(teams/create-project-role conn profile-id (:id project) :owner)
|
||||
|
||||
(defn create-project-profile-state
|
||||
[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}))
|
||||
(db/insert! conn :team-project-profile-rel
|
||||
{:project-id (:id project)
|
||||
:profile-id profile-id
|
||||
:team-id team-id
|
||||
:is-pinned true})
|
||||
|
||||
(assoc project :is-pinned true))))
|
||||
|
||||
;; --- Mutation: Toggle Project Pin
|
||||
|
||||
|
@ -122,4 +115,7 @@
|
|||
{:deleted-at (dt/now)}
|
||||
{:id id :is-default false})]
|
||||
(rph/with-meta (rph/wrap)
|
||||
{::audit/props {:team-id (:team-id project)}}))))
|
||||
{::audit/props {:team-id (:team-id project)
|
||||
:name (:name project)
|
||||
:created-at (:created-at project)
|
||||
:modified-at (:modified-at project)}}))))
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
[app.common.spec :as us]
|
||||
[app.common.uuid :as uuid]
|
||||
[app.db :as db]
|
||||
[app.rpc :as-alias rpc]
|
||||
[app.util.services :as sv]
|
||||
[clojure.spec.alpha :as s]
|
||||
[cuerdas.core :as str]))
|
||||
|
@ -36,7 +37,7 @@
|
|||
(s/keys :opt-un [::profile-id]))
|
||||
|
||||
(sv/defmethod ::profile
|
||||
{:auth false}
|
||||
{::rpc/auth false}
|
||||
[{:keys [pool] :as cfg} {:keys [profile-id] :as params}]
|
||||
;; We need to return the anonymous profile object in two cases, when
|
||||
;; no profile-id is in session, and when db call raises not found. In all other
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
(:require
|
||||
[app.common.spec :as us]
|
||||
[app.db :as db]
|
||||
[app.rpc :as-alias rpc]
|
||||
[app.rpc.commands.viewer :as viewer]
|
||||
[app.rpc.doc :as-alias doc]
|
||||
[app.util.services :as sv]
|
||||
|
@ -19,7 +20,7 @@
|
|||
(s/keys :opt-un [::components-v2])))
|
||||
|
||||
(sv/defmethod ::view-only-bundle
|
||||
{:auth false
|
||||
{::rpc/auth false
|
||||
::doc/added "1.3"
|
||||
::doc/deprecated "1.17"}
|
||||
[{:keys [pool] :as cfg} {:keys [features components-v2] :as params}]
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
[app.db :as db]
|
||||
[app.main :as-alias main]
|
||||
[app.setup.builtin-templates]
|
||||
[app.setup.initial-user]
|
||||
[app.setup.keys :as keys]
|
||||
[buddy.core.codecs :as bc]
|
||||
[buddy.core.nonce :as bn]
|
||||
|
|
40
backend/src/app/setup/initial_user.clj
Normal file
40
backend/src/app/setup/initial_user.clj
Normal file
|
@ -0,0 +1,40 @@
|
|||
;; 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) KALEIDOS INC
|
||||
|
||||
(ns app.setup.initial-user
|
||||
"Initial data setup of instance."
|
||||
(:require
|
||||
[app.auth :as auth]
|
||||
[app.common.logging :as l]
|
||||
[app.config :as cf]
|
||||
[app.db :as db]
|
||||
[app.setup :as-alias setup]
|
||||
[clojure.spec.alpha :as s]
|
||||
[integrant.core :as ig]))
|
||||
|
||||
(def ^:private sql:insert-profile
|
||||
"insert into profile (id, fullname, email, password, is_active, is_admin, created_at, modified_at)
|
||||
values ('00000000-0000-0000-0000-000000000000', 'Admin', ?, ?, true, true, now(), now())
|
||||
on conflict (id)
|
||||
do update set email = ?, password = ?")
|
||||
|
||||
(defmethod ig/pre-init-spec ::setup/initial-profile [_]
|
||||
(s/keys :req [::db/pool]))
|
||||
|
||||
(defmethod ig/init-key ::setup/initial-profile
|
||||
[_ {:keys [::db/pool]}]
|
||||
(let [email (cf/get :setup-admin-email)
|
||||
password (cf/get :setup-admin-password)]
|
||||
(when (and email password)
|
||||
(db/with-atomic [conn pool]
|
||||
(let [pwd (auth/derive-password password)]
|
||||
(db/exec-one! conn [sql:insert-profile email pwd email pwd])
|
||||
(l/info :hint "setting initial user (admin)"
|
||||
:email email
|
||||
:password "********"))))
|
||||
nil))
|
||||
|
||||
|
|
@ -8,6 +8,7 @@
|
|||
"A main namespace for server repl."
|
||||
#_:clj-kondo/ignore
|
||||
(:require
|
||||
[app.auth :refer [derive-password]]
|
||||
[app.common.data :as d]
|
||||
[app.common.exceptions :as ex]
|
||||
[app.common.logging :as l]
|
||||
|
@ -20,7 +21,6 @@
|
|||
[app.db :as db]
|
||||
[app.db.sql :as sql]
|
||||
[app.main :refer [system]]
|
||||
[app.rpc.commands.auth :refer [derive-password]]
|
||||
[app.rpc.queries.profile :as prof]
|
||||
[app.util.blob :as blob]
|
||||
[app.util.time :as dt]
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
(ns app.tasks.objects-gc
|
||||
"A maintenance task that performs a general purpose garbage collection
|
||||
of deleted objects."
|
||||
of deleted or unreachable objects."
|
||||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.common.logging :as l]
|
||||
|
@ -16,154 +16,247 @@
|
|||
[app.storage :as sto]
|
||||
[app.util.time :as dt]
|
||||
[clojure.spec.alpha :as s]
|
||||
[cuerdas.core :as str]
|
||||
[integrant.core :as ig]))
|
||||
|
||||
(def target-tables
|
||||
["profile"
|
||||
"team"
|
||||
"file"
|
||||
"project"
|
||||
"team_font_variant"])
|
||||
|
||||
(defmulti delete-objects :table)
|
||||
|
||||
(def sql:delete-objects
|
||||
"with deleted as (
|
||||
select id from %(table)s
|
||||
where deleted_at is not null
|
||||
and deleted_at < now() - ?::interval
|
||||
order by deleted_at
|
||||
limit %(limit)s
|
||||
)
|
||||
delete from %(table)s
|
||||
where id in (select id from deleted)
|
||||
returning *")
|
||||
|
||||
;; --- IMPL: generic object deletion
|
||||
|
||||
(defmethod delete-objects :default
|
||||
[{:keys [conn min-age table] :as cfg}]
|
||||
(let [sql (str/fmt sql:delete-objects
|
||||
{:table table :limit 50})
|
||||
result (db/exec! conn [sql min-age])]
|
||||
|
||||
(doseq [{:keys [id] :as item} result]
|
||||
(l/debug :hint "permanently delete object" :table table :id id))
|
||||
|
||||
(count result)))
|
||||
|
||||
;; --- IMPL: file deletion
|
||||
|
||||
(defmethod delete-objects "file"
|
||||
[{:keys [conn min-age table] :as cfg}]
|
||||
(let [sql (str/fmt sql:delete-objects {:table table :limit 50})
|
||||
result (db/exec! conn [sql min-age])]
|
||||
|
||||
(doseq [{:keys [id] :as item} result]
|
||||
(l/debug :hint "permanently delete object" :table table :id id))
|
||||
|
||||
(count result)))
|
||||
|
||||
;; --- IMPL: team-font-variant deletion
|
||||
|
||||
(defmethod delete-objects "team_font_variant"
|
||||
[{:keys [conn min-age storage table] :as cfg}]
|
||||
(let [sql (str/fmt sql:delete-objects {:table table :limit 50})
|
||||
fonts (db/exec! conn [sql min-age])
|
||||
storage (media/configure-assets-storage storage conn)]
|
||||
(doseq [{:keys [id] :as font} fonts]
|
||||
(l/debug :hint "permanently delete object" :table table :id id)
|
||||
(some->> (:woff1-file-id font) (sto/touch-object! storage) deref)
|
||||
(some->> (:woff2-file-id font) (sto/touch-object! storage) deref)
|
||||
(some->> (:otf-file-id font) (sto/touch-object! storage) deref)
|
||||
(some->> (:ttf-file-id font) (sto/touch-object! storage) deref))
|
||||
(count fonts)))
|
||||
|
||||
;; --- IMPL: team deletion
|
||||
|
||||
(defmethod delete-objects "team"
|
||||
[{:keys [conn min-age storage table] :as cfg}]
|
||||
(let [sql (str/fmt sql:delete-objects {:table table :limit 50})
|
||||
teams (db/exec! conn [sql min-age])
|
||||
storage (media/configure-assets-storage storage conn)]
|
||||
|
||||
(doseq [{:keys [id] :as team} teams]
|
||||
(l/debug :hint "permanently delete object" :table table :id id)
|
||||
(some->> (:photo-id team) (sto/touch-object! storage) deref))
|
||||
|
||||
(count teams)))
|
||||
|
||||
;; --- IMPL: profile deletion
|
||||
|
||||
(def sql:retrieve-deleted-profiles
|
||||
"select id, photo_id from profile
|
||||
where deleted_at is not null
|
||||
and deleted_at < now() - ?::interval
|
||||
order by deleted_at
|
||||
limit ?
|
||||
for update")
|
||||
|
||||
(defmethod delete-objects "profile"
|
||||
[{:keys [conn min-age storage table] :as cfg}]
|
||||
(let [profiles (db/exec! conn [sql:retrieve-deleted-profiles min-age 50])
|
||||
storage (media/configure-assets-storage storage conn)]
|
||||
|
||||
(doseq [{:keys [id] :as profile} profiles]
|
||||
(l/debug :hint "permanently delete object" :table table :id id)
|
||||
|
||||
;; Mark as deleted the storage object related with the photo-id
|
||||
;; field.
|
||||
(some->> (:photo-id profile) (sto/touch-object! storage) deref)
|
||||
|
||||
;; And finally, permanently delete the profile.
|
||||
(db/delete! conn :profile {:id id}))
|
||||
|
||||
(count profiles)))
|
||||
|
||||
;; --- INIT
|
||||
|
||||
(defn- process-table
|
||||
[{:keys [table] :as cfg}]
|
||||
(loop [n 0]
|
||||
(let [res (delete-objects cfg)]
|
||||
(if (pos? res)
|
||||
(recur (+ n res))
|
||||
(do
|
||||
(l/debug :hint "delete summary" :table table :total n)
|
||||
n)))))
|
||||
(declare ^:private delete-profiles!)
|
||||
(declare ^:private delete-teams!)
|
||||
(declare ^:private delete-fonts!)
|
||||
(declare ^:private delete-projects!)
|
||||
(declare ^:private delete-files!)
|
||||
(declare ^:private delete-orphan-teams!)
|
||||
|
||||
(s/def ::min-age ::dt/duration)
|
||||
|
||||
(defmethod ig/pre-init-spec ::handler [_]
|
||||
(s/keys :req-un [::db/pool ::sto/storage]
|
||||
:opt-un [::min-age]))
|
||||
(s/keys :req [::db/pool ::sto/storage]
|
||||
:opt [::min-age]))
|
||||
|
||||
(defmethod ig/prep-key ::handler
|
||||
[_ cfg]
|
||||
(merge {:min-age cf/deletion-delay}
|
||||
(merge {::min-age cf/deletion-delay}
|
||||
(d/without-nils cfg)))
|
||||
|
||||
(defmethod ig/init-key ::handler
|
||||
[_ {:keys [pool] :as cfg}]
|
||||
[_ {:keys [::db/pool ::sto/storage] :as cfg}]
|
||||
(fn [params]
|
||||
(db/with-atomic [conn pool]
|
||||
(let [min-age (or (:min-age params) (:min-age cfg))
|
||||
(let [min-age (or (:min-age params) (::min-age cfg))
|
||||
_ (l/info :hint "gc started"
|
||||
:min-age (dt/format-duration min-age)
|
||||
:rollback? (boolean (:rollback? params)))
|
||||
|
||||
storage (media/configure-assets-storage storage conn)
|
||||
cfg (-> cfg
|
||||
(assoc :min-age (db/interval min-age))
|
||||
(assoc :conn conn))]
|
||||
(loop [tables (seq target-tables)
|
||||
total 0]
|
||||
(if-let [table (first tables)]
|
||||
(recur (rest tables)
|
||||
(+ total (process-table (assoc cfg :table table))))
|
||||
(do
|
||||
(l/info :hint "objects gc finished successfully"
|
||||
:min-age (dt/format-duration min-age)
|
||||
:total total)
|
||||
(assoc ::min-age (db/interval min-age))
|
||||
(assoc ::conn conn)
|
||||
(assoc ::storage storage))
|
||||
|
||||
(when (:rollback? params)
|
||||
(db/rollback! conn))
|
||||
htotal (+ (delete-profiles! cfg)
|
||||
(delete-teams! cfg)
|
||||
(delete-projects! cfg)
|
||||
(delete-files! cfg)
|
||||
(delete-fonts! cfg))
|
||||
stotal (delete-orphan-teams! cfg)]
|
||||
|
||||
{:processed total})))))))
|
||||
(l/info :hint "gc finished"
|
||||
:deleted htotal
|
||||
:orphans stotal
|
||||
:rollback? (boolean (:rollback? params)))
|
||||
|
||||
(when (:rollback? params)
|
||||
(db/rollback! conn))
|
||||
|
||||
{:processed (+ stotal htotal)}))))
|
||||
|
||||
|
||||
(def ^:private sql:get-profiles-chunk
|
||||
"select id, photo_id, created_at from profile
|
||||
where deleted_at is not null
|
||||
and deleted_at < now() - ?::interval
|
||||
and created_at < ?
|
||||
order by created_at desc
|
||||
limit 10
|
||||
for update skip locked")
|
||||
|
||||
(defn- delete-profiles!
|
||||
[{:keys [::conn ::min-age ::storage] :as cfg}]
|
||||
(letfn [(get-chunk [cursor]
|
||||
(let [rows (db/exec! conn [sql:get-profiles-chunk min-age cursor])]
|
||||
[(some->> rows peek :created-at) rows]))]
|
||||
(reduce
|
||||
(fn [total {:keys [id photo-id]}]
|
||||
(l/debug :hint "permanently delete profile" :id (str id))
|
||||
|
||||
;; Mark as deleted the storage object related with the
|
||||
;; photo-id field.
|
||||
(some->> photo-id (sto/touch-object! storage) deref)
|
||||
|
||||
;; And finally, permanently delete the profile.
|
||||
(db/delete! conn :profile {:id id})
|
||||
|
||||
(inc total))
|
||||
0
|
||||
(d/iteration get-chunk
|
||||
:vf second
|
||||
:kf first
|
||||
:initk (dt/now)))))
|
||||
|
||||
(def ^:private sql:get-teams-chunk
|
||||
"select id, photo_id, created_at from team
|
||||
where deleted_at is not null
|
||||
and deleted_at < now() - ?::interval
|
||||
and created_at < ?
|
||||
order by created_at desc
|
||||
limit 10
|
||||
for update skip locked")
|
||||
|
||||
(defn- delete-teams!
|
||||
[{:keys [::conn ::min-age ::storage] :as cfg}]
|
||||
(letfn [(get-chunk [cursor]
|
||||
(let [rows (db/exec! conn [sql:get-teams-chunk min-age cursor])]
|
||||
[(some->> rows peek :created-at) rows]))]
|
||||
(reduce
|
||||
(fn [total {:keys [id photo-id]}]
|
||||
(l/debug :hint "permanently delete team" :id (str id))
|
||||
|
||||
;; Mark as deleted the storage object related with the
|
||||
;; photo-id field.
|
||||
(some->> photo-id (sto/touch-object! storage) deref)
|
||||
|
||||
;; And finally, permanently delete the team.
|
||||
(db/delete! conn :team {:id id})
|
||||
|
||||
(inc total))
|
||||
0
|
||||
(d/iteration get-chunk
|
||||
:vf second
|
||||
:kf first
|
||||
:initk (dt/now)))))
|
||||
|
||||
|
||||
(def ^:private sql:get-orphan-teams-chunk
|
||||
"select t.id, t.created_at
|
||||
from team as t
|
||||
left join team_profile_rel as tpr
|
||||
on (t.id = tpr.team_id)
|
||||
where tpr.profile_id is null
|
||||
and t.created_at < ?
|
||||
order by t.created_at desc
|
||||
limit 10
|
||||
for update of t skip locked;")
|
||||
|
||||
(defn- delete-orphan-teams!
|
||||
"Find all orphan teams (with no members and mark them for
|
||||
deletion (soft delete)."
|
||||
[{:keys [::conn] :as cfg}]
|
||||
(letfn [(get-chunk [cursor]
|
||||
(let [rows (db/exec! conn [sql:get-orphan-teams-chunk cursor])]
|
||||
[(some->> rows peek :created-at) rows]))]
|
||||
(reduce
|
||||
(fn [total {:keys [id]}]
|
||||
(l/debug :hint "mark team for deletion" :id (str id))
|
||||
|
||||
;; And finally, permanently delete the team.
|
||||
(db/update! conn :team
|
||||
{:deleted-at (dt/now)}
|
||||
{:id id})
|
||||
|
||||
(inc total))
|
||||
0
|
||||
(d/iteration get-chunk
|
||||
:vf second
|
||||
:kf first
|
||||
:initk (dt/now)))))
|
||||
|
||||
(def ^:private sql:get-fonts-chunk
|
||||
"select id, created_at, woff1_file_id, woff2_file_id, otf_file_id, ttf_file_id
|
||||
from team_font_variant
|
||||
where deleted_at is not null
|
||||
and deleted_at < now() - ?::interval
|
||||
and created_at < ?
|
||||
order by created_at desc
|
||||
limit 10
|
||||
for update skip locked")
|
||||
|
||||
(defn- delete-fonts!
|
||||
[{:keys [::conn ::min-age ::storage] :as cfg}]
|
||||
(letfn [(get-chunk [cursor]
|
||||
(let [rows (db/exec! conn [sql:get-fonts-chunk min-age cursor])]
|
||||
[(some->> rows peek :created-at) rows]))]
|
||||
(reduce
|
||||
(fn [total {:keys [id] :as font}]
|
||||
(l/debug :hint "permanently delete font variant" :id (str id))
|
||||
|
||||
;; Mark as deleted the all related storage objects
|
||||
(some->> (:woff1-file-id font) (sto/touch-object! storage) deref)
|
||||
(some->> (:woff2-file-id font) (sto/touch-object! storage) deref)
|
||||
(some->> (:otf-file-id font) (sto/touch-object! storage) deref)
|
||||
(some->> (:ttf-file-id font) (sto/touch-object! storage) deref)
|
||||
|
||||
;; And finally, permanently delete the team font variant
|
||||
(db/delete! conn :team-font-variant {:id id})
|
||||
|
||||
(inc total))
|
||||
0
|
||||
(d/iteration get-chunk
|
||||
:vf second
|
||||
:kf first
|
||||
:initk (dt/now)))))
|
||||
|
||||
(def ^:private sql:get-projects-chunk
|
||||
"select id, created_at
|
||||
from project
|
||||
where deleted_at is not null
|
||||
and deleted_at < now() - ?::interval
|
||||
and created_at < ?
|
||||
order by created_at desc
|
||||
limit 10
|
||||
for update skip locked")
|
||||
|
||||
(defn- delete-projects!
|
||||
[{:keys [::conn ::min-age] :as cfg}]
|
||||
(letfn [(get-chunk [cursor]
|
||||
(let [rows (db/exec! conn [sql:get-projects-chunk min-age cursor])]
|
||||
[(some->> rows peek :created-at) rows]))]
|
||||
(reduce
|
||||
(fn [total {:keys [id]}]
|
||||
(l/debug :hint "permanently delete project" :id (str id))
|
||||
|
||||
;; And finally, permanently delete the project.
|
||||
(db/delete! conn :project {:id id})
|
||||
|
||||
(inc total))
|
||||
0
|
||||
(d/iteration get-chunk
|
||||
:vf second
|
||||
:kf first
|
||||
:initk (dt/now)))))
|
||||
|
||||
(def ^:private sql:get-files-chunk
|
||||
"select id, created_at
|
||||
from file
|
||||
where deleted_at is not null
|
||||
and deleted_at < now() - ?::interval
|
||||
and created_at < ?
|
||||
order by created_at desc
|
||||
limit 10
|
||||
for update skip locked")
|
||||
|
||||
(defn- delete-files!
|
||||
[{:keys [::conn ::min-age] :as cfg}]
|
||||
(letfn [(get-chunk [cursor]
|
||||
(let [rows (db/exec! conn [sql:get-files-chunk min-age cursor])]
|
||||
[(some->> rows peek :created-at) rows]))]
|
||||
(reduce
|
||||
(fn [total {:keys [id]}]
|
||||
(l/debug :hint "permanently delete file" :id (str id))
|
||||
|
||||
;; And finally, permanently delete the file.
|
||||
(db/delete! conn :file {:id id})
|
||||
|
||||
(inc total))
|
||||
0
|
||||
(d/iteration get-chunk
|
||||
:vf second
|
||||
:kf first
|
||||
:initk (dt/now)))))
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
(ns backend-tests.helpers
|
||||
(:require
|
||||
[app.auth]
|
||||
[app.common.data :as d]
|
||||
[app.common.flags :as flags]
|
||||
[app.common.pages :as cp]
|
||||
|
@ -17,6 +18,7 @@
|
|||
[app.main :as main]
|
||||
[app.media]
|
||||
[app.migrations]
|
||||
[app.rpc :as-alias rpc]
|
||||
[app.rpc.commands.auth :as cmd.auth]
|
||||
[app.rpc.commands.files :as files]
|
||||
[app.rpc.commands.files.create :as files.create]
|
||||
|
@ -101,8 +103,9 @@
|
|||
*pool* (:app.db/pool system)]
|
||||
(with-redefs [app.config/flags (flags/parse flags/default default-flags (:flags config))
|
||||
app.config/config config
|
||||
app.rpc.commands.auth/derive-password identity
|
||||
app.rpc.commands.auth/verify-password (fn [a b] {:valid (= a b)})]
|
||||
app.loggers.audit/submit! (constantly nil)
|
||||
app.auth/derive-password identity
|
||||
app.auth/verify-password (fn [a b] {:valid (= a b)})]
|
||||
(next)))
|
||||
(finally
|
||||
(ig/halt! system)))))
|
||||
|
@ -322,14 +325,21 @@
|
|||
(try-on! (method-fn (dissoc data ::type)))))
|
||||
|
||||
(defn mutation!
|
||||
[{:keys [::type] :as data}]
|
||||
[{:keys [::type profile-id] :as data}]
|
||||
(let [method-fn (get-in *system* [:app.rpc/methods :mutations type])]
|
||||
(try-on! (method-fn (dissoc data ::type)))))
|
||||
(try-on! (method-fn (-> data
|
||||
(dissoc ::type)
|
||||
(assoc ::rpc/profile-id profile-id)
|
||||
(d/without-nils))))))
|
||||
|
||||
(defn query!
|
||||
[{:keys [::type] :as data}]
|
||||
[{:keys [::type profile-id] :as data}]
|
||||
(let [method-fn (get-in *system* [:app.rpc/methods :queries type])]
|
||||
(try-on! (method-fn (dissoc data ::type)))))
|
||||
(try-on! (method-fn (-> data
|
||||
(dissoc ::type)
|
||||
(assoc ::rpc/profile-id profile-id)
|
||||
(d/without-nils))))))
|
||||
|
||||
|
||||
(defn run-task!
|
||||
([name]
|
||||
|
|
|
@ -65,8 +65,7 @@
|
|||
|
||||
;; Refresh webhook
|
||||
(let [whk' (th/db-get :webhook {:id (:id whk)})]
|
||||
(t/is (nil? (:error-code whk')))
|
||||
(prn whk'))
|
||||
(t/is (nil? (:error-code whk'))))
|
||||
|
||||
)))
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
[app.common.uuid :as uuid]
|
||||
[app.db :as db]
|
||||
[app.util.time :as dt]
|
||||
[app.rpc :as-alias rpc]
|
||||
[backend-tests.helpers :as th]
|
||||
[clojure.test :as t]))
|
||||
|
||||
|
@ -37,7 +38,7 @@
|
|||
|
||||
params {::th/type :push-audit-events
|
||||
:app.http/request http-request
|
||||
:profile-id (:id prof)
|
||||
::rpc/profile-id (:id prof)
|
||||
:events [{:name "navigate"
|
||||
:props {:project-id proj-id
|
||||
:team-id team-id
|
||||
|
@ -67,7 +68,7 @@
|
|||
|
||||
params {::th/type :push-audit-events
|
||||
:app.http/request http-request
|
||||
:profile-id (:id prof)
|
||||
::rpc/profile-id (:id prof)
|
||||
:events [{:name "navigate"
|
||||
:props {:project-id proj-id
|
||||
:team-id team-id
|
||||
|
|
|
@ -6,12 +6,13 @@
|
|||
|
||||
(ns backend-tests.rpc-cond-middleware-test
|
||||
(:require
|
||||
[backend-tests.storage-test :refer [configure-storage-backend]]
|
||||
[backend-tests.helpers :as th]
|
||||
[app.common.uuid :as uuid]
|
||||
[app.db :as db]
|
||||
[app.http :as http]
|
||||
[app.rpc :as-alias rpc]
|
||||
[app.rpc.cond :as cond]
|
||||
[backend-tests.helpers :as th]
|
||||
[backend-tests.storage-test :refer [configure-storage-backend]]
|
||||
[clojure.test :as t]
|
||||
[datoteka.core :as fs]))
|
||||
|
||||
|
@ -24,7 +25,9 @@
|
|||
:profile-id (:id profile)})
|
||||
file1 (th/create-file* 1 {:profile-id (:id profile)
|
||||
:project-id (:id project)})
|
||||
params {::th/type :get-file :id (:id file1) :profile-id (:id profile)}]
|
||||
params {::th/type :get-file
|
||||
:id (:id file1)
|
||||
::rpc/profile-id (:id profile)}]
|
||||
|
||||
(binding [cond/*enabled* true]
|
||||
(let [{:keys [error result]} (th/command! params)]
|
||||
|
|
|
@ -583,6 +583,7 @@
|
|||
:object-id (str page-id frame1-id)
|
||||
:data nil}
|
||||
{:keys [error result] :as out} (th/mutation! data)]
|
||||
;; (th/print-result! out)
|
||||
(t/is (nil? error))
|
||||
(t/is (nil? result)))
|
||||
|
||||
|
|
|
@ -6,12 +6,13 @@
|
|||
|
||||
(ns backend-tests.rpc-management-test
|
||||
(:require
|
||||
[backend-tests.storage-test :refer [configure-storage-backend]]
|
||||
[backend-tests.helpers :as th]
|
||||
[app.common.uuid :as uuid]
|
||||
[app.db :as db]
|
||||
[app.http :as http]
|
||||
[app.rpc :as-alias rpc]
|
||||
[app.storage :as sto]
|
||||
[backend-tests.helpers :as th]
|
||||
[backend-tests.storage-test :refer [configure-storage-backend]]
|
||||
[buddy.core.bytes :as b]
|
||||
[clojure.test :as t]
|
||||
[datoteka.core :as fs]))
|
||||
|
@ -50,7 +51,7 @@
|
|||
:object (select-keys mobj [:id :width :height :mtype :name])}]})
|
||||
|
||||
(let [data {::th/type :duplicate-file
|
||||
:profile-id (:id profile)
|
||||
::rpc/profile-id (:id profile)
|
||||
:file-id (:id file1)
|
||||
:name "file 1 (copy)"}
|
||||
out (th/command! data)]
|
||||
|
@ -122,7 +123,7 @@
|
|||
@(sto/del-object! storage sobject)
|
||||
|
||||
(let [data {::th/type :duplicate-file
|
||||
:profile-id (:id profile)
|
||||
::rpc/profile-id (:id profile)
|
||||
:file-id (:id file1)
|
||||
:name "file 1 (copy)"}
|
||||
out (th/command! data)]
|
||||
|
@ -184,7 +185,7 @@
|
|||
|
||||
|
||||
(let [data {::th/type :duplicate-project
|
||||
:profile-id (:id profile)
|
||||
::rpc/profile-id (:id profile)
|
||||
:project-id (:id project)
|
||||
:name "project 1 (copy)"}
|
||||
out (th/command! data)]
|
||||
|
@ -250,7 +251,7 @@
|
|||
(th/mark-file-deleted* {:id (:id file1)})
|
||||
|
||||
(let [data {::th/type :duplicate-project
|
||||
:profile-id (:id profile)
|
||||
::rpc/profile-id (:id profile)
|
||||
:project-id (:id project)
|
||||
:name "project 1 (copy)"}
|
||||
out (th/command! data)]
|
||||
|
@ -313,7 +314,7 @@
|
|||
|
||||
;; Try to move to same project
|
||||
(let [data {::th/type :move-files
|
||||
:profile-id (:id profile)
|
||||
::rpc/profile-id (:id profile)
|
||||
:project-id (:id project1)
|
||||
:ids #{(:id file1)}}
|
||||
|
||||
|
@ -333,7 +334,7 @@
|
|||
|
||||
;; move a file1 to project2 (in the same team)
|
||||
(let [data {::th/type :move-files
|
||||
:profile-id (:id profile)
|
||||
::rpc/profile-id (:id profile)
|
||||
:project-id (:id project2)
|
||||
:ids #{(:id file1)}}
|
||||
|
||||
|
@ -416,7 +417,7 @@
|
|||
|
||||
;; move to other project in other team
|
||||
(let [data {::th/type :move-files
|
||||
:profile-id (:id profile)
|
||||
::rpc/profile-id (:id profile)
|
||||
:project-id (:id project2)
|
||||
:ids #{(:id file1)}}
|
||||
out (th/command! data)]
|
||||
|
@ -489,7 +490,7 @@
|
|||
|
||||
;; move the library to other project
|
||||
(let [data {::th/type :move-files
|
||||
:profile-id (:id profile)
|
||||
::rpc/profile-id (:id profile)
|
||||
:project-id (:id project2)
|
||||
:ids #{(:id file2)}}
|
||||
out (th/command! data)]
|
||||
|
@ -575,7 +576,7 @@
|
|||
;; move project1 to other team
|
||||
;; TODO: correct team change of project
|
||||
(let [data {::th/type :move-project
|
||||
:profile-id (:id profile)
|
||||
::rpc/profile-id (:id profile)
|
||||
:project-id (:id project1)
|
||||
:team-id (:id team)}
|
||||
out (th/command! data)]
|
||||
|
@ -608,7 +609,7 @@
|
|||
(t/deftest clone-template
|
||||
(let [prof (th/create-profile* 1 {:is-active true})
|
||||
data {::th/type :clone-template
|
||||
:profile-id (:id prof)
|
||||
::rpc/profile-id (:id prof)
|
||||
:project-id (:default-project-id prof)
|
||||
:template-id "test"}
|
||||
|
||||
|
@ -624,7 +625,7 @@
|
|||
(t/deftest retrieve-list-of-buitin-templates
|
||||
(let [prof (th/create-profile* 1 {:is-active true})
|
||||
data {::th/type :retrieve-list-of-builtin-templates
|
||||
:profile-id (:id prof)}
|
||||
::rpc/profile-id (:id prof)}
|
||||
out (th/command! data)]
|
||||
;; (th/print-result! out)
|
||||
(t/is (nil? (:error out)))
|
||||
|
|
|
@ -146,7 +146,12 @@
|
|||
|
||||
;; execute permanent deletion task
|
||||
(let [result (th/run-task! :objects-gc {:min-age (dt/duration "-1m")})]
|
||||
(t/is (= 1 (:processed result))))
|
||||
(t/is (= 2 (:processed result))))
|
||||
|
||||
(let [row (th/db-get :team
|
||||
{:id (:default-team-id prof)}
|
||||
{:check-deleted? false})]
|
||||
(t/is (dt/instant? (:deleted-at row))))
|
||||
|
||||
;; query profile after delete
|
||||
(let [params {::th/type :profile
|
||||
|
|
|
@ -6,13 +6,14 @@
|
|||
|
||||
(ns backend-tests.rpc-team-test
|
||||
(:require
|
||||
[backend-tests.helpers :as th]
|
||||
[app.common.uuid :as uuid]
|
||||
[app.db :as db]
|
||||
[app.http :as http]
|
||||
[app.rpc :as-alias rpc]
|
||||
[app.storage :as sto]
|
||||
[app.tokens :as tokens]
|
||||
[app.util.time :as dt]
|
||||
[backend-tests.helpers :as th]
|
||||
[clojure.test :as t]
|
||||
[datoteka.core :as fs]
|
||||
[mockery.core :refer [with-mocks]]))
|
||||
|
@ -65,7 +66,7 @@
|
|||
|
||||
;; get invitation token
|
||||
(let [params {::th/type :get-team-invitation-token
|
||||
:profile-id (:id profile1)
|
||||
::rpc/profile-id (:id profile1)
|
||||
:team-id (:id team)
|
||||
:email "foo@bar.com"}
|
||||
out (th/command! params)]
|
||||
|
@ -214,7 +215,7 @@
|
|||
:role "editor"
|
||||
:valid-until (dt/in-future "48h")})
|
||||
|
||||
(let [data {::th/type :verify-token :token token :profile-id (:id profile2)}
|
||||
(let [data {::th/type :verify-token :token token ::rpc/profile-id (:id profile2)}
|
||||
out (th/command! data)]
|
||||
;; (th/print-result! out)
|
||||
(t/is (th/success? out))
|
||||
|
@ -235,7 +236,7 @@
|
|||
:role "editor"
|
||||
:valid-until (dt/in-future "48h")})
|
||||
|
||||
(let [data {::th/type :verify-token :token token :profile-id (:id profile1)}
|
||||
(let [data {::th/type :verify-token :token token ::rpc/profile-id (:id profile1)}
|
||||
out (th/command! data)]
|
||||
;; (th/print-result! out)
|
||||
(t/is (not (th/success? out)))
|
||||
|
|
|
@ -100,6 +100,7 @@
|
|||
out (th/query! data)]
|
||||
|
||||
;; (th/print-result! out)
|
||||
(t/is (nil? (:error out)))
|
||||
(let [result (:result out)]
|
||||
(t/is (contains? result :file))
|
||||
(t/is (contains? result :project)))))
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
[app.db :as db]
|
||||
[app.http :as http]
|
||||
[app.storage :as sto]
|
||||
[app.rpc :as-alias rpc]
|
||||
[backend-tests.helpers :as th]
|
||||
[clojure.test :as t]
|
||||
[mockery.core :refer [with-mocks]]))
|
||||
|
@ -28,7 +29,7 @@
|
|||
|
||||
(t/testing "create webhook"
|
||||
(let [params {::th/type :create-webhook
|
||||
:profile-id (:id prof)
|
||||
::rpc/profile-id (:id prof)
|
||||
:team-id team-id
|
||||
:uri "http://example.com"
|
||||
:mtype "application/json"}
|
||||
|
@ -54,7 +55,7 @@
|
|||
|
||||
(t/testing "update webhook 1 (success)"
|
||||
(let [params {::th/type :update-webhook
|
||||
:profile-id (:id prof)
|
||||
::rpc/profile-id (:id prof)
|
||||
:id (:id @whook)
|
||||
:uri (:uri @whook)
|
||||
:mtype "application/transit+json"
|
||||
|
@ -82,7 +83,7 @@
|
|||
|
||||
(t/testing "update webhook 2 (change uri)"
|
||||
(let [params {::th/type :update-webhook
|
||||
:profile-id (:id prof)
|
||||
::rpc/profile-id (:id prof)
|
||||
:id (:id @whook)
|
||||
:uri (str (:uri @whook) "/test")
|
||||
:mtype "application/transit+json"
|
||||
|
@ -97,7 +98,7 @@
|
|||
|
||||
(t/testing "update webhook 3 (not authorized)"
|
||||
(let [params {::th/type :update-webhook
|
||||
:profile-id uuid/zero
|
||||
::rpc/profile-id uuid/zero
|
||||
:id (:id @whook)
|
||||
:uri (str (:uri @whook) "/test")
|
||||
:mtype "application/transit+json"
|
||||
|
@ -115,7 +116,7 @@
|
|||
|
||||
(t/testing "delete webhook (success)"
|
||||
(let [params {::th/type :delete-webhook
|
||||
:profile-id (:id prof)
|
||||
::rpc/profile-id (:id prof)
|
||||
:id (:id @whook)}
|
||||
out (th/command! params)]
|
||||
|
||||
|
@ -128,7 +129,7 @@
|
|||
|
||||
(t/testing "delete webhook (unauthorozed)"
|
||||
(let [params {::th/type :delete-webhook
|
||||
:profile-id uuid/zero
|
||||
::rpc/profile-id uuid/zero
|
||||
:id (:id @whook)}
|
||||
out (th/command! params)]
|
||||
|
||||
|
@ -149,7 +150,7 @@
|
|||
(let [prof (th/create-profile* 1 {:is-active true})
|
||||
team-id (:default-team-id prof)
|
||||
params {::th/type :create-webhook
|
||||
:profile-id (:id prof)
|
||||
::rpc/profile-id (:id prof)
|
||||
:team-id team-id
|
||||
:uri "http://example.com"
|
||||
:mtype "application/json"}
|
||||
|
|
|
@ -12,8 +12,7 @@
|
|||
(def default
|
||||
"A common flags that affects both: backend and frontend."
|
||||
[:enable-registration
|
||||
:enable-login
|
||||
:enable-webhooks])
|
||||
:enable-login-with-password])
|
||||
|
||||
(defn parse
|
||||
[& flags]
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
[lambdaisland.uri :as luri]
|
||||
[linked.core :as lk]
|
||||
[linked.set :as lks]
|
||||
#?(:clj [datoteka.fs :as fs])
|
||||
#?(:cljs ["luxon" :as lxn]))
|
||||
#?(:clj
|
||||
(:import
|
||||
|
@ -22,6 +23,7 @@
|
|||
java.io.ByteArrayInputStream
|
||||
java.io.ByteArrayOutputStream
|
||||
java.io.File
|
||||
java.nio.file.Path
|
||||
java.time.Duration
|
||||
java.time.Instant
|
||||
java.time.OffsetDateTime
|
||||
|
@ -102,11 +104,15 @@
|
|||
;; --- HANDLERS
|
||||
|
||||
(add-handlers!
|
||||
#?(:clj
|
||||
{:id "file"
|
||||
:class File
|
||||
:wfn str
|
||||
:rfn identity})
|
||||
#?@(:clj
|
||||
[{:id "file"
|
||||
:class File
|
||||
:wfn str
|
||||
:rfn identity}
|
||||
{:id "path"
|
||||
:class Path
|
||||
:wfn str
|
||||
:rfn fs/path}])
|
||||
|
||||
#?(:cljs
|
||||
{:id "n"
|
||||
|
|
|
@ -113,6 +113,10 @@ http {
|
|||
proxy_pass http://127.0.0.1:6060/api;
|
||||
}
|
||||
|
||||
location /admin {
|
||||
proxy_pass http://127.0.0.1:6063/admin;
|
||||
}
|
||||
|
||||
location /webhooks {
|
||||
proxy_pass http://127.0.0.1:6060/webhooks;
|
||||
}
|
||||
|
|
|
@ -1,11 +1,68 @@
|
|||
FROM ubuntu:22.04 as jre-build
|
||||
|
||||
ENV DEBIAN_FRONTEND=noninteractive \
|
||||
TZ=Etc/UTC
|
||||
|
||||
RUN set -eux; \
|
||||
apt-get -qq update; \
|
||||
apt-get -qqy --no-install-recommends install \
|
||||
curl \
|
||||
ca-certificates \
|
||||
binutils \
|
||||
; \
|
||||
rm -rf /var/lib/apt/lists/*;
|
||||
|
||||
RUN set -eux; \
|
||||
ARCH="$(dpkg --print-architecture)"; \
|
||||
case "${ARCH}" in \
|
||||
aarch64|arm64) \
|
||||
ESUM='262be608e266fd76d7496af83b2832be853c3aaf7460d6a4da198cd40db74553'; \
|
||||
BINARY_URL='https://github.com/adoptium/temurin18-binaries/releases/download/jdk-18.0.2.1%2B1/OpenJDK18U-jdk_aarch64_linux_hotspot_18.0.2.1_1.tar.gz'; \
|
||||
;; \
|
||||
armhf|armv7l) \
|
||||
ESUM='4cd49b92d13847bfad7b3bf635cca349e2c89c7641748c5288bc40d612cdbbd6'; \
|
||||
BINARY_URL='https://github.com/adoptium/temurin18-binaries/releases/download/jdk-18.0.2.1%2B1/OpenJDK18U-jdk_arm_linux_hotspot_18.0.2.1_1.tar.gz'; \
|
||||
;; \
|
||||
amd64|x86_64) \
|
||||
ESUM='7d6beba8cfc0a8347f278f7414351191a95a707d46b6586e9a786f2669af0f8b'; \
|
||||
BINARY_URL='https://github.com/adoptium/temurin18-binaries/releases/download/jdk-18.0.2.1%2B1/OpenJDK18U-jdk_x64_linux_hotspot_18.0.2.1_1.tar.gz'; \
|
||||
;; \
|
||||
*) \
|
||||
echo "Unsupported arch: ${ARCH}"; \
|
||||
exit 1; \
|
||||
;; \
|
||||
esac; \
|
||||
curl -LfsSo /tmp/openjdk.tar.gz ${BINARY_URL}; \
|
||||
echo "${ESUM} */tmp/openjdk.tar.gz" | sha256sum -c -; \
|
||||
mkdir -p /opt/jdk; \
|
||||
cd /opt/jdk; \
|
||||
tar -xf /tmp/openjdk.tar.gz --strip-components=1; \
|
||||
rm -rf /tmp/openjdk.tar.gz;
|
||||
|
||||
RUN /opt/jdk/bin/jlink \
|
||||
--verbose \
|
||||
--module-path /opt/jdk/jmods \
|
||||
--strip-debug \
|
||||
--no-man-pages \
|
||||
--no-header-files \
|
||||
--compress 0 \
|
||||
--add-modules java.base,java.naming,java.xml,java.logging,java.net.http,java.sql,java.management,java.desktop,jdk.jfr,jdk.unsupported,jdk.management.jfr \
|
||||
--output /opt/jre
|
||||
|
||||
|
||||
FROM ubuntu:22.04
|
||||
|
||||
LABEL maintainer="Andrey Antukh <niwi@niwi.nz>"
|
||||
ENV LANG='en_US.UTF-8' \
|
||||
LC_ALL='en_US.UTF-8' \
|
||||
JAVA_HOME="/opt/jre" \
|
||||
PATH=/opt/jre/bin:$PATH \
|
||||
TZ=Etc/UTC
|
||||
|
||||
ENV LANG='en_US.UTF-8' LC_ALL='en_US.UTF-8'
|
||||
|
||||
WORKDIR /root
|
||||
COPY --from=jre-build /opt/jre /opt/jre
|
||||
|
||||
RUN set -ex; \
|
||||
useradd -U -M -u 1001 -s /bin/false -d /opt/penpot penpot; \
|
||||
apt-get -qq update; \
|
||||
apt-get -qqy --no-install-recommends install \
|
||||
curl \
|
||||
|
@ -23,34 +80,8 @@ RUN set -ex; \
|
|||
locale-gen; \
|
||||
rm -rf /var/lib/apt/lists/*;
|
||||
|
||||
RUN set -eux; \
|
||||
ARCH="$(dpkg --print-architecture)"; \
|
||||
case "${ARCH}" in \
|
||||
aarch64|arm64) \
|
||||
ESUM='37ceaf232a85cce46bcccfd71839854e8b14bf3160e7ef72a676b9cae45ee8af'; \
|
||||
BINARY_URL='https://github.com/adoptium/temurin18-binaries/releases/download/jdk-18.0.1%2B10/OpenJDK18U-jdk_aarch64_linux_hotspot_18.0.1_10.tar.gz'; \
|
||||
;; \
|
||||
armhf|armv7l) \
|
||||
ESUM='0ddec3c165ab0b662a57a845db3fdaeb840660b493f164696b03df76aadf61c8'; \
|
||||
BINARY_URL='https://github.com/adoptium/temurin18-binaries/releases/download/jdk-18.0.1%2B10/OpenJDK18U-jdk_arm_linux_hotspot_18.0.1_10.tar.gz'; \
|
||||
;; \
|
||||
amd64|x86_64) \
|
||||
ESUM='16b1d9d75f22c157af04a1fd9c664324c7f4b5163c022b382a2f2e8897c1b0a2'; \
|
||||
BINARY_URL='https://github.com/adoptium/temurin18-binaries/releases/download/jdk-18.0.1%2B10/OpenJDK18U-jdk_x64_linux_hotspot_18.0.1_10.tar.gz'; \
|
||||
;; \
|
||||
*) \
|
||||
echo "Unsupported arch: ${ARCH}"; \
|
||||
exit 1; \
|
||||
;; \
|
||||
esac; \
|
||||
curl -LfsSo /tmp/openjdk.tar.gz ${BINARY_URL}; \
|
||||
echo "${ESUM} */tmp/openjdk.tar.gz" | sha256sum -c -; \
|
||||
mkdir -p /usr/lib/jvm/openjdk; \
|
||||
cd /usr/lib/jvm/openjdk; \
|
||||
tar -xf /tmp/openjdk.tar.gz --strip-components=1; \
|
||||
rm -rf /tmp/openjdk.tar.gz;
|
||||
COPY --chown=penpot:penpot ./bundle-backend/ /opt/penpot/backend/
|
||||
|
||||
ENV JAVA_HOME=/usr/lib/jvm/openjdk PATH="/usr/lib/jvm/openjdk/bin:$PATH"
|
||||
ADD ./bundle-backend/ /opt/penpot/backend/
|
||||
USER penpot:penpot
|
||||
WORKDIR /opt/penpot/backend
|
||||
CMD ["/bin/bash", "run.sh"]
|
||||
|
|
|
@ -1,70 +1,76 @@
|
|||
FROM ubuntu:22.04
|
||||
LABEL maintainer="Andrey Antukh <niwi@niwi.nz>"
|
||||
|
||||
ARG DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
ENV LANG=en_US.UTF-8 \
|
||||
LC_ALL=en_US.UTF-8 \
|
||||
NODE_VERSION=v16.17.0
|
||||
NODE_VERSION=v18.12.1 \
|
||||
DEBIAN_FRONTEND=noninteractive \
|
||||
PATH=/opt/node/bin:$PATH
|
||||
|
||||
RUN set -ex; \
|
||||
useradd -U -M -u 1001 -s /bin/false -d /opt/penpot penpot; \
|
||||
mkdir -p /etc/resolvconf/resolv.conf.d; \
|
||||
echo "nameserver 8.8.8.8" > /etc/resolvconf/resolv.conf.d/tail; \
|
||||
echo "nameserver 127.0.0.11" > /etc/resolvconf/resolv.conf.d/tail; \
|
||||
apt-get -qq update; \
|
||||
apt-get -qqy --no-install-recommends install curl tzdata locales ca-certificates fontconfig xz-utils; \
|
||||
apt-get -qqy --no-install-recommends install \
|
||||
curl \
|
||||
tzdata \
|
||||
locales \
|
||||
ca-certificates \
|
||||
fontconfig \
|
||||
xz-utils \
|
||||
; \
|
||||
rm -rf /var/lib/apt/lists/*; \
|
||||
echo "en_US.UTF-8 UTF-8" >> /etc/locale.gen; \
|
||||
locale-gen; \
|
||||
rm -rf /var/lib/apt/lists/*;
|
||||
locale-gen;
|
||||
|
||||
RUN set -ex; \
|
||||
apt-get -qq update; \
|
||||
apt-get -qqy install \
|
||||
imagemagick \
|
||||
ghostscript \
|
||||
netpbm \
|
||||
poppler-utils \
|
||||
potrace \
|
||||
gconf-service \
|
||||
libasound2 \
|
||||
libatk1.0-0 \
|
||||
libatk-bridge2.0-0 \
|
||||
libatomic1 \
|
||||
libcairo2 \
|
||||
libcups2 \
|
||||
libdbus-1-3 \
|
||||
libexpat1 \
|
||||
libfontconfig1 \
|
||||
libgcc1 \
|
||||
libgconf-2-4 \
|
||||
libgdk-pixbuf2.0-0 \
|
||||
libglib2.0-0 \
|
||||
libgtk-3-0 \
|
||||
libnspr4 \
|
||||
libpango-1.0-0 \
|
||||
libpangocairo-1.0-0 \
|
||||
libx11-6 \
|
||||
libx11-xcb1 \
|
||||
libxcb1 \
|
||||
libxcb-dri3-0 \
|
||||
libxcomposite1 \
|
||||
libxcursor1 \
|
||||
libxdamage1 \
|
||||
libxext6 \
|
||||
libxfixes3 \
|
||||
libxi6 \
|
||||
libxrandr2 \
|
||||
libxrender1 \
|
||||
libxshmfence1 \
|
||||
libxss1 \
|
||||
libxtst6 \
|
||||
fonts-liberation \
|
||||
libnss3 \
|
||||
libgbm1 \
|
||||
imagemagick \
|
||||
ghostscript \
|
||||
netpbm \
|
||||
poppler-utils \
|
||||
potrace \
|
||||
gconf-service \
|
||||
libasound2 \
|
||||
libatk1.0-0 \
|
||||
libatk-bridge2.0-0 \
|
||||
libatomic1 \
|
||||
libcairo2 \
|
||||
libcups2 \
|
||||
libdbus-1-3 \
|
||||
libexpat1 \
|
||||
libfontconfig1 \
|
||||
libgcc1 \
|
||||
libgconf-2-4 \
|
||||
libgdk-pixbuf2.0-0 \
|
||||
libglib2.0-0 \
|
||||
libgtk-3-0 \
|
||||
libnspr4 \
|
||||
libpango-1.0-0 \
|
||||
libpangocairo-1.0-0 \
|
||||
libx11-6 \
|
||||
libx11-xcb1 \
|
||||
libxcb1 \
|
||||
libxcb-dri3-0 \
|
||||
libxcomposite1 \
|
||||
libxcursor1 \
|
||||
libxdamage1 \
|
||||
libxext6 \
|
||||
libxfixes3 \
|
||||
libxi6 \
|
||||
libxrandr2 \
|
||||
libxrender1 \
|
||||
libxshmfence1 \
|
||||
libxss1 \
|
||||
libxtst6 \
|
||||
fonts-liberation \
|
||||
libnss3 \
|
||||
libgbm1 \
|
||||
; \
|
||||
rm -rf /var/lib/apt/lists/*;
|
||||
|
||||
ENV PATH="/usr/local/nodejs/bin:$PATH"
|
||||
|
||||
RUN set -eux; \
|
||||
ARCH="$(dpkg --print-architecture)"; \
|
||||
case "${ARCH}" in \
|
||||
|
@ -83,19 +89,22 @@ RUN set -eux; \
|
|||
;; \
|
||||
esac; \
|
||||
curl -LfsSo /tmp/nodejs.tar.xz ${BINARY_URL}; \
|
||||
mkdir -p /usr/local/nodejs; \
|
||||
cd /usr/local/nodejs; \
|
||||
mkdir -p /opt/node; \
|
||||
cd /opt/node; \
|
||||
tar -xf /tmp/nodejs.tar.xz --strip-components=1; \
|
||||
chown -R root /usr/local/nodejs; \
|
||||
/usr/local/nodejs/bin/npm install -g yarn; \
|
||||
rm -rf /tmp/nodejs.tar.xz;
|
||||
chown -R root /opt/node; \
|
||||
npm install -g yarn; \
|
||||
rm -rf /tmp/nodejs.tar.xz; \
|
||||
mkdir -p /opt/penpot; \
|
||||
chown -R penpot:penpot /opt/penpot;
|
||||
|
||||
WORKDIR /opt/app
|
||||
ADD --chown=penpot:penpot ./bundle-exporter/ /opt/penpot/exporter
|
||||
|
||||
ADD ./bundle-exporter/ /opt/app/
|
||||
WORKDIR /opt/penpot/exporter
|
||||
USER penpot:penpot
|
||||
|
||||
RUN set -ex; \
|
||||
yarn; \
|
||||
npx playwright install chromium;
|
||||
yarn run playwright install chromium;
|
||||
|
||||
CMD ["/usr/local/nodejs/bin/node", "app.js"]
|
||||
CMD ["node", "app.js"]
|
||||
|
|
|
@ -1,96 +0,0 @@
|
|||
## Should be set to the public domain where penpot is going to be served.
|
||||
##
|
||||
## NOTE: If you are going to serve it under different domain than
|
||||
## 'localhost' without HTTPS, consider setting the
|
||||
## `disable-secure-session-cookies' flag on the 'PENPOT_FLAGS'
|
||||
## setting.
|
||||
|
||||
PENPOT_PUBLIC_URI=http://localhost:9001
|
||||
|
||||
## Feature flags.
|
||||
PENPOT_FLAGS=enable-registration enable-login disable-email-verification
|
||||
|
||||
## Temporal workaround because of bad builtin default
|
||||
|
||||
PENPOT_HTTP_SERVER_HOST=0.0.0.0
|
||||
|
||||
## Standard database connection parameters (only postgresql is supported):
|
||||
|
||||
PENPOT_DATABASE_URI=postgresql://penpot-postgres/penpot
|
||||
PENPOT_DATABASE_USERNAME=penpot
|
||||
PENPOT_DATABASE_PASSWORD=penpot
|
||||
|
||||
## Redis is used for the websockets notifications.
|
||||
|
||||
PENPOT_REDIS_URI=redis://penpot-redis/0
|
||||
|
||||
## By default, files uploaded by users are stored in local
|
||||
## filesystem. But it can be configured to store in AWS S3.
|
||||
|
||||
PENPOT_ASSETS_STORAGE_BACKEND=assets-fs
|
||||
PENPOT_STORAGE_ASSETS_FS_DIRECTORY=/opt/data/assets
|
||||
|
||||
## Telemetry. When enabled, a periodical process will send anonymous
|
||||
## data about this instance. Telemetry data will enable us to learn on
|
||||
## how the application is used, based on real scenarios. If you want
|
||||
## to help us, please leave it enabled.
|
||||
|
||||
PENPOT_TELEMETRY_ENABLED=true
|
||||
|
||||
## Email sending configuration. By default, emails are printed in the
|
||||
## console, but for production usage is recommended to setup a real
|
||||
## SMTP provider. Emails are used to confirm user registrations.
|
||||
|
||||
PENPOT_SMTP_DEFAULT_FROM=no-reply@example.com
|
||||
PENPOT_SMTP_DEFAULT_REPLY_TO=no-reply@example.com
|
||||
# PENPOT_SMTP_HOST=
|
||||
# PENPOT_SMTP_PORT=
|
||||
# PENPOT_SMTP_USERNAME=
|
||||
# PENPOT_SMTP_PASSWORD=
|
||||
# PENPOT_SMTP_TLS=true
|
||||
# PENPOT_SMTP_SSL=false
|
||||
|
||||
## Comma separated list of allowed domains to register. Empty to allow
|
||||
## all.
|
||||
|
||||
# PENPOT_REGISTRATION_DOMAIN_WHITELIST=""
|
||||
|
||||
## Authentication providers
|
||||
|
||||
## Google
|
||||
|
||||
# PENPOT_GOOGLE_CLIENT_ID=
|
||||
# PENPOT_GOOGLE_CLIENT_SECRET=
|
||||
|
||||
## GitHub
|
||||
|
||||
# PENPOT_GITHUB_CLIENT_ID=
|
||||
# PENPOT_GITHUB_CLIENT_SECRET=
|
||||
|
||||
## GitLab
|
||||
|
||||
# PENPOT_GITLAB_BASE_URI=https://gitlab.com
|
||||
# PENPOT_GITLAB_CLIENT_ID=
|
||||
# PENPOT_GITLAB_CLIENT_SECRET=
|
||||
|
||||
## OpenID Connect (since 1.5.0)
|
||||
|
||||
# PENPOT_OIDC_BASE_URI=
|
||||
# PENPOT_OIDC_CLIENT_ID=
|
||||
# PENPOT_OIDC_CLIENT_SECRET=
|
||||
|
||||
## LDAP
|
||||
##
|
||||
## NOTE: to enable ldap, you will need to put 'enable-login-with-ldap'
|
||||
## on the 'PENPOT_FLAGS' environment variable.
|
||||
|
||||
# PENPOT_LDAP_HOST=ldap
|
||||
# PENPOT_LDAP_PORT=10389
|
||||
# PENPOT_LDAP_SSL=false
|
||||
# PENPOT_LDAP_STARTTLS=false
|
||||
# PENPOT_LDAP_BASE_DN=ou=people,dc=planetexpress,dc=com
|
||||
# PENPOT_LDAP_BIND_DN=cn=admin,dc=planetexpress,dc=com
|
||||
# PENPOT_LDAP_BIND_PASSWORD=GoodNewsEveryone
|
||||
# PENPOT_LDAP_ATTRS_USERNAME=uid
|
||||
# PENPOT_LDAP_ATTRS_EMAIL=mail
|
||||
# PENPOT_LDAP_ATTRS_FULLNAME=cn
|
|
@ -5,20 +5,43 @@ networks:
|
|||
penpot:
|
||||
|
||||
volumes:
|
||||
penpot_postgres_data:
|
||||
penpot_assets_data:
|
||||
penpot_postgres_v15:
|
||||
penpot_assets:
|
||||
# penpot_traefik:
|
||||
# penpot_minio:
|
||||
|
||||
services:
|
||||
## Traefik service declaration example. Consider using it if you are
|
||||
## going to expose penpot to the internet or different host than
|
||||
## `localhost`.
|
||||
|
||||
# traefik:
|
||||
# image: traefik:v2.9
|
||||
# networks:
|
||||
# - penpot
|
||||
# command:
|
||||
# - "--api.insecure=true"
|
||||
# - "--entryPoints.web.address=:80"
|
||||
# - "--providers.docker=true"
|
||||
# - "--providers.docker.exposedbydefault=false"
|
||||
# - "--entryPoints.websecure.address=:443"
|
||||
# - "--certificatesresolvers.letsencrypt.acme.tlschallenge=true"
|
||||
# - "--certificatesresolvers.letsencrypt.acme.email=<EMAIL_ADDRESS>"
|
||||
# - "--certificatesresolvers.letsencrypt.acme.storage=/traefik/acme.json"
|
||||
# volumes:
|
||||
# - "penpot_traefik:/traefik"
|
||||
# - "/var/run/docker.sock:/var/run/docker.sock"
|
||||
# ports:
|
||||
# - "80:80"
|
||||
# - "443:443"
|
||||
|
||||
penpot-frontend:
|
||||
image: "penpotapp/frontend:latest"
|
||||
ports:
|
||||
- 9001:80
|
||||
|
||||
volumes:
|
||||
- penpot_assets_data:/opt/data
|
||||
|
||||
env_file:
|
||||
- config.env
|
||||
- penpot_assets:/opt/data
|
||||
|
||||
depends_on:
|
||||
- penpot-backend
|
||||
|
@ -27,51 +50,250 @@ services:
|
|||
networks:
|
||||
- penpot
|
||||
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
|
||||
## HTTP: example of labels for the case if you are going to
|
||||
## expose penpot to the internet using only HTTP (without HTTPS)
|
||||
## with traefik
|
||||
|
||||
# - "traefik.http.routers.penpot-http.entrypoints=web"
|
||||
# - "traefik.http.routers.penpot-http.rule=Host(`<DOMAIN_NAME>`)"
|
||||
# - "traefik.http.services.penpot-http.loadbalancer.server.port=80"
|
||||
|
||||
## HTTPS: example of labels for the case if you are going to
|
||||
## expose penpot to the internet using with HTTPS using traefik
|
||||
|
||||
# - "traefik.http.middlewares.http-redirect.redirectscheme.scheme=https"
|
||||
# - "traefik.http.middlewares.http-redirect.redirectscheme.permanent=true"
|
||||
# - "traefik.http.routers.penpot-http.entrypoints=web"
|
||||
# - "traefik.http.routers.penpot-http.rule=Host(`<DOMAIN_NAME>`)"
|
||||
# - "traefik.http.routers.penpot-http.middlewares=http-redirect"
|
||||
# - "traefik.http.routers.penpot-https.entrypoints=websecure"
|
||||
# - "traefik.http.routers.penpot-https.rule=Host(`<DOMAIN_NAME>`)"
|
||||
# - "traefik.http.services.penpot-https.loadbalancer.server.port=80"
|
||||
# - "traefik.http.routers.penpot-https.tls=true"
|
||||
# - "traefik.http.routers.penpot-https.tls.certresolver=letsencrypt"
|
||||
|
||||
## Configuration envronment variables for frontend the
|
||||
## container. In this case this container only needs the
|
||||
## `PENPOT_FLAGS`. This environment variable is shared with other
|
||||
## services but not all flags are relevant to all services.
|
||||
##
|
||||
## Relevant flags for frontend:
|
||||
## - demo-users
|
||||
## - login-with-github
|
||||
## - login-with-gitlab
|
||||
## - login-with-google
|
||||
## - login-with-ldap
|
||||
## - login-with-oidc
|
||||
## - login-with-password
|
||||
## - registration
|
||||
## - webhooks
|
||||
##
|
||||
## You can read more about all available flags on:
|
||||
## https://help.penpot.app/technical-guide/configuration/#advanced-configuration
|
||||
|
||||
environment:
|
||||
- PENPOT_FLAGS=enable-registration enable-login-with-password
|
||||
|
||||
penpot-backend:
|
||||
image: "penpotapp/backend:latest"
|
||||
volumes:
|
||||
- penpot_assets_data:/opt/data
|
||||
- penpot_assets:/opt/data
|
||||
|
||||
depends_on:
|
||||
- penpot-postgres
|
||||
- penpot-redis
|
||||
|
||||
env_file:
|
||||
- config.env
|
||||
|
||||
networks:
|
||||
- penpot
|
||||
|
||||
## Configuration envronment variables for backend the
|
||||
## container.
|
||||
##
|
||||
## Relevant flags for backend:
|
||||
## - demo-users
|
||||
## - email-verification
|
||||
## - log-emails
|
||||
## - log-invitation-tokens
|
||||
## - login-with-github
|
||||
## - login-with-gitlab
|
||||
## - login-with-google
|
||||
## - login-with-ldap
|
||||
## - login-with-oidc
|
||||
## - login-with-password
|
||||
## - registration
|
||||
## - secure-session-cookies
|
||||
## - smtp
|
||||
## - smtp-debug
|
||||
## - telemetry
|
||||
## - webhooks
|
||||
##
|
||||
## You can read more about all available flags and other
|
||||
## environment variables for the backend here:
|
||||
## https://help.penpot.app/technical-guide/configuration/#advanced-configuration
|
||||
|
||||
environment:
|
||||
- PENPOT_FLAGS=enable-registration enable-login disable-email-verification enable-smtp
|
||||
|
||||
## Setup initial administration user, uncommit only if you are
|
||||
## going to use the penpot-admin; Once uncommented, the special
|
||||
## user will be created on application start. This user can only
|
||||
## be used for access admin, you will not be able to login with
|
||||
## it on penpot application.
|
||||
|
||||
# - PENPOT_SETUP_ADMIN_EMAIL=admin@example.com
|
||||
# - PENPOT_SETUP_ADMIN_PASSWORD=password
|
||||
|
||||
## Public URI. If you are going to expose this instance to the
|
||||
## internet, or use it under different domain than 'localhost'
|
||||
## consider using traefik and set the
|
||||
## `disable-secure-session-cookies` if you are not going to
|
||||
## serve penpot under HTTPS.
|
||||
|
||||
- PENPOT_PUBLIC_URI=http://localhost:9001
|
||||
|
||||
## Database connection parameters. Don't touch them unless you
|
||||
## are using custom postgresql connection parameters
|
||||
|
||||
- PENPOT_DATABASE_URI=postgresql://penpot-postgres/penpot
|
||||
- PENPOT_DATABASE_USERNAME=penpot
|
||||
- PENPOT_DATABASE_PASSWORD=penpot
|
||||
|
||||
## Redis is used for the websockets notifications. Don't touch
|
||||
## unless the redis container has different parameters or
|
||||
## different name.
|
||||
|
||||
- PENPOT_REDIS_URI=redis://penpot-redis/0
|
||||
|
||||
## Default configuration for assets storage: using filesystem
|
||||
## based with all files stored in a docker volume.
|
||||
|
||||
- PENPOT_ASSETS_STORAGE_BACKEND=assets-fs
|
||||
- PENPOT_STORAGE_ASSETS_FS_DIRECTORY=/opt/data/assets
|
||||
|
||||
## Also can be configured to to use a S3 compatible storage
|
||||
## service like MiniIO. Look below for minio service setup.
|
||||
|
||||
# - AWS_ACCESS_KEY_ID=<KEY_ID>
|
||||
# - AWS_SECRET_ACCESS_KEY=<ACCESS_KEY>
|
||||
# - PENPOT_ASSETS_STORAGE_BACKEND=assets-s3
|
||||
# - PENPOT_STORAGE_ASSETS_S3_ENDPOINT=http://penpot-minio:9000
|
||||
# - PENPOT_STORAGE_ASSETS_S3_BUCKET=<BUKET_NAME>
|
||||
|
||||
## Telemetry. When enabled, a periodical process will send
|
||||
## anonymous data about this instance. Telemetry data will
|
||||
## enable us to learn on how the application is used, based on
|
||||
## real scenarios. If you want to help us, please leave it
|
||||
## enabled. You can audit what data we send with the code
|
||||
## available on github
|
||||
- PENPOT_TELEMETRY_ENABLED=true
|
||||
|
||||
## Example SMTP/Email configuration. By default, emails are sent
|
||||
## to the mailcatch service, but for production usage is
|
||||
## recommended to setup a real SMTP provider. Emails are used to
|
||||
## confirm user registrations & invitations. Look below how
|
||||
## mailcatch service is configured.
|
||||
- PENPOT_SMTP_DEFAULT_FROM=no-reply@example.com
|
||||
- PENPOT_SMTP_DEFAULT_REPLY_TO=no-reply@example.com
|
||||
- PENPOT_SMTP_HOST=penpot-mailcatch
|
||||
- PENPOT_SMTP_PORT=1025
|
||||
- PENPOT_SMTP_USERNAME=
|
||||
- PENPOT_SMTP_PASSWORD=
|
||||
- PENPOT_SMTP_TLS=false
|
||||
- PENPOT_SMTP_SSL=false
|
||||
|
||||
penpot-exporter:
|
||||
image: "penpotapp/exporter:latest"
|
||||
env_file:
|
||||
- config.env
|
||||
networks:
|
||||
- penpot
|
||||
|
||||
environment:
|
||||
# Don't touch it; this uses internal docker network to
|
||||
# communicate with the frontend.
|
||||
- PENPOT_PUBLIC_URI=http://penpot-frontend
|
||||
networks:
|
||||
- penpot
|
||||
|
||||
## Redis is used for the websockets notifications.
|
||||
- PENPOT_REDIS_URI=redis://penpot-redis/0
|
||||
|
||||
penpot-postgres:
|
||||
image: "postgres:14"
|
||||
image: "postgres:15"
|
||||
restart: always
|
||||
stop_signal: SIGINT
|
||||
|
||||
volumes:
|
||||
- penpot_postgres_v15:/var/lib/postgresql/data
|
||||
|
||||
networks:
|
||||
- penpot
|
||||
|
||||
environment:
|
||||
- POSTGRES_INITDB_ARGS=--data-checksums
|
||||
- POSTGRES_DB=penpot
|
||||
- POSTGRES_USER=penpot
|
||||
- POSTGRES_PASSWORD=penpot
|
||||
|
||||
volumes:
|
||||
- penpot_postgres_data:/var/lib/postgresql/data
|
||||
|
||||
networks:
|
||||
- penpot
|
||||
|
||||
penpot-redis:
|
||||
image: redis:7
|
||||
restart: always
|
||||
networks:
|
||||
- penpot
|
||||
|
||||
## An optional admin application for pentpot. It allows manage
|
||||
## users, teams and inspect some parts of the database. You can read
|
||||
## more about it on: https://github.com/penpot/penpot-admin
|
||||
|
||||
# penpot-admin:
|
||||
# image: "penpotapp/admin:alpha"
|
||||
# networks:
|
||||
# - penpot
|
||||
|
||||
# depends_on:
|
||||
# - penpot-postgres
|
||||
# - penpot-backend
|
||||
|
||||
# environment:
|
||||
# - PENPOT_PUBLIC_URI=http://localhost:9001
|
||||
# - PENPOT_API_URI=http://penpot-frontend/
|
||||
|
||||
# - PENPOT_DATABASE_HOST=penpot-postgres
|
||||
# - PENPOT_DATABASE_NAME=penpot
|
||||
# - PENPOT_DATABASE_USERNAME=penpot
|
||||
# - PENPOT_DATABASE_PASSWORD=penpot
|
||||
# - PENPOT_REDIS_URI=redis://penpot-redis/0
|
||||
# - PENPOT_DEBUG="false"
|
||||
|
||||
## A mailcatch service, used as temporal SMTP server. You can access
|
||||
## via HTTP to the port 1080 for read all emails the penpot platform
|
||||
## has sent. Should be only used as a temporal solution meanwhile
|
||||
## you don't have a real SMTP provider configured.
|
||||
|
||||
penpot-mailcatch:
|
||||
image: sj26/mailcatcher:latest
|
||||
restart: always
|
||||
expose:
|
||||
- '1025'
|
||||
ports:
|
||||
- "1080:1080"
|
||||
|
||||
## Example configuration of MiniIO (S3 compatible object storage
|
||||
## service); If you don't have preference, then just use filesystem,
|
||||
## this is here just for the completeness.
|
||||
|
||||
# minio:
|
||||
# image: "minio/minio:latest"
|
||||
# command: minio server /mnt/data --console-address ":9001"
|
||||
#
|
||||
# volumes:
|
||||
# - "penpot_minio:/mnt/data"
|
||||
#
|
||||
# environment:
|
||||
# - MINIO_ROOT_USER=minioadmin
|
||||
# - MINIO_ROOT_PASSWORD=minioadmin
|
||||
#
|
||||
# ports:
|
||||
# - 9000:9000
|
||||
# - 9001:9001
|
||||
|
||||
|
||||
|
|
|
@ -25,7 +25,7 @@ http {
|
|||
include /etc/nginx/mime.types;
|
||||
default_type application/octet-stream;
|
||||
|
||||
error_log /dev/stdout;
|
||||
error_log /dev/stderr;
|
||||
access_log /dev/stdout;
|
||||
|
||||
gzip on;
|
||||
|
@ -60,29 +60,6 @@ http {
|
|||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
|
||||
etag off;
|
||||
root /var/www/app/;
|
||||
|
||||
location ~* \.(js|css).*$ {
|
||||
add_header Cache-Control "max-age=86400" always; # 24 hours
|
||||
}
|
||||
|
||||
location ~* \.(html).*$ {
|
||||
add_header Cache-Control "no-cache, max-age=0" always;
|
||||
}
|
||||
|
||||
location /api/export {
|
||||
proxy_pass http://penpot-exporter:6061;
|
||||
}
|
||||
|
||||
location /api {
|
||||
proxy_pass http://penpot-backend:6060/api;
|
||||
}
|
||||
|
||||
location /ws/notifications {
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection 'upgrade';
|
||||
proxy_pass http://penpot-backend:6060/ws/notifications;
|
||||
}
|
||||
|
||||
location @handle_redirect {
|
||||
set $redirect_uri "$upstream_http_location";
|
||||
|
@ -116,5 +93,35 @@ http {
|
|||
alias /opt/data/assets;
|
||||
add_header x-internal-redirect "$upstream_http_x_accel_redirect";
|
||||
}
|
||||
|
||||
|
||||
location /api/export {
|
||||
proxy_pass http://penpot-exporter:6061;
|
||||
}
|
||||
|
||||
location /api {
|
||||
proxy_pass http://penpot-backend:6060/api;
|
||||
}
|
||||
|
||||
location /admin {
|
||||
proxy_pass http://penpot-admin:6063/admin;
|
||||
}
|
||||
|
||||
location /ws/notifications {
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection 'upgrade';
|
||||
proxy_pass http://penpot-backend:6060/ws/notifications;
|
||||
}
|
||||
|
||||
location / {
|
||||
location ~* \.(js|css).*$ {
|
||||
add_header Cache-Control "max-age=86400" always; # 24 hours
|
||||
}
|
||||
|
||||
location ~* \.(html).*$ {
|
||||
add_header Cache-Control "no-cache, max-age=0" always;
|
||||
}
|
||||
root /var/www/app/;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -44,3 +44,6 @@
|
|||
(proc/on "uncaughtException"
|
||||
(fn [cause]
|
||||
(js/console.error cause)))
|
||||
|
||||
(proc/on "SIGTERM" (fn [] (proc/exit 0)))
|
||||
(proc/on "SIGINT" (fn [] (proc/exit 0)))
|
||||
|
|
|
@ -102,6 +102,10 @@
|
|||
(= :profile-blocked (:code cause)))
|
||||
(reset! error (tr "errors.profile-blocked"))
|
||||
|
||||
(and (= :restriction (:type cause))
|
||||
(= :admin-only-profile (:code cause)))
|
||||
(reset! error (tr "errors.profile-blocked"))
|
||||
|
||||
(and (= :validation (:type cause))
|
||||
(= :wrong-credentials (:code cause)))
|
||||
(reset! error (tr "errors.wrong-credentials"))
|
||||
|
@ -167,7 +171,8 @@
|
|||
:label (tr "auth.password")}]]
|
||||
|
||||
[:div.buttons-stack
|
||||
(when (contains? @cf/flags :login)
|
||||
(when (or (contains? @cf/flags :login)
|
||||
(contains? @cf/flags :login-with-password))
|
||||
[:& fm/submit-button
|
||||
{:label (tr "auth.login-submit")
|
||||
:data-test "login-submit"}])
|
||||
|
@ -228,6 +233,7 @@
|
|||
[:& login-buttons {:params params}]
|
||||
|
||||
(when (or (contains? @cf/flags :login)
|
||||
(contains? @cf/flags :login-with-password)
|
||||
(contains? @cf/flags :login-with-ldap))
|
||||
[:span.separator
|
||||
[:span.line]
|
||||
|
@ -235,6 +241,7 @@
|
|||
[:span.line]])])
|
||||
|
||||
(when (or (contains? @cf/flags :login)
|
||||
(contains? @cf/flags :login-with-password)
|
||||
(contains? @cf/flags :login-with-ldap))
|
||||
[:& login-form {:params params :on-success-callback on-success-callback}])])
|
||||
|
||||
|
@ -247,7 +254,8 @@
|
|||
[:& login-methods {:params params}]
|
||||
|
||||
[:div.links
|
||||
(when (contains? @cf/flags :login)
|
||||
(when (or (contains? @cf/flags :login)
|
||||
(contains? @cf/flags :login-with-password))
|
||||
[:div.link-entry
|
||||
[:& lk/link {:action #(st/emit! (rt/nav :auth-recovery-request))
|
||||
:data-test "forgot-password"}
|
||||
|
|
|
@ -1441,7 +1441,7 @@ msgstr "Resend invitation"
|
|||
|
||||
#: src/app/main/ui/dashboard/team.cljs
|
||||
msgid "labels.copy-invitation-link"
|
||||
msgstr "Copy invitation link"
|
||||
msgstr "Copy link"
|
||||
|
||||
#: src/app/main/ui/static.cljs, src/app/main/ui/static.cljs, src/app/main/ui/static.cljs
|
||||
msgid "labels.retry"
|
||||
|
|
|
@ -1611,7 +1611,7 @@ msgstr "Reenviar invitacion"
|
|||
|
||||
#: src/app/main/ui/dashboard/team.cljs
|
||||
msgid "labels.copy-invitation-link"
|
||||
msgstr "Copiar link de invitación"
|
||||
msgstr "Copiar enlace"
|
||||
|
||||
#: src/app/main/ui/static.cljs, src/app/main/ui/static.cljs
|
||||
msgid "labels.retry"
|
||||
|
|
42
manage.sh
42
manage.sh
|
@ -1,5 +1,4 @@
|
|||
#!/usr/bin/env bash
|
||||
set -ex
|
||||
|
||||
export ORGANIZATION="penpotapp";
|
||||
export DEVENV_IMGNAME="$ORGANIZATION/devenv";
|
||||
|
@ -11,6 +10,8 @@ export CURRENT_BRANCH=$(git rev-parse --abbrev-ref HEAD);
|
|||
export CURRENT_HASH=$(git rev-parse --short HEAD);
|
||||
export CURRENT_COMMITS=$(git rev-list --count HEAD)
|
||||
|
||||
set -ex
|
||||
|
||||
function print-current-version {
|
||||
echo -n "$CURRENT_VERSION-$CURRENT_COMMITS-g$CURRENT_HASH"
|
||||
}
|
||||
|
@ -78,7 +79,7 @@ function log-devenv {
|
|||
docker compose -p $DEVENV_PNAME -f docker/devenv/docker-compose.yaml logs -f --tail=50
|
||||
}
|
||||
|
||||
function run-devenv {
|
||||
function run-devenv-tmux {
|
||||
if [[ ! $(docker ps -f "name=penpot-devenv-main" -q) ]]; then
|
||||
start-devenv
|
||||
fi
|
||||
|
@ -86,6 +87,14 @@ function run-devenv {
|
|||
docker exec -ti penpot-devenv-main sudo -EH -u penpot /home/start-tmux.sh
|
||||
}
|
||||
|
||||
function run-devenv-shell {
|
||||
if [[ ! $(docker ps -f "name=penpot-devenv-main" -q) ]]; then
|
||||
start-devenv
|
||||
fi
|
||||
docker exec -ti penpot-devenv-main sudo -EH -u penpot bash
|
||||
}
|
||||
|
||||
|
||||
function build {
|
||||
echo ">> build start: $1"
|
||||
local version=$(print-current-version);
|
||||
|
@ -164,6 +173,20 @@ function build-exporter-bundle {
|
|||
echo ">> bundle exporter end";
|
||||
}
|
||||
|
||||
function build-docker-images {
|
||||
rsync -avr --delete ./bundles/frontend/ ./docker/images/bundle-frontend/;
|
||||
rsync -avr --delete ./bundles/backend/ ./docker/images/bundle-backend/;
|
||||
rsync -avr --delete ./bundles/exporter/ ./docker/images/bundle-exporter/;
|
||||
|
||||
pushd ./docker/images;
|
||||
|
||||
docker build -t penpotapp/frontend:$CURRENT_BRANCH -f Dockerfile.frontend .;
|
||||
docker build -t penpotapp/backend:$CURRENT_BRANCH -f Dockerfile.backend .;
|
||||
docker build -t penpotapp/exporter:$CURRENT_BRANCH -f Dockerfile.exporter .;
|
||||
|
||||
popd;
|
||||
}
|
||||
|
||||
function usage {
|
||||
echo "PENPOT build & release manager"
|
||||
echo "USAGE: $0 OPTION"
|
||||
|
@ -203,7 +226,10 @@ case $1 in
|
|||
start-devenv ${@:2}
|
||||
;;
|
||||
run-devenv)
|
||||
run-devenv ${@:2}
|
||||
run-devenv-tmux ${@:2}
|
||||
;;
|
||||
run-devenv-shell)
|
||||
run-devenv-shell ${@:2}
|
||||
;;
|
||||
stop-devenv)
|
||||
stop-devenv ${@:2}
|
||||
|
@ -216,6 +242,12 @@ case $1 in
|
|||
;;
|
||||
|
||||
# production builds
|
||||
build-bundle)
|
||||
build-frontend-bundle;
|
||||
build-backend-bundle;
|
||||
build-exporter-bundle;
|
||||
;;
|
||||
|
||||
build-frontend-bundle)
|
||||
build-frontend-bundle;
|
||||
;;
|
||||
|
@ -228,6 +260,10 @@ case $1 in
|
|||
build-exporter-bundle;
|
||||
;;
|
||||
|
||||
build-docker-images)
|
||||
build-docker-images
|
||||
;;
|
||||
|
||||
# Docker Image Tasks
|
||||
*)
|
||||
usage
|
||||
|
|
Loading…
Add table
Reference in a new issue