wip: currently rewriting the project as a full stack application
tangled.org/kacaii.dev/sigo
gleam
1import app/domain/admin/sql
2import app/domain/user/sql as user_sql
3import app/web
4import app/web/context.{type Context}
5import argus
6import envoy
7import gleam/bool
8import gleam/dynamic/decode
9import gleam/http
10import gleam/list
11import gleam/result
12import pog
13import wisp
14
15const admin_registration = "000"
16
17const admin_password = "aluno"
18
19/// Generate the first admin user
20pub fn handle_request(
21 request req: wisp.Request,
22 ctx ctx: Context,
23) -> wisp.Response {
24 use <- wisp.require_method(req, http.Post)
25 use json_data <- wisp.require_json(req)
26
27 case decode.run(json_data, key_decoder()) {
28 Error(_) ->
29 wisp.unprocessable_content()
30 |> wisp.set_body(wisp.Text("Chave Secreta ausente"))
31 Ok(key) ->
32 case validate_admin_key(ctx, key) {
33 Error(err) -> handle_error(err)
34 Ok(_) -> insert_first_admin(ctx)
35 }
36 }
37}
38
39fn insert_first_admin(ctx: Context) -> wisp.Response {
40 let insert_result = {
41 use hashed_password <- result.try(
42 argus.hasher()
43 |> argus.hash(admin_password, argus.gen_salt())
44 |> result.replace_error(HashError),
45 )
46
47 use _ <- result.try(
48 user_sql.insert_new_user(
49 ctx.db,
50 "Drop Table da Silva",
51 admin_registration,
52 "0000000000",
53 "admin@email.com",
54 hashed_password.encoded_hash,
55 user_sql.Admin,
56 )
57 |> result.map_error(DataBase),
58 )
59
60 // No need to return anything from this function
61 Ok(Nil)
62 }
63
64 case insert_result {
65 Ok(_) ->
66 wisp.created()
67 |> wisp.set_body(wisp.Text("Primeiro admin criado com sucesso!"))
68 Error(err) -> handle_error(err)
69 }
70}
71
72fn handle_error(err: SetupAdminError) -> wisp.Response {
73 case err {
74 DataBaseNotEmpty ->
75 wisp.bad_request(
76 "O banco de dados precisa estar com a tabela de usuários vazia",
77 )
78
79 DataBaseReturnedEmptyRow(_) ->
80 wisp.internal_server_error()
81 |> wisp.set_body(wisp.Text(
82 "Não foi possível consultar o número total de usuários cadastrados",
83 ))
84
85 HashError ->
86 wisp.internal_server_error()
87 |> wisp.set_body(wisp.Text(
88 "Ocorreu um erro ao encriptografar a senha do usuário",
89 ))
90
91 IncorrectRequestToken(_) ->
92 wisp.response(403)
93 |> wisp.set_body(wisp.Text("Token Inválido"))
94
95 MissingEnvToken ->
96 wisp.internal_server_error()
97 |> wisp.set_body(wisp.Text(
98 "A variável de ambiente necessária para o acesso a este endpoint se encontra ausente",
99 ))
100
101 DataBase(err) -> web.handle_database_error(err)
102 }
103}
104
105fn validate_admin_key(ctx: Context, key: String) -> Result(Nil, SetupAdminError) {
106 use admin_token <- result.try(
107 envoy.get("ADMIN_TOKEN")
108 |> result.replace_error(MissingEnvToken),
109 )
110
111 use returned <- result.try(
112 sql.count_total_users(ctx.db)
113 |> result.map_error(DataBase),
114 )
115
116 use row <- result.try(
117 list.first(returned.rows)
118 |> result.map_error(DataBaseReturnedEmptyRow),
119 )
120
121 use <- bool.guard(when: row.total > 0, return: Error(DataBaseNotEmpty))
122 case key == admin_token {
123 True -> Ok(Nil)
124 False -> Error(IncorrectRequestToken(key))
125 }
126}
127
128fn key_decoder() -> decode.Decoder(String) {
129 use key <- decode.field("key", decode.string)
130 decode.success(key)
131}
132
133/// Setting up default admin can fail
134type SetupAdminError {
135 /// Submitted the wrong access token
136 IncorrectRequestToken(String)
137 /// Env has not been found
138 MissingEnvToken
139 /// An error occurred while accessing the DataBase
140 DataBase(pog.QueryError)
141 /// Failed to count how many users are registered
142 DataBaseReturnedEmptyRow(Nil)
143 /// Database needs to be empty
144 DataBaseNotEmpty
145 /// Failed to hash the admin password
146 HashError
147}