diff --git a/backend/resources/migrations/0002.users.sql b/backend/resources/migrations/0002.users.sql index 0d92b8e55..3154f27ab 100644 --- a/backend/resources/migrations/0002.users.sql +++ b/backend/resources/migrations/0002.users.sql @@ -15,6 +15,37 @@ CREATE TABLE users ( is_demo boolean NOT NULL DEFAULT false ); +CREATE UNIQUE INDEX users__username__idx + ON users (username) + WHERE deleted_at IS null; + +CREATE UNIQUE INDEX users__email__idx + ON users (email) + WHERE deleted_at IS null; + +CREATE INDEX users__is_demo + ON users (is_demo) + WHERE deleted_at IS null + AND is_demo IS true; + +--- Table used for register all used emails by the user +CREATE TABLE IF NOT EXISTS user_emails ( + user_id uuid NOT NULL REFERENCES users(id) ON DELETE CASCADE, + + created_at timestamptz NOT NULL DEFAULT clock_timestamp(), + verified_at timestamptz NULL DEFAULT NULL, + + email text NOT NULL, + + is_main boolean NOT NULL DEFAULT false, + is_verified boolean NOT NULL DEFAULT false +); + +CREATE INDEX user_emails__user_id__idx + ON user_emails (user_id); + +--- Table for user key value attributes + CREATE TABLE IF NOT EXISTS user_attrs ( user_id uuid NOT NULL REFERENCES users(id) ON DELETE CASCADE, @@ -27,6 +58,8 @@ CREATE TABLE IF NOT EXISTS user_attrs ( PRIMARY KEY (key, user_id) ); +--- Table for store verification tokens + CREATE TABLE IF NOT EXISTS tokens ( user_id uuid NOT NULL REFERENCES users(id) ON DELETE CASCADE, token text NOT NULL, @@ -37,6 +70,8 @@ CREATE TABLE IF NOT EXISTS tokens ( PRIMARY KEY (token, user_id) ); +--- Table for store user sessions. + CREATE TABLE IF NOT EXISTS sessions ( id uuid PRIMARY KEY DEFAULT uuid_generate_v4(), @@ -47,6 +82,9 @@ CREATE TABLE IF NOT EXISTS sessions ( user_agent text NULL ); +CREATE INDEX sessions__user_id__idx + ON sessions (user_id); + -- Insert a placeholder system user. INSERT INTO users (id, fullname, username, email, photo, password) @@ -57,18 +95,7 @@ VALUES ('00000000-0000-0000-0000-000000000000'::uuid, '', '!'); -CREATE UNIQUE INDEX users__username__idx - ON users (username) - WHERE deleted_at IS null; - -CREATE UNIQUE INDEX users__email__idx - ON users (email) - WHERE deleted_at IS null; - -CREATE INDEX users__is_demo - ON users(is_demo) - WHERE deleted_at IS null - AND is_demo IS true; +--- Triggers CREATE TRIGGER users__modified_at__tgr BEFORE UPDATE ON users diff --git a/backend/src/uxbox/services/mutations/demo.clj b/backend/src/uxbox/services/mutations/demo.clj index 20063955d..37b398ea3 100644 --- a/backend/src/uxbox/services/mutations/demo.clj +++ b/backend/src/uxbox/services/mutations/demo.clj @@ -32,10 +32,14 @@ [uxbox.util.uuid :as uuid] [vertx.core :as vc])) -(def sql:create-demo-user +(def sql:insert-user "insert into users (id, fullname, username, email, password, photo, is_demo) values ($1, $2, $3, $4, $5, '', true) returning *") +(def sql:insert-email + "insert into user_emails (user_id, email, is_main) + values ($1, $2, true)") + (sm/defmutation ::create-demo-profile [params] (let [id (uuid/next) @@ -47,6 +51,7 @@ (sodi.util/bytes->b64s)) password' (sodi.pwhash/derive password)] (db/with-atomic [conn db/pool] - (db/query-one conn [sql:create-demo-user id fullname username email password']) + (db/query-one conn [sql:insert-user id fullname username email password']) + (db/query-one conn [sql:insert-email id email]) {:username username :password password}))) diff --git a/backend/src/uxbox/services/mutations/profile.clj b/backend/src/uxbox/services/mutations/profile.clj index 35fa6859d..2154d6a3d 100644 --- a/backend/src/uxbox/services/mutations/profile.clj +++ b/backend/src/uxbox/services/mutations/profile.clj @@ -35,7 +35,7 @@ (s/def ::email ::us/email) (s/def ::fullname ::us/string) -(s/def ::metadata any?) +(s/def ::lang ::us/string) (s/def ::old-password ::us/string) (s/def ::password ::us/string) (s/def ::path ::us/string) @@ -82,6 +82,15 @@ (-> (retrieve-user db/pool username) (p/then' check-user)))) +;; --- Mutation: Add additional email +;; TODO + +;; --- Mutation: Mark email as main email +;; TODO + +;; --- Mutation: Verify email (or maybe query?) +;; TODO + ;; --- Mutation: Update Profile (own) (defn- check-username-and-email! @@ -109,23 +118,22 @@ (def sql:update-profile "update users set username = $2, - email = $3, - fullname = $4, - metadata = $5 + fullname = $3, + lang = $4 where id = $1 and deleted_at is null returning *") (defn- update-profile - [conn {:keys [id username email fullname metadata] :as params}] - (let [sqlv [sql:update-profile id username - email fullname (blob/encode metadata)]] + [conn {:keys [id username fullname lang] :as params}] + (let [sqlv [sql:update-profile + id username fullname lang]] (-> (db/query-one conn sqlv) (p/then' su/raise-not-found-if-nil) (p/then' profile/strip-private-attrs)))) (s/def ::update-profile - (s/keys :req-un [::id ::username ::email ::fullname ::metadata])) + (s/keys :req-un [::id ::username ::fullname ::lang])) (sm/defmutation ::update-profile [params] @@ -213,10 +221,14 @@ ;; --- Mutation: Register Profile -(def sql:create-user +(def sql:insert-user "insert into users (id, fullname, username, email, password, photo) values ($1, $2, $3, $4, $5, '') returning *") +(def sql:insert-email + "insert into user_emails (user_id, email, is_main) + values ($1, $2, true)") + (defn- check-profile-existence! [conn {:keys [username email] :as params}] (let [sql "select exists @@ -237,22 +249,24 @@ [conn {:keys [id username fullname email password] :as params}] (let [id (or id (uuid/next)) password (sodi.pwhash/derive password) - sqlv [sql:create-user - id - fullname - username - email - password]] - (db/query-one conn sqlv))) + sqlv1 [sql:insert-user id + fullname username + email password] + sqlv2 [sql:insert-email id email]] + (p/let [profile (db/query-one conn sqlv1)] + (db/query-one conn sqlv2) + profile))) (defn register-profile [conn params] (-> (create-profile conn params) (p/then' profile/strip-private-attrs) (p/then (fn [profile] - (-> (emails/send! emails/register {:to (:email params) - :name (:fullname params)}) - (p/then' (constantly profile))))))) + ;; TODO: send a correct link for email verification + (p/let [data {:to (:email params) + :name (:fullname params)}] + (emails/send! emails/register data) + profile))))) (s/def ::register-profile (s/keys :req-un [::username ::email ::password ::fullname]))