···48 |> pog.execute(db)
49}
5000000000000000000000000000000000000000000051// --- Encoding/decoding utils -------------------------------------------------
5253/// A decoder to decode `Uuid`s coming from a Postgres query.
···48 |> pog.execute(db)
49}
5051+/// A row you get from running the `whoami` query
52+/// defined in `./src/server/auth/sql/whoami.sql`.
53+///
54+/// > ๐ฟ๏ธ This type definition was generated automatically using v4.6.0 of the
55+/// > [squirrel package](https://github.com/giacomocavalieri/squirrel).
56+///
57+pub type WhoamiRow {
58+ WhoamiRow(id: Uuid, full_name: String, email: String, phone: String)
59+}
60+61+/// Runs the `whoami` query
62+/// defined in `./src/server/auth/sql/whoami.sql`.
63+///
64+/// > ๐ฟ๏ธ This function was generated automatically using v4.6.0 of
65+/// > the [squirrel package](https://github.com/giacomocavalieri/squirrel).
66+///
67+pub fn whoami(
68+ db: pog.Connection,
69+ arg_1: Uuid,
70+) -> Result(pog.Returned(WhoamiRow), pog.QueryError) {
71+ let decoder = {
72+ use id <- decode.field(0, uuid_decoder())
73+ use full_name <- decode.field(1, decode.string)
74+ use email <- decode.field(2, decode.string)
75+ use phone <- decode.field(3, decode.string)
76+ decode.success(WhoamiRow(id:, full_name:, email:, phone:))
77+ }
78+79+ "select
80+ u.id,
81+ u.full_name,
82+ u.email,
83+ u.phone
84+from public.user_account as u
85+where u.id = $1;
86+"
87+ |> pog.query
88+ |> pog.parameter(pog.text(uuid.to_string(arg_1)))
89+ |> pog.returning(decoder)
90+ |> pog.execute(db)
91+}
92+93// --- Encoding/decoding utils -------------------------------------------------
9495/// A decoder to decode `Uuid`s coming from a Postgres query.
+2-1
server/src/server/auth/sql/authenticate.sql
···4 u.password_hash,
5 u.is_active
6from public.user_account as u
7-where u.email = $1;
0
···4 u.password_hash,
5 u.is_active
6from public.user_account as u
7+where u.email =
8+$1;
+2-10
server/src/server/router/login.gleam
···8import server/auth
9import server/context.{type Context}
10import shared/session
11-import shared/user
12import wisp.{type Request, type Response}
13import youid/uuid
1415type LoginError {
16 Auth(auth.AuthError)
17- QueryError(user.UserError)
18}
1920/// ๎ธฌ Queries the database and store a session token containing the user uuid
···46) -> Response {
47 let query_result = {
48 use user <- result.map(
49- user.find(db, user_uuid)
50- |> result.map_error(QueryError),
51 )
5253 session.Authenticated(user)
···74fn handle_error(err: LoginError) -> Response {
75 case err {
76 Auth(err) -> auth.handle_error(err)
77-78- QueryError(err) ->
79- case err {
80- user.NotFound -> wisp.not_found()
81- user.Database(_) -> wisp.internal_server_error()
82- }
83 }
84}
85
···8import server/auth
9import server/context.{type Context}
10import shared/session
011import wisp.{type Request, type Response}
12import youid/uuid
1314type LoginError {
15 Auth(auth.AuthError)
016}
1718/// ๎ธฌ Queries the database and store a session token containing the user uuid
···44) -> Response {
45 let query_result = {
46 use user <- result.map(
47+ auth.whoami(db, user_uuid)
48+ |> result.map_error(Auth),
49 )
5051 session.Authenticated(user)
···72fn handle_error(err: LoginError) -> Response {
73 case err {
74 Auth(err) -> auth.handle_error(err)
00000075 }
76}
77
···2# You typically do not need to edit this file
34packages = [
0000000005 { name = "gleam_crypto", version = "1.5.1", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_crypto", source = "hex", outer_checksum = "50774BAFFF1144E7872814C566C5D653D83A3EBF23ACC3156B757A1B6819086E" },
06 { name = "gleam_json", version = "3.1.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_json", source = "hex", outer_checksum = "44FDAA8847BE8FC48CA7A1C089706BD54BADCC4C45B237A992EDDF9F2CDB2836" },
007 { name = "gleam_stdlib", version = "0.69.0", build_tools = ["gleam"], requirements = [], otp_app = "gleam_stdlib", source = "hex", outer_checksum = "AAB0962BEBFAA67A2FBEE9EEE218B057756808DC9AF77430F5182C6115B3A315" },
8 { name = "gleam_time", version = "1.7.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_time", source = "hex", outer_checksum = "56DB0EF9433826D3B99DB0B4AF7A2BFED13D09755EC64B1DAAB46F804A9AD47D" },
9 { name = "gleeunit", version = "1.9.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleeunit", source = "hex", outer_checksum = "DA9553CE58B67924B3C631F96FE3370C49EB6D6DC6B384EC4862CC4AAA718F3C" },
0000000000000010 { name = "youid", version = "1.5.1", build_tools = ["gleam"], requirements = ["gleam_crypto", "gleam_stdlib", "gleam_time"], otp_app = "youid", source = "hex", outer_checksum = "580E909FD704DB16416D5CB080618EDC2DA0F1BE4D21B490C0683335E3FFC5AF" },
11]
12···14gleam_json = { version = ">= 3.1.0 and < 4.0.0" }
15gleam_stdlib = { version = ">= 0.44.0 and < 2.0.0" }
16gleeunit = { version = ">= 1.0.0 and < 2.0.0" }
0017youid = { version = ">= 1.5.1 and < 2.0.0" }
-24
shared/src/shared/user.gleam
···1import gleam/dynamic/decode
2import gleam/json
3-import gleam/list
4-import gleam/result
5-import pog
6-import shared/user/sql
7import youid/uuid
8-9-pub type UserError {
10- Database(pog.QueryError)
11- NotFound
12-}
1314pub type User {
15 User(uuid: uuid.Uuid, full_name: String, email: String, phone: String)
···30 use email <- decode.field("email", decode.string)
31 use phone <- decode.field("phone", decode.string)
32 decode.success(User(uuid:, full_name:, email:, phone:))
33-}
34-35-/// Query an user by Uuid
36-pub fn find(db: pog.Connection, uuid: uuid.Uuid) -> Result(User, UserError) {
37- use returned <- result.try(
38- sql.find_by_id(db, uuid)
39- |> result.map_error(Database),
40- )
41-42- use row <- result.map(
43- list.first(returned.rows)
44- |> result.replace_error(NotFound),
45- )
46-47- User(uuid:, full_name: row.full_name, email: row.email, phone: row.phone)
48}
4950// helpers
···1import gleam/dynamic/decode
2import gleam/json
00003import youid/uuid
0000045pub type User {
6 User(uuid: uuid.Uuid, full_name: String, email: String, phone: String)
···21 use email <- decode.field("email", decode.string)
22 use phone <- decode.field("phone", decode.string)
23 decode.success(User(uuid:, full_name:, email:, phone:))
00000000000000024}
2526// helpers
-64
shared/src/shared/user/sql.gleam
···1-//// This module contains the code to run the sql queries defined in
2-//// `./src/shared/user/sql`.
3-//// > ๐ฟ๏ธ This module was generated automatically using v4.6.0 of
4-//// > the [squirrel package](https://github.com/giacomocavalieri/squirrel).
5-////
6-7-import gleam/dynamic/decode
8-import pog
9-import youid/uuid.{type Uuid}
10-11-/// A row you get from running the `find_by_id` query
12-/// defined in `./src/shared/user/sql/find_by_id.sql`.
13-///
14-/// > ๐ฟ๏ธ This type definition was generated automatically using v4.6.0 of the
15-/// > [squirrel package](https://github.com/giacomocavalieri/squirrel).
16-///
17-pub type FindByIdRow {
18- FindByIdRow(id: Uuid, full_name: String, email: String, phone: String)
19-}
20-21-/// Runs the `find_by_id` query
22-/// defined in `./src/shared/user/sql/find_by_id.sql`.
23-///
24-/// > ๐ฟ๏ธ This function was generated automatically using v4.6.0 of
25-/// > the [squirrel package](https://github.com/giacomocavalieri/squirrel).
26-///
27-pub fn find_by_id(
28- db: pog.Connection,
29- arg_1: Uuid,
30-) -> Result(pog.Returned(FindByIdRow), pog.QueryError) {
31- let decoder = {
32- use id <- decode.field(0, uuid_decoder())
33- use full_name <- decode.field(1, decode.string)
34- use email <- decode.field(2, decode.string)
35- use phone <- decode.field(3, decode.string)
36- decode.success(FindByIdRow(id:, full_name:, email:, phone:))
37- }
38-39- "select
40- u.id,
41- u.full_name,
42- u.email,
43- u.phone
44-from public.user_account as u
45-where u.id =
46-$1;
47-"
48- |> pog.query
49- |> pog.parameter(pog.text(uuid.to_string(arg_1)))
50- |> pog.returning(decoder)
51- |> pog.execute(db)
52-}
53-54-// --- Encoding/decoding utils -------------------------------------------------
55-56-/// A decoder to decode `Uuid`s coming from a Postgres query.
57-///
58-fn uuid_decoder() {
59- use bit_array <- decode.then(decode.bit_array)
60- case uuid.from_bit_array(bit_array) {
61- Ok(uuid) -> decode.success(uuid)
62- Error(_) -> decode.failure(uuid.v7(), "Uuid")
63- }
64-}