···22import app/domain/user
33import app/web/context.{type Context}
44import app/web/socket/message as msg
55-import gleam/erlang/process
65import gleam/list
76import gleam/result
87import group_registry
···1918 use returned <- result.map(sql.query_members_id(ctx.db, brigade_id))
20192120 use row <- list.each(returned.rows)
2222- process.spawn(fn() { user.broadcast(registry, row.id, message) })
2121+ user.broadcast(registry, row.id, message)
2322}
+5-2
src/app/domain/occurrence.gleam
···2020 use returned <- result.map(sql.query_participants(ctx.db, occ_id))
21212222 use row <- list.each(returned.rows)
2323- process.spawn(fn() { user.broadcast(registry, row.user_id, message) })
2323+ user.broadcast(registry, row.user_id, message)
2424}
25252626/// Notify subscribed users that a new occurrence has been added
2727+///
2828+/// Spawns a new process
2729pub fn notify_new_occurrence(
2830 registry registry: group_registry.GroupRegistry(msg.Msg),
2931 new id: uuid.Uuid,
···34363537 use subject <- list.each(members)
3638 process.spawn(fn() {
3737- process.send(subject, msg.Domain(msg.OccurrenceCreated(id:, category:)))
3939+ let msg = msg.Domain(msg.OccurrenceCreated(id:, category:))
4040+ process.send(subject, msg)
3841 })
3942}
+26-25
src/app/domain/user.gleam
···1919 /// Authentication failed
2020 Authentication(AuthenticationError)
2121 /// User is authentication but lacks permissions
2222- Authorization(
2222+ AuthorizationError(
2323 user_uuid: uuid.Uuid,
2424 user_role: role.Role,
2525 authorized_roles: List(role.Role),
···4040 InvalidUUID(String)
4141}
42424343-/// Broadcast a message to an user
4343+/// Broadcast an arbitrary message to an user
4444+///
4545+/// Spawns a new process
4446pub fn broadcast(
4547 registry: group_registry.GroupRegistry(msg.Msg),
4648 user_id: uuid.Uuid,
···6365 |> result.map_error(DataBase),
6466 )
65676666- case list.first(returned.rows) {
6767- Error(_) -> Error(RoleNotFound)
6868- Ok(row) ->
6969- Ok(case row.user_role {
7070- sql.Admin -> role.Admin
7171- sql.Analyst -> role.Analyst
7272- sql.Captain -> role.Captain
7373- sql.Developer -> role.Developer
7474- sql.Firefighter -> role.Firefighter
7575- sql.Sargeant -> role.Sargeant
7676- })
6868+ use row <- result.map(
6969+ list.first(returned.rows)
7070+ |> result.replace_error(RoleNotFound),
7171+ )
7272+7373+ case row.user_role {
7474+ sql.Admin -> role.Admin
7575+ sql.Analyst -> role.Analyst
7676+ sql.Captain -> role.Captain
7777+ sql.Developer -> role.Developer
7878+ sql.Firefighter -> role.Firefighter
7979+ sql.Sargeant -> role.Sargeant
7780 }
7881}
7982···99102 cookie_name cookie_name: String,
100103 authorized_roles authorized_roles: List(role.Role),
101104) -> Result(#(uuid.Uuid, role.Role), AccessControlError) {
102102- // Indentify who is sending the request
103105 use user_uuid <- result.try(
104104- extract_uuid(request:, cookie_name:)
106106+ extract_uuid(request, cookie_name)
105107 |> result.map_error(Authentication),
106108 )
107109···111113 // Check if that role has authorization
112114 use user_role <- result.map(
113115 list.find(authorized_roles, fn(authorized) { user_role == authorized })
114114- |> result.replace_error(Authorization(
116116+ |> result.replace_error(AuthorizationError(
115117 user_uuid:,
116118 user_role:,
117119 authorized_roles:,
···124126pub fn handle_authentication_error(err: AuthenticationError) {
125127 case err {
126128 InvalidUUID(id) ->
127127- wisp.response(401)
128128- |> wisp.set_body(wisp.Text("ID de usuário inválido: " <> id))
129129+ wisp.Text("ID de usuário inválido: " <> id)
130130+ |> wisp.set_body(wisp.response(401), _)
129131 MissingCookie ->
130130- wisp.response(401)
131131- |> wisp.set_body(wisp.Text("Cookie de autenticação ausente"))
132132+ "Cookie de autenticação ausente"
133133+ |> wisp.Text
134134+ |> wisp.set_body(wisp.response(401), _)
132135 }
133136}
134137···141144 |> wisp.Text
142145 |> wisp.set_body(wisp.response(401), _)
143146144144- InvalidRole(str) -> {
145145- let body = "Usuário autenticado possui cargo inválido: " <> str
146146- wisp.Text(body)
147147+ InvalidRole(str) ->
148148+ wisp.Text("Usuário autenticado possui cargo inválido: " <> str)
147149 |> wisp.set_body(wisp.response(401), _)
148148- }
149150150150- Authorization(user_uuid:, user_role:, authorized_roles:) -> {
151151+ AuthorizationError(user_uuid:, user_role:, authorized_roles:) -> {
151152 role.log_unauthorized_access_attempt(
152153 request: req,
153154 user_uuid:,
+11-13
src/app/domain/user/delete_user.gleam
···6363 ctx: Context,
6464 target_id: String,
6565) -> Result(String, DeleteUserError) {
6666- // User that is going to be deleted
6766 use target_user_uuid <- result.try(
6867 uuid.from_string(target_id)
6968 |> result.replace_error(InvalidUserUuid(target_id)),
···7978 |> result.map_error(AccessControl),
8079 )
81808282- // Check if the authenticated user is trying to delete theirself
8381 case uuid.to_string(user_uuid) == target_id {
8482 True -> Error(CantDeleteSelf)
8583 False -> {
···8886 |> result.map_error(DataBase),
8987 )
90889191- case list.first(returned.rows) {
9292- Error(_) -> Error(UserNotFound(user_uuid))
9393- Ok(row) -> {
9494- json.object([
9595- #("id", json.string(uuid.to_string(row.id))),
9696- #("full_name", json.string(row.full_name)),
9797- ])
9898- |> json.to_string
9999- |> Ok
100100- }
101101- }
8989+ use row <- result.map(
9090+ list.first(returned.rows)
9191+ |> result.replace_error(UserNotFound(user_uuid)),
9292+ )
9393+9494+ [
9595+ #("id", json.string(uuid.to_string(row.id))),
9696+ #("full_name", json.string(row.full_name)),
9797+ ]
9898+ |> json.object
9999+ |> json.to_string
102100 }
103101 }
104102}
+2
src/app/web/socket.gleam
···7474}
75757676/// Broadcast a message to all active users
7777+///
7878+/// Spawns a new process
7779pub fn broadcast(
7880 registry registry: group_registry.GroupRegistry(msg.Msg),
7981 message message: msg.Msg,