From 8f360d6b6478556258ebd8d58c717bd4d4811423 Mon Sep 17 00:00:00 2001 From: Arne Brasseur Date: Sun, 18 Aug 2024 12:02:03 +0200 Subject: [PATCH 1/7] First steps for "public/private profile" --- src/co/gaiwan/compass/db/schema.clj | 36 +++++++++++++++++++++++++---- 1 file changed, 31 insertions(+), 5 deletions(-) diff --git a/src/co/gaiwan/compass/db/schema.clj b/src/co/gaiwan/compass/db/schema.clj index 88bebbb..0997aae 100644 --- a/src/co/gaiwan/compass/db/schema.clj +++ b/src/co/gaiwan/compass/db/schema.clj @@ -6,11 +6,37 @@ (def schema [[:user/uuid :uuid "Unique user identifier" :identity] - [:user/email :string "User email" :identity] - [:user/name :string "User name, e.g. 'Arne'"] - [:user/handle :string "User handle, e.g. 'sunnyplexus'"] - [:user/title :string "User's job title or any description, e.g. 'CEO of Gaiwan'"] - [:user/image-path :string "User image path in the compass web server"] + [:user/public-profile :ref "User's public profile"] + [:user/private-profile :ref "User's public profile"] + [:user/confidantes :ref "People you connected with / accepted a connection + request from. A :u/c B means that user A agrees to show their public profile + to user B. When two people connect we create connections in both directions, + each person can subsequently revoke their side of the connection (I no longer + want to share my details with that person). Similarly if person B does not + accept the connection, we only add it on one side, so person A can only see + B's public profile, B sees A's private profile. It should not be visible to B + that A did not accept. (they don't know if what they are seeing is public or + private.)" :many] + + [:public-profile/name :string "Publicly visible user name, e.g. 'Arne'"] + [:public-profile/avatar-url :string "Relative or absolute URL of the user's avatar"] + [:public-profile/bio :string "Free-form Markdown field"] + [:public-profile/hidden? :boolean "Hide this profile from listings or attendance lists"] + [:public-profile/links :ref "Links that are publicly visible" :many] + + [:private-profile/name :string "User name visible to contacts"] + [:private-profile/links :ref "Links that are only visible to contacts" :many] + [:private-profile/bio :string "Free-form Markdown field"] + + [:profile-link/user :ref "User this link belongs too"] + [:profile-link/type :string "`mastodon`, `linkedin`, `personal-site`, etc."] + [:profile-link/href :string "http/mailto URL"] + + ;; [:user/email :string "User email" :identity] + ;; [:user/name :string "User name, e.g. 'Arne'"] + ;; [:user/handle :string "User handle, e.g. 'sunnyplexus'"] + ;; [:user/title :string "User's job title or any description, e.g. 'CEO of Gaiwan'"] + ;; [:user/image-path :string "User image path in the compass web server"] [:discord/id :string "Unique user id on discord, a 'snowflake', i.e. uint64 encoded as string"] [:discord/access-token :string "Discord OAuth2 access-token"] From 37b2c3ff53d7bc0d185ad30eaf274fcc4174f674 Mon Sep 17 00:00:00 2001 From: Arne Brasseur Date: Mon, 19 Aug 2024 10:01:51 +0200 Subject: [PATCH 2/7] Schema changes for user/profile --- src/co/gaiwan/compass/db/schema.clj | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/co/gaiwan/compass/db/schema.clj b/src/co/gaiwan/compass/db/schema.clj index 0997aae..6364ab5 100644 --- a/src/co/gaiwan/compass/db/schema.clj +++ b/src/co/gaiwan/compass/db/schema.clj @@ -5,10 +5,9 @@ ;; keyword | long | ref | string | symbol | tuple | uuid | uri (def schema - [[:user/uuid :uuid "Unique user identifier" :identity] - [:user/public-profile :ref "User's public profile"] - [:user/private-profile :ref "User's public profile"] - [:user/confidantes :ref "People you connected with / accepted a connection + [;; Start user entity + [:user/uuid :uuid "Unique user identifier" :identity] + [:user/contacts :ref "People you connected with / accepted a connection request from. A :u/c B means that user A agrees to show their public profile to user B. When two people connect we create connections in both directions, each person can subsequently revoke their side of the connection (I no longer @@ -27,6 +26,7 @@ [:private-profile/name :string "User name visible to contacts"] [:private-profile/links :ref "Links that are only visible to contacts" :many] [:private-profile/bio :string "Free-form Markdown field"] + ;; End user entity [:profile-link/user :ref "User this link belongs too"] [:profile-link/type :string "`mastodon`, `linkedin`, `personal-site`, etc."] From cd69e58798e8205ef7a86cd993763a5a70c96cb5 Mon Sep 17 00:00:00 2001 From: Laurence Chen Date: Tue, 20 Aug 2024 15:06:15 +0800 Subject: [PATCH 3/7] drop :user/email --- src/co/gaiwan/compass/db/schema.clj | 2 +- src/co/gaiwan/compass/routes/oauth.clj | 15 +++++++-------- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/src/co/gaiwan/compass/db/schema.clj b/src/co/gaiwan/compass/db/schema.clj index 6364ab5..f856666 100644 --- a/src/co/gaiwan/compass/db/schema.clj +++ b/src/co/gaiwan/compass/db/schema.clj @@ -38,7 +38,7 @@ ;; [:user/title :string "User's job title or any description, e.g. 'CEO of Gaiwan'"] ;; [:user/image-path :string "User image path in the compass web server"] - [:discord/id :string "Unique user id on discord, a 'snowflake', i.e. uint64 encoded as string"] + [:discord/id :string "Unique user id on discord, a 'snowflake', i.e. uint64 encoded as string" :identity] [:discord/access-token :string "Discord OAuth2 access-token"] [:discord/expires-at :instant "Expiration timestamp for the OAuth2 token"] [:discord/refresh-token :string "Discord OAuth2 refresh-token"] diff --git a/src/co/gaiwan/compass/routes/oauth.clj b/src/co/gaiwan/compass/routes/oauth.clj index 9f7fd6c..e1ebf5a 100644 --- a/src/co/gaiwan/compass/routes/oauth.clj +++ b/src/co/gaiwan/compass/routes/oauth.clj @@ -45,13 +45,12 @@ :else (let [{:keys [access_token refresh_token expires_in]} body - {:keys [id global_name email username]} (discord/fetch-user-info access_token) - user-uuid (:user/uuid (d/entity (db/db) [:user/email email]) (random-uuid)) + {:keys [id global_name email username] :as fetch} (discord/fetch-user-info access_token) + _ (tap> {:fetch fetch}) + user-uuid (:user/uuid (d/entity (db/db) [:discord/id id]) (random-uuid)) tx-data [{:user/uuid user-uuid - :user/email email - :user/name global_name - :user/handle username + :public-profile/name global_name :discord/id id :discord/access-token access_token :discord/refresh-token refresh_token @@ -87,6 +86,6 @@ ["/logout" {:get {:handler (fn [req] (assoc - (response/redirect "/") - :flash "You were signed out" - :session {}))}}]]) + (response/redirect "/") + :flash "You were signed out" + :session {}))}}]]) From 3105d74646dae273d01cbc31a775b31553611d28 Mon Sep 17 00:00:00 2001 From: Laurence Chen Date: Tue, 20 Aug 2024 15:12:48 +0800 Subject: [PATCH 4/7] replace :user/image-path with :public-profile/avatar-url --- src/co/gaiwan/compass/html/profiles.clj | 4 ++-- src/co/gaiwan/compass/routes/profiles.clj | 5 ++--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/co/gaiwan/compass/html/profiles.clj b/src/co/gaiwan/compass/html/profiles.clj index c1016c1..ab7610c 100644 --- a/src/co/gaiwan/compass/html/profiles.clj +++ b/src/co/gaiwan/compass/html/profiles.clj @@ -34,10 +34,10 @@ (o/defstyled profile-detail :div#detail [image-frame :w-100px] ([{:discord/keys [access-token id refresh-token expires-at avatar-url] - :user/keys [email handle name uuid title image-path] :as user}] + :user/keys [email handle name uuid title] :as user}] [:<> [image-frame {:profile/image - (if-let [image (or image-path avatar-url)] + (if-let [image (or (:public-profile/avatar-url user) avatar-url)] (str "url(" image ")") (str "var(--gradient-" (inc (rand-int 7)) ")"))} user] [:div.details diff --git a/src/co/gaiwan/compass/routes/profiles.clj b/src/co/gaiwan/compass/routes/profiles.clj index f7605f3..5055453 100644 --- a/src/co/gaiwan/compass/routes/profiles.clj +++ b/src/co/gaiwan/compass/routes/profiles.clj @@ -34,7 +34,7 @@ file-id (str (:db/id identity)) filepath (str (config/value :uploads/dir) "/" file-id "_" filename) {:keys [tempids]} @(db/transact [(merge - {:user/image-path (str "/" filepath)} + {:public-profile/avatar-url (str "/" filepath)} (params->profile-data params))])] ;; (tap> req) ;; Copy the image file content to the uploads directory @@ -48,8 +48,7 @@ (ring-response/file-response (.getPath file)) (ring-response/not-found "File not found")))) -(defn GET-attendees [req] - ) +(defn GET-attendees [req]) (defn routes [] [["/profile" From 3f6965b5598c5994059dc602dc9b6b08cac7ac35 Mon Sep 17 00:00:00 2001 From: Laurence Chen Date: Tue, 20 Aug 2024 15:18:12 +0800 Subject: [PATCH 5/7] drop :user/title :user/name --- src/co/gaiwan/compass/html/navigation.clj | 2 +- src/co/gaiwan/compass/html/profiles.clj | 4 ---- src/co/gaiwan/compass/routes/profiles.clj | 5 ++--- 3 files changed, 3 insertions(+), 8 deletions(-) diff --git a/src/co/gaiwan/compass/html/navigation.clj b/src/co/gaiwan/compass/html/navigation.clj index 0d02fef..f9ed3d7 100644 --- a/src/co/gaiwan/compass/html/navigation.clj +++ b/src/co/gaiwan/compass/html/navigation.clj @@ -54,7 +54,7 @@ #_[:pre (pr-str user)] [:ul [:li - (if-let [name (:user/name user)] + (if-let [name (:public-profile/name user)] [:<> [:p "Welcome, " name] [:a {:href "/logout"} "Sign out"]] diff --git a/src/co/gaiwan/compass/html/profiles.clj b/src/co/gaiwan/compass/html/profiles.clj index ab7610c..57479be 100644 --- a/src/co/gaiwan/compass/html/profiles.clj +++ b/src/co/gaiwan/compass/html/profiles.clj @@ -58,10 +58,6 @@ [:label {:for "name"} "Display Name"] [:input {:id "name" :name "name" :type "text" :required true :min-length 2}]] - [:div - [:label {:for "title"} "title"] - [:input {:id "title" :name "title" :type "text" - :min-length 2}]] [:div [:label {:for "image"} "Profile Image"] [:input {:id "image" :name "image" :type "file" :accept "image/png, image/jpeg"}]] diff --git a/src/co/gaiwan/compass/routes/profiles.clj b/src/co/gaiwan/compass/routes/profiles.clj index 5055453..7587bfb 100644 --- a/src/co/gaiwan/compass/routes/profiles.clj +++ b/src/co/gaiwan/compass/routes/profiles.clj @@ -17,10 +17,9 @@ (:identity req)]}) (defn params->profile-data - [{:keys [name title user-id] :as params}] + [{:keys [name user-id] :as params}] {:db/id (parse-long user-id) - :user/name name - :user/title title}) + :public-profile/name name}) (defn POST-save-profile "Save profile to DB From c485f37a6496d91d6104230ce14c9f72c43f1274 Mon Sep 17 00:00:00 2001 From: Arne Brasseur Date: Tue, 20 Aug 2024 11:06:45 +0200 Subject: [PATCH 6/7] Socket REPL --- ops/cloud_init.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ops/cloud_init.sh b/ops/cloud_init.sh index 960f644..bb34a33 100644 --- a/ops/cloud_init.sh +++ b/ops/cloud_init.sh @@ -211,7 +211,7 @@ After=txor.service Restart=always RestartSec=1 WorkingDirectory=/home/compass/app/current -ExecStart=/usr/local/bin/clojure -J-Dclojure.main.report=stderr -A:prod -M -m co.gaiwan.compass run --env prod --config /home/compass/config.edn +ExecStart=/usr/local/bin/clojure -J-Dclojure.main.report=stderr -Dclojure.server.repl='{:port 5555 :accept clojure.core.server/repl}' -A:prod -M -m co.gaiwan.compass run --env prod --config /home/compass/config.edn User=compass [Install] From 11925e2c165311ff342575abdb7a51a752f39aa9 Mon Sep 17 00:00:00 2001 From: Arne Brasseur Date: Tue, 20 Aug 2024 11:10:20 +0200 Subject: [PATCH 7/7] Add tito sync, fix db/transact call, fix default filter behavior --- resources/co/gaiwan/compass/config.edn | 4 +++- resources/co/gaiwan/compass/system.edn | 7 +++--- src/co/gaiwan/compass/db/queries.clj | 26 +++++++++++++++-------- src/co/gaiwan/compass/model/session.clj | 13 ++++++------ src/co/gaiwan/compass/routes/filters.clj | 8 +++---- src/co/gaiwan/compass/routes/profiles.clj | 5 ++++- src/co/gaiwan/compass/routes/ticket.clj | 2 +- src/co/gaiwan/compass/services/tito.clj | 19 ++++++++++++++++- 8 files changed, 57 insertions(+), 27 deletions(-) diff --git a/resources/co/gaiwan/compass/config.edn b/resources/co/gaiwan/compass/config.edn index fdb87c1..7843864 100644 --- a/resources/co/gaiwan/compass/config.edn +++ b/resources/co/gaiwan/compass/config.edn @@ -1,4 +1,6 @@ {:port 8099 :tito/event-slug "heart-of-clojure/2024" :uploads/dir "uploads" - :dynamic-routes? false} + :dynamic-routes? false + :tito/sync-interval-seconds 900 ;; every 15 minutes + } diff --git a/resources/co/gaiwan/compass/system.edn b/resources/co/gaiwan/compass/system.edn index eeaf8dd..3e2a766 100644 --- a/resources/co/gaiwan/compass/system.edn +++ b/resources/co/gaiwan/compass/system.edn @@ -1,5 +1,6 @@ -{:compass/http {:port #config :port - :router #ig/ref :compass/router +{:compass/http {:port #config :port + :router #ig/ref :compass/router :dynamic? #config :dynamic-routes?} :compass/router {:dynamic? #config :dynamic-routes?} - :compass/db {:url #config :datomic/url}} + :compass/db {:url #config :datomic/url} + :tito/sync {:interval-seconds #config :tito/sync-interval-seconds}} diff --git a/src/co/gaiwan/compass/db/queries.clj b/src/co/gaiwan/compass/db/queries.clj index a1a8e6e..a564404 100644 --- a/src/co/gaiwan/compass/db/queries.clj +++ b/src/co/gaiwan/compass/db/queries.clj @@ -5,14 +5,22 @@ (defn all-sessions [] - (sort-by :session/time - (db/q - '[:find - [(pull ?e [* {:session/type [*] - :session/location [*]}]) ...] - :where - [?e :session/title]] - (db/db)))) + (sort-by + :session/time + (db/q + '[:find + [(pull ?e [* {:session/type [*] + :session/location [*]}]) ...] + :where + [?e :session/title]] + (db/db)))) (defn all-users [] - ) + (sort-by + :public-profile/name + (db/q + '[:find + [(pull ?e [*]) ...] + :where + [?e :public-profile/name]] + (db/db)))) diff --git a/src/co/gaiwan/compass/model/session.clj b/src/co/gaiwan/compass/model/session.clj index f43a727..c7ab04d 100644 --- a/src/co/gaiwan/compass/model/session.clj +++ b/src/co/gaiwan/compass/model/session.clj @@ -19,13 +19,13 @@ ;; first make sure that user is already login (some? user) (or - ;; Condition 1: organized property record the user's :db/id + ;; Condition 1: organized property record the user's :db/id (= (:db/id user) (:db/id organized)) - ;; Condition 2: organized property record the user's group :db/id + ;; Condition 2: organized property record the user's group :db/id (some (comp #{(:db/id user)} :db/id) (:user-group/users organized)) - ;; Condition 3: The user belongs to orga group + ;; Condition 3: The user belongs to orga group (some :user-group/orga (:user-group/_users user)))))) @@ -102,11 +102,12 @@ (< (count participants) capacity)) sessions)) +(def default-filters + {:include-past false}) + (defn apply-filters [sessions user filters] - (def sessions sessions) - (def f filters) (reduce (fn [sessions [k v]] (apply-filter sessions user k v)) sessions - filters)) + (merge default-filters filters))) diff --git a/src/co/gaiwan/compass/routes/filters.clj b/src/co/gaiwan/compass/routes/filters.clj index 29a77fb..cc16a38 100644 --- a/src/co/gaiwan/compass/routes/filters.clj +++ b/src/co/gaiwan/compass/routes/filters.clj @@ -2,10 +2,8 @@ "Filtering behavior" (:require [co.gaiwan.compass.html.filters :as filters] - [co.gaiwan.compass.http.response :as redirect])) - -(def defaults - {:include-past false}) + [co.gaiwan.compass.http.response :as redirect] + [co.gaiwan.compass.model.session :as session])) (defn GET-filters [req] {:html/layout false @@ -18,7 +16,7 @@ :location "/" :session (assoc session :session-filters - (merge defaults + (merge session/default-filters (update-vals params keyword)))})) (defn routes [] diff --git a/src/co/gaiwan/compass/routes/profiles.clj b/src/co/gaiwan/compass/routes/profiles.clj index 7587bfb..697eeaf 100644 --- a/src/co/gaiwan/compass/routes/profiles.clj +++ b/src/co/gaiwan/compass/routes/profiles.clj @@ -4,6 +4,7 @@ [clojure.java.io :as io] [co.gaiwan.compass.config :as config] [co.gaiwan.compass.db :as db] + [co.gaiwan.compass.db.queries :as q] [co.gaiwan.compass.html.profiles :as h] [co.gaiwan.compass.http.response :as response] [ring.util.response :as ring-response])) @@ -47,7 +48,9 @@ (ring-response/file-response (.getPath file)) (ring-response/not-found "File not found")))) -(defn GET-attendees [req]) +(defn GET-attendees [req] + (let [attendees (q/all-users)] + {:html/body [:p "TODO"] #_[attendees/user-list attendees]})) (defn routes [] [["/profile" diff --git a/src/co/gaiwan/compass/routes/ticket.clj b/src/co/gaiwan/compass/routes/ticket.clj index bb86f55..2ee13c2 100644 --- a/src/co/gaiwan/compass/routes/ticket.clj +++ b/src/co/gaiwan/compass/routes/ticket.clj @@ -29,7 +29,7 @@ (if-let [ticket (tito/find-unassigned-ticket (str/upper-case ref) email)] (do @(db/transact - [:db/add (:db/id ticket) :tito.ticket/assigned-to [:user/uuid (:user/uuid identity)]]) + [[:db/add (:db/id ticket) :tito.ticket/assigned-to [:user/uuid (:user/uuid identity)]]]) (discord/assign-ticket-role (:discord/id identity) ticket) (response/redirect "/" {:flash [:p "Ticket connection successful! You should now have the appropriate roles in our Discord server."]})) (response/redirect "/connect-ticket" {:status :found diff --git a/src/co/gaiwan/compass/services/tito.clj b/src/co/gaiwan/compass/services/tito.clj index fb69803..5e04001 100644 --- a/src/co/gaiwan/compass/services/tito.clj +++ b/src/co/gaiwan/compass/services/tito.clj @@ -14,7 +14,9 @@ [co.gaiwan.compass.config :as config] [co.gaiwan.compass.db :as db] [co.gaiwan.compass.util :as util] - [hato.client :as hato])) + [hato.client :as hato] + [integrant.core :as ig] + [io.pedestal.log :as log])) (def API_ENDPOINT (str "https://api.tito.io/v3/" (config/value :tito/event-slug) "/")) @@ -128,6 +130,21 @@ (not [?ticket :tito.ticket/assigned-to _])] (db/db) reference email)) +(defmethod ig/init-key :tito/sync [_ {:keys [interval-seconds]}] + (log/info :tito/starting-sync-loop {:interval-seconds interval-seconds}) + (let [stop? (volatile! false)] + (future + (while (not @stop?) + (try + (sync!) + (catch Exception e + (log/error :tito/sync-failed {} :exception e))) + (Thread/sleep (* 1000 interval-seconds)))) + stop?)) + +(defmethod ig/halt-key! :tito/sync [_ stop?] + (vreset! stop? true)) + (comment (sync!)