| 9 | | Корисникот сака да се регистрира во системот. |
| | 9 | == Предуслов: измени во DDL == |
| | 10 | |
| | 11 | Регистрацијата е двофазна — прво се создава корисник само со мејл и лозинка, а личните податоци се пополнуваат подоцна. Во моменталната шема `name`, `work_position` и `company_size` се `NOT NULL`, што го прави тоа невозможно. Истото важи и за `account.name`. |
| | 12 | |
| | 13 | {{{ |
| | 14 | ALTER TABLE account ALTER COLUMN name DROP NOT NULL; |
| | 15 | ALTER TABLE "user" ALTER COLUMN name DROP NOT NULL; |
| | 16 | ALTER TABLE "user" ALTER COLUMN work_position DROP NOT NULL; |
| | 17 | ALTER TABLE "user" ALTER COLUMN company_size DROP NOT NULL; |
| | 18 | |
| | 19 | -- is_profile_complete = true значи дека сите полиња се пополнети |
| | 20 | ALTER TABLE "user" ADD CONSTRAINT ck_user_profile_complete CHECK ( |
| | 21 | NOT is_profile_complete |
| | 22 | OR (name IS NOT NULL AND work_position IS NOT NULL AND company_size IS NOT NULL) |
| | 23 | ); |
| | 24 | }}} |
| 21 | | WITH new_user AS ( |
| 22 | | INSERT INTO _user ( |
| 23 | | email, |
| 24 | | password_hash, |
| 25 | | login_provider, |
| 26 | | verified, |
| 27 | | profile_complete, |
| 28 | | deleted, |
| 29 | | used_free_consultation, |
| 30 | | points) |
| 31 | | VALUES ( |
| 32 | | 'borjan.gjorgjievski@students.finki.ukim.mk', |
| 33 | | 'hashed_password_example', |
| 34 | | 'local', |
| 35 | | FALSE, |
| 36 | | FALSE, |
| 37 | | FALSE, |
| 38 | | FALSE, |
| 39 | | 0) |
| 40 | | RETURNING id) |
| 41 | | INSERT INTO verification_token (user_id, token, created_at, expires_at) |
| | 40 | BEGIN; |
| | 41 | |
| | 42 | WITH new_account AS ( |
| | 43 | INSERT INTO account (email, password_hash, name) |
| | 44 | VALUES ('borjan.gjorgjievski@students.finki.ukim.mk', |
| | 45 | 'hashed_password_example', |
| | 46 | NULL) |
| | 47 | RETURNING id |
| | 48 | ), |
| | 49 | new_user AS ( |
| | 50 | INSERT INTO "user" (account_id, |
| | 51 | email, |
| | 52 | password_hash, |
| | 53 | login_provider, |
| | 54 | is_verified, |
| | 55 | is_profile_complete, |
| | 56 | has_used_free_consultation, |
| | 57 | points) |
| 43 | | 'd9cafe62-760f-44ae-8f79-8f4048414642', |
| 44 | | CURRENT_TIMESTAMP, |
| 45 | | CURRENT_TIMESTAMP + INTERVAL '30 minutes' |
| 46 | | FROM new_user; |
| | 59 | 'borjan.gjorgjievski@students.finki.ukim.mk', |
| | 60 | 'hashed_password_example', |
| | 61 | 'local', |
| | 62 | FALSE, |
| | 63 | FALSE, |
| | 64 | FALSE, |
| | 65 | 0 |
| | 66 | FROM new_account |
| | 67 | RETURNING user_id |
| | 68 | ) |
| | 69 | INSERT INTO verification_token (verification_token_uuid, user_id, created_at, expires_at) |
| | 70 | SELECT 'd9cafe62-760f-44ae-8f79-8f4048414642'::uuid, |
| | 71 | user_id, |
| | 72 | CURRENT_TIMESTAMP, |
| | 73 | CURRENT_TIMESTAMP + INTERVAL '30 minutes' |
| | 74 | FROM new_user; |
| | 75 | |
| | 76 | COMMIT; |
| 49 | | * При клик на линкот кој е испратен до неговата мејл адреса, на корисникот му се прикажува страната за персонализација |
| | 80 | |
| | 81 | * При клик на линкот кој е испратен до неговата мејл адреса, токенот се проверува и корисникот се означува како верификуван. |
| | 82 | |
| | 83 | {{{ |
| | 84 | BEGIN; |
| | 85 | |
| | 86 | WITH valid_token AS ( |
| | 87 | SELECT user_id |
| | 88 | FROM verification_token |
| | 89 | WHERE verification_token_uuid = 'd9cafe62-760f-44ae-8f79-8f4048414642'::uuid |
| | 90 | AND expires_at > CURRENT_TIMESTAMP |
| | 91 | ) |
| | 92 | UPDATE "user" u |
| | 93 | SET is_verified = TRUE |
| | 94 | FROM valid_token vt |
| | 95 | WHERE u.user_id = vt.user_id; |
| | 96 | |
| | 97 | -- искористениот токен повеќе не е потребен |
| | 98 | DELETE FROM verification_token |
| | 99 | WHERE verification_token_uuid = 'd9cafe62-760f-44ae-8f79-8f4048414642'::uuid; |
| | 100 | |
| | 101 | COMMIT; |
| | 102 | }}} |
| | 103 | |
| | 104 | * На корисникот му се прикажува страната за персонализација |
| 53 | | UPDATE _user |
| 54 | | SET |
| 55 | | name = 'Borjan Gjorgjievski', |
| 56 | | work_position = 'Sales & Digital Solutions', |
| 57 | | company_size = 'Freelance' |
| 58 | | WHERE id = 1; |
| | 110 | BEGIN; |
| | 111 | |
| | 112 | UPDATE "user" |
| | 113 | SET name = 'Borjan Gjorgjievski', |
| | 114 | work_position = 'Sales & Digital Solutions', |
| | 115 | company_size = 'freelance', |
| | 116 | is_profile_complete = TRUE |
| | 117 | WHERE user_id = :user_id; |
| | 118 | |
| | 119 | -- account.name е дупликат на "user".name и мора да се одржува рачно |
| | 120 | UPDATE account |
| | 121 | SET name = 'Borjan Gjorgjievski' |
| | 122 | WHERE id = (SELECT account_id FROM "user" WHERE user_id = :user_id); |
| | 123 | |
| | 124 | COMMIT; |
| | 148 | {{{ |
| | 149 | -- проверка на статусот на токенот |
| | 150 | SELECT user_id, |
| | 151 | expires_at, |
| | 152 | (expires_at > CURRENT_TIMESTAMP) AS is_valid |
| | 153 | FROM verification_token |
| | 154 | WHERE verification_token_uuid = 'd9cafe62-760f-44ae-8f79-8f4048414642'::uuid; |
| | 155 | |
| | 156 | -- повторно испраќање: старите токени се бришат, се креира нов |
| | 157 | BEGIN; |
| | 158 | |
| | 159 | DELETE FROM verification_token |
| | 160 | WHERE user_id = :user_id; |
| | 161 | |
| | 162 | INSERT INTO verification_token (user_id, created_at, expires_at) |
| | 163 | VALUES (:user_id, |
| | 164 | CURRENT_TIMESTAMP, |
| | 165 | CURRENT_TIMESTAMP + INTERVAL '30 minutes') |
| | 166 | RETURNING verification_token_uuid; |
| | 167 | |
| | 168 | COMMIT; |
| | 169 | }}} |
| | 170 | |
| | 171 | == Забелешки == |
| | 172 | |
| | 173 | - `company_size` е PostgreSQL enum со мали букви (`freelance`, `micro`, `small`, `medium`, `mid_market`, `enterprise`, `other`). Вредноста `'Freelance'` предизвикува грешка. |
| | 174 | - Шемата нема колона `deleted` на `"user"` — не постои soft delete. |
| | 175 | - `email` и `password_hash` постојат и во `account` и во `"user"`. Регистрацијата ги запишува двапати, а секоја промена на лозинка мора да ажурира две табели. Ова треба да се реши на ниво на шема. |
| | 176 | - При повторно испраќање на токен, `gen_random_uuid()` го генерира UUID-то автоматски — не треба да се внесува рачно. |