Merge remote-tracking branch 'origin/staging' into develop

This commit is contained in:
Andrey Antukh 2026-08-18 13:19:34 +02:00
commit 296dd748bd
11 changed files with 146 additions and 45 deletions

View File

@ -166,8 +166,12 @@
;; the same row/object.
(let [profile (get-profile conn profile-id ::db/for-update true)
fullname (d/normalize-string fullname)
lang (d/normalize-string lang)
theme (d/normalize-string theme)
lang (if (contains? params :lang)
(d/normalize-string lang)
(:lang profile))
theme (if (contains? params :theme)
(d/normalize-string theme)
(:theme profile))
;; Update the profile map with direct params
profile (-> profile
(assoc :fullname fullname)

View File

@ -46,6 +46,7 @@
[app.common.data :as d]
[app.common.exceptions :as ex]
[app.common.logging :as l]
[app.common.math :as mth]
[app.common.schema :as sm]
[app.common.time :as ct]
[app.common.uri :as uri]
@ -180,8 +181,8 @@
result (rds/eval rconn script)
allowed? (boolean (nth result 0))
remaining (nth result 1)
reset (* (/ (inst-ms interval) rate)
(- capacity remaining))]
reset (long (mth/ceil (double (* (/ (inst-ms interval) rate)
(- capacity remaining)))))]
(l/trace :hint "limit processed"
:method method
:limit (name (::name limit))

View File

@ -125,6 +125,17 @@
(t/is (= "en" (:lang result)))
(t/is (= "dark" (:theme result))))))
(t/testing "update profile preserves omitted optional fields"
(let [data {::th/type :update-profile
::rpc/profile-id (:id profile)
:fullname "Updated Name"}
out (th/command! data)]
(t/is (nil? (:error out)))
(t/is (= "Updated Name" (get-in out [:result :fullname])))
(t/is (= "en" (get-in out [:result :lang])))
(t/is (= "dark" (get-in out [:result :theme])))))
(t/testing "update photo"
(let [data {::th/type :update-profile-photo
::rpc/profile-id (:id profile)

View File

@ -0,0 +1,28 @@
;; 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 Sucursal en Espana SL
(ns backend-tests.rpc-rlimit-test
(:require
[app.common.time :as ct]
[app.redis :as rds]
[app.rpc.rlimit :as rlimit]
[clojure.test :as t]))
(t/deftest bucket-reset-supports-fractional-milliseconds
(let [now (ct/inst 0)
limit {::rlimit/name :test
::rlimit/strategy :bucket
::rlimit/key "test"
::rlimit/method "main.test"
::rlimit/capacity 5
::rlimit/rate 3
::rlimit/interval (ct/duration 1000)
::rlimit/params [1 3 5]
::rlimit/opts "5/3/1s"}]
(with-redefs [rds/eval (fn [_ _] [true 4])]
(let [result (rlimit/process-limit nil "profile" now limit)]
(t/is (= (ct/inst 334)
(:app.rpc.rlimit.result/reset result)))))))

View File

@ -161,6 +161,16 @@ http {
location / {
include /etc/nginx/overrides/location.d/*.conf;
# Regenerated from the environment on every container start
# (see nginx-entrypoint.sh) while its URL is only versioned by
# the build, so a flags only restart leaves the URL untouched.
# Caching it like a build asset would keep returning users on
# the previous PENPOT_FLAGS for up to a week.
location = /js/config.js {
include /etc/nginx/nginx-security-headers.conf;
add_header Cache-Control "no-store, no-cache, max-age=0" always;
}
location ~* \.(js|css|jpg|png|svg|gif|ttf|woff|woff2|wasm|map)$ {
include /etc/nginx/nginx-security-headers.conf;
add_header Cache-Control "public, max-age=604800" always; # 7 days

View File

@ -122,6 +122,10 @@
;; --- Update Profile
(defn profile-update-params
[profile]
(d/without-nils (select-keys profile [:fullname :lang :theme])))
(defn persist-profile
[& {:as opts}]
(ptk/reify ::persist-profile
@ -130,7 +134,7 @@
(let [on-success (:on-success opts identity)
on-error (:on-error opts rx/throw)
profile (:profile state)
params (select-keys profile [:fullname :lang :theme])]
params (profile-update-params profile)]
(->> (rp/cmd! :update-profile params)
(rx/tap on-success)
(rx/map set-profile)
@ -143,7 +147,7 @@
props"
[profile]
(let [profile (check-profile profile)]
(let [profile (check-profile (d/without-nils profile))]
(ptk/reify ::update-profile
ptk/WatchEvent
(watch [_ state _]

View File

@ -609,50 +609,56 @@
(defn check-and-submit-invite-members
"Fetches fresh team data from the server to ensure up-to-date organization
permissions, then submits member invitations or shows a restriction modal."
permissions, then submits member invitations or shows a permission error."
[{:keys [team-id] :as params} origin do-invite-members]
(ptk/reify ::check-and-submit-invite-members
ptk/WatchEvent
(watch [_ _ _]
(if (contains? cf/flags :admin-console)
(with-refreshed-team team-id
(fn [team]
(if (not (cto/allowed? :add-anybody-to-team
{:organization-perms (:organization team)}))
(->> (rp/cmd! :check-organization-members {:organization-id (get-in team [:organization :id])
:emails (vec (:emails params))})
(rx/mapcat
(fn [result]
(let [blocked (into [] (comp (filter (fn [[_ v]] (not v)))
(map first))
result)]
(cond
(empty? blocked)
(do (do-invite-members params origin) (rx/empty))
(watch [_ state _]
(let [profile-id (dm/get-in state [:profile :id])]
(if (contains? cf/flags :admin-console)
(with-refreshed-team team-id
(fn [team]
(if (not (cto/can-send-invitations?
{:organization (:organization team)
:profile-id profile-id
:team-permissions (:permissions team)}))
(rx/of (modal/show :no-permission-modal {:type :invite-members}))
(if (not (cto/allowed? :add-anybody-to-team
{:organization-perms (:organization team)}))
(->> (rp/cmd! :check-organization-members {:organization-id (get-in team [:organization :id])
:emails (vec (:emails params))})
(rx/mapcat
(fn [result]
(let [blocked (into [] (comp (filter (fn [[_ v]] (not v)))
(map first))
result)]
(cond
(empty? blocked)
(do (do-invite-members params origin) (rx/empty))
(= (count blocked) (count result))
(rx/of
(modal/show
{:type :alert
:title (tr "modals.invite-restricted-members.all-blocked-title")
:message (tr "modals.invite-restricted-members.all-blocked")
:accept-label (tr "labels.accept")
:accept-style :primary}))
(= (count blocked) (count result))
(rx/of
(modal/show
{:type :alert
:title (tr "modals.invite-restricted-members.all-blocked-title")
:message (tr "modals.invite-restricted-members.all-blocked")
:accept-label (tr "labels.accept")
:accept-style :primary}))
:else
(rx/of
(modal/show
{:type :invite-restricted-members
:blocked-emails blocked
:on-accept (fn []
(let [valid-emails (into #{} (filter (fn [e] (get result e)))
(:emails params))
params' (assoc params :emails valid-emails)]
(do-invite-members params' origin)))})))))))
(do (do-invite-members params origin)
(rx/empty)))))
(do (do-invite-members params origin)
(rx/empty))))))
:else
(rx/of
(modal/show
{:type :invite-restricted-members
:blocked-emails blocked
:on-accept (fn []
(let [valid-emails (into #{} (filter (fn [e] (get result e)))
(:emails params))
params' (assoc params :emails valid-emails)]
(do-invite-members params' origin)))})))))))
(do (do-invite-members params origin)
(rx/empty))))))
(do (do-invite-members params origin)
(rx/empty)))))))
(defn copy-invitation-link
[{:keys [email team-id] :as params}]

View File

@ -233,6 +233,9 @@
(= :email-domain-is-not-allowed code))
(st/emit! (ntf/error (tr "errors.email-domain-not-allowed"))
(modal/hide))
(and (= :validation type)
(= :insufficient-permissions code))
(st/emit! (modal/show :no-permission-modal {:type :invite-members}))
:else
(st/emit! (ntf/error (tr "errors.generic"))

View File

@ -152,6 +152,8 @@
(tr "dashboard.no-permission-create-team.message" organization-name)]
:delete-team [(tr "dashboard.delete-team")
(tr "dashboard.no-permission-delete-team.message" organization-name)]
:invite-members [(tr "modals.invite-team-member.title")
(tr "dashboard.invitations.no-permission")]
:no-organizations-create [(tr "dashboard.select-organization-modal.title")
(tr "dashboard.no-organization-allows-create-team.message")]
:no-organizations-change [(tr "dashboard.change-organization-modal.title")

View File

@ -0,0 +1,30 @@
;; 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 Sucursal en España SL
(ns frontend-tests.data.profile-test
(:require
[app.common.uuid :as uuid]
[app.main.data.profile :as dprof]
[cljs.test :as t :include-macros true]))
(t/deftest profile-update-params-omits-nil-values
(t/is (= {:fullname "Updated Name"}
(dprof/profile-update-params {:fullname "Updated Name"
:lang nil
:theme nil}))))
(t/deftest profile-update-params-preserves-present-values
(t/is (= {:fullname "Updated Name"
:lang "en"
:theme "dark"}
(dprof/profile-update-params {:fullname "Updated Name"
:lang "en"
:theme "dark"}))))
(t/deftest update-profile-accepts-nil-optional-values
(t/is (some? (dprof/update-profile {:id uuid/zero
:fullname "Updated Name"
:theme nil}))))

View File

@ -11,6 +11,7 @@
[frontend-tests.data.dashboard-test]
[frontend-tests.data.exports-assets-test]
[frontend-tests.data.nitrate-test]
[frontend-tests.data.profile-test]
[frontend-tests.data.repo-test]
[frontend-tests.data.store-test]
[frontend-tests.data.uploads-test]
@ -107,6 +108,7 @@
'frontend-tests.copy-as-svg-test
'frontend-tests.data.dashboard-test
'frontend-tests.data.nitrate-test
'frontend-tests.data.profile-test
'frontend-tests.data.repo-test
'frontend-tests.data.store-test
'frontend-tests.data.exports-assets-test