118 lines
3.7 KiB
Elixir
118 lines
3.7 KiB
Elixir
defmodule WhoNeedHelp.Accounts.DataLifecycle do
|
|
@moduledoc """
|
|
Read-only preflight for an account-deletion case.
|
|
|
|
This module intentionally does not erase or anonymise records. The applicable
|
|
retention policy is not encoded in the project, so an automatic destructive
|
|
action would make an unverified legal assumption. Operators get a repeatable,
|
|
audited checklist of live product state before a policy-backed executor is added.
|
|
"""
|
|
|
|
import Ecto.Query
|
|
|
|
alias WhoNeedHelp.Accounts
|
|
alias WhoNeedHelp.Accounts.{Scope, User}
|
|
alias WhoNeedHelp.Activities.{Activity, Participant}
|
|
alias WhoNeedHelp.Help.{Assignment, HelpRequest}
|
|
alias WhoNeedHelp.Repo
|
|
alias WhoNeedHelp.Support.SupportRequest
|
|
alias WhoNeedHelp.Tracking.TrackingSession
|
|
alias WhoNeedHelp.Trust.{AbuseSignal, Report}
|
|
|
|
def deletion_assessment(%Scope{user: moderator}, %SupportRequest{} = request) do
|
|
cond do
|
|
request.kind != :account_deletion ->
|
|
{:error, :not_deletion_request}
|
|
|
|
is_nil(request.requester_id) ->
|
|
{:error, :account_not_linked}
|
|
|
|
is_nil(request.contact_verified_at) ->
|
|
{:error, :contact_not_verified}
|
|
|
|
true ->
|
|
user = Repo.get(User, request.requester_id)
|
|
|
|
if user do
|
|
counts = blocking_counts(user.id)
|
|
|
|
blockers =
|
|
counts
|
|
|> Enum.filter(fn {_name, count} -> count > 0 end)
|
|
|> Enum.map(fn {name, count} -> %{kind: name, count: count} end)
|
|
|
|
{:ok,
|
|
%{
|
|
case_id: request.id,
|
|
reference: request.reference,
|
|
account_id: user.id,
|
|
account_staff_roles: Accounts.staff_roles(user),
|
|
contact_verified: true,
|
|
assessed_by: moderator.id,
|
|
assessed_at: DateTime.utc_now(:second),
|
|
blocking_counts: counts,
|
|
blockers: blockers,
|
|
technically_idle: blockers == [],
|
|
execution_available: false,
|
|
execution_blocker: :retention_policy_not_configured
|
|
}}
|
|
else
|
|
{:error, :account_not_found}
|
|
end
|
|
end
|
|
end
|
|
|
|
defp blocking_counts(user_id) do
|
|
%{
|
|
active_help_requests:
|
|
HelpRequest
|
|
|> where(
|
|
[request],
|
|
request.requester_id == ^user_id and
|
|
request.status in [:open, :matched, :in_progress]
|
|
)
|
|
|> count(),
|
|
active_helper_assignments:
|
|
Assignment
|
|
|> where(
|
|
[assignment],
|
|
assignment.helper_id == ^user_id and assignment.active and
|
|
assignment.status in [:accepted, :in_progress]
|
|
)
|
|
|> count(),
|
|
active_tracking_sessions:
|
|
TrackingSession
|
|
|> where([session], session.user_id == ^user_id and session.active)
|
|
|> count(),
|
|
open_activities:
|
|
Activity
|
|
|> where([activity], activity.creator_id == ^user_id and activity.status == :open)
|
|
|> count(),
|
|
active_activity_memberships:
|
|
Participant
|
|
|> join(:inner, [participant], activity in Activity,
|
|
on: activity.id == participant.activity_id
|
|
)
|
|
|> where(
|
|
[participant, activity],
|
|
participant.user_id == ^user_id and activity.status == :open and
|
|
participant.status in [:requested, :approved]
|
|
)
|
|
|> Repo.aggregate(:count),
|
|
unresolved_reports:
|
|
Report
|
|
|> where(
|
|
[report],
|
|
report.reporter_id == ^user_id and report.status in [:open, :reviewing]
|
|
)
|
|
|> count(),
|
|
open_trust_signals:
|
|
AbuseSignal
|
|
|> where([signal], signal.subject_id == ^user_id and signal.status == :open)
|
|
|> count()
|
|
}
|
|
end
|
|
|
|
defp count(query), do: Repo.aggregate(query, :count)
|
|
end
|