Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

First steps for "public/private profile" #21

Merged
merged 7 commits into from
Aug 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion ops/cloud_init.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down
4 changes: 3 additions & 1 deletion resources/co/gaiwan/compass/config.edn
Original file line number Diff line number Diff line change
@@ -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
}
7 changes: 4 additions & 3 deletions resources/co/gaiwan/compass/system.edn
Original file line number Diff line number Diff line change
@@ -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}}
26 changes: 17 additions & 9 deletions src/co/gaiwan/compass/db/queries.clj
Original file line number Diff line number Diff line change
Expand Up @@ -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))))
42 changes: 34 additions & 8 deletions src/co/gaiwan/compass/db/schema.clj
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,40 @@
;; keyword | long | ref | string | symbol | tuple | uuid | uri

(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"]

[:discord/id :string "Unique user id on discord, a 'snowflake', i.e. uint64 encoded as string"]
[;; 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
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"]
;; End user entity

[: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" :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"]
Expand Down
2 changes: 1 addition & 1 deletion src/co/gaiwan/compass/html/navigation.clj
Original file line number Diff line number Diff line change
Expand Up @@ -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"]]
Expand Down
8 changes: 2 additions & 6 deletions src/co/gaiwan/compass/html/profiles.clj
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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"}]]
Expand Down
13 changes: 7 additions & 6 deletions src/co/gaiwan/compass/model/session.clj
Original file line number Diff line number Diff line change
Expand Up @@ -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))))))

Expand Down Expand Up @@ -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)))
8 changes: 3 additions & 5 deletions src/co/gaiwan/compass/routes/filters.clj
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -18,7 +16,7 @@
:location "/"
:session (assoc session
:session-filters
(merge defaults
(merge session/default-filters
(update-vals params keyword)))}))

(defn routes []
Expand Down
15 changes: 7 additions & 8 deletions src/co/gaiwan/compass/routes/oauth.clj
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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 {}))}}]])
11 changes: 6 additions & 5 deletions src/co/gaiwan/compass/routes/profiles.clj
Original file line number Diff line number Diff line change
Expand Up @@ -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]))
Expand All @@ -17,10 +18,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
Expand All @@ -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
Expand All @@ -49,7 +49,8 @@
(ring-response/not-found "File not found"))))

(defn GET-attendees [req]
)
(let [attendees (q/all-users)]
{:html/body [:p "TODO"] #_[attendees/user-list attendees]}))

(defn routes []
[["/profile"
Expand Down
2 changes: 1 addition & 1 deletion src/co/gaiwan/compass/routes/ticket.clj
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
19 changes: 18 additions & 1 deletion src/co/gaiwan/compass/services/tito.clj
Original file line number Diff line number Diff line change
Expand Up @@ -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) "/"))

Expand Down Expand Up @@ -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!)

Expand Down
Loading