feat: add join request resource
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
This commit is contained in:
parent
2a04fad4fe
commit
2515a679b8
9 changed files with 323 additions and 5 deletions
|
|
@ -9,6 +9,7 @@ defmodule Mv.Membership do
|
|||
- `Setting` - Global application settings (singleton)
|
||||
- `Group` - Groups that members can belong to
|
||||
- `MemberGroup` - Join table for many-to-many relationship between Members and Groups
|
||||
- `JoinRequest` - Public join form submissions (pending_confirmation → submitted after email confirm)
|
||||
|
||||
## Public API
|
||||
The domain exposes these main actions:
|
||||
|
|
@ -27,6 +28,8 @@ defmodule Mv.Membership do
|
|||
|
||||
require Ash.Query
|
||||
import Ash.Expr
|
||||
alias Ash.Error.Query.NotFound, as: NotFoundError
|
||||
alias Mv.Membership.JoinRequest
|
||||
|
||||
admin do
|
||||
show? true
|
||||
|
|
@ -80,6 +83,10 @@ defmodule Mv.Membership do
|
|||
define :list_member_groups, action: :read
|
||||
define :destroy_member_group, action: :destroy
|
||||
end
|
||||
|
||||
resource Mv.Membership.JoinRequest do
|
||||
define :submit_join_request, action: :submit
|
||||
end
|
||||
end
|
||||
|
||||
# Singleton pattern: Get the single settings record
|
||||
|
|
@ -342,4 +349,49 @@ defmodule Mv.Membership do
|
|||
|> Keyword.put_new(:domain, __MODULE__)
|
||||
|> then(&Ash.read_one(query, &1))
|
||||
end
|
||||
|
||||
@doc """
|
||||
Confirms a join request by token (public confirmation link).
|
||||
|
||||
Hashes the token, finds the JoinRequest by confirmation_token_hash, then updates
|
||||
to status :submitted and invalidates the token. Idempotent: if already submitted,
|
||||
returns the existing record without changing it.
|
||||
|
||||
## Options
|
||||
- `:actor` - Must be nil for public confirm (policy allows only unauthenticated).
|
||||
|
||||
## Returns
|
||||
- `{:ok, request}` - Updated or already-submitted JoinRequest
|
||||
- `{:error, error}` - Token unknown/invalid or authorization error
|
||||
"""
|
||||
def confirm_join_request(token, opts \\ []) when is_binary(token) do
|
||||
hash = confirmation_token_hash(token)
|
||||
actor = Keyword.get(opts, :actor)
|
||||
|
||||
query =
|
||||
Ash.Query.for_read(JoinRequest, :get_by_confirmation_token_hash, %{
|
||||
confirmation_token_hash: hash
|
||||
})
|
||||
|
||||
case Ash.read_one(query, actor: actor, domain: __MODULE__) do
|
||||
{:ok, nil} ->
|
||||
{:error, NotFoundError.exception(resource: JoinRequest)}
|
||||
|
||||
{:ok, request} ->
|
||||
if request.status == :submitted do
|
||||
{:ok, request}
|
||||
else
|
||||
request
|
||||
|> Ash.Changeset.for_update(:confirm, %{}, domain: __MODULE__)
|
||||
|> Ash.update(domain: __MODULE__, actor: actor)
|
||||
end
|
||||
|
||||
{:error, error} ->
|
||||
{:error, error}
|
||||
end
|
||||
end
|
||||
|
||||
defp confirmation_token_hash(token) do
|
||||
:crypto.hash(:sha256, token) |> Base.encode16(case: :lower)
|
||||
end
|
||||
end
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue