defmodule Towerops.Admin do @moduledoc """ The Admin context for superuser operations. Provides functions for managing users, organizations, and audit logging. """ import Ecto.Query alias Towerops.Accounts.User alias Towerops.Admin.AuditLog alias Towerops.Organizations.Organization alias Towerops.Repo ## User Management @doc """ Lists all users in the system with their organizations. ## Options * `:limit` - Maximum number of users to return (default: 100) * `:offset` - Number of users to skip (default: 0) ## Examples iex> list_all_users() [%User{}, ...] iex> list_all_users(limit: 50, offset: 100) [%User{}, ...] """ def list_all_users(opts \\ []) do limit = Keyword.get(opts, :limit, 100) offset = Keyword.get(opts, :offset, 0) User |> order_by([u], desc: u.inserted_at) |> limit(^limit) |> offset(^offset) |> preload(:organizations) |> Repo.all() end @doc """ Returns the total count of users in the system. ## Examples iex> count_users() 42 """ def count_users do Repo.aggregate(User, :count) end @doc """ Deletes a user and creates an audit log entry. ## Examples iex> delete_user(user_id, superuser_id, "127.0.0.1") {:ok, %User{}} iex> delete_user(invalid_id, superuser_id, "127.0.0.1") ** (Ecto.NoResultsError) """ def delete_user(user_id, superuser_id, ip_address) do user = Repo.get!(User, user_id) Repo.transaction(fn -> # Create audit log before deletion create_audit_log(%{ action: "user_delete", superuser_id: superuser_id, target_user_id: user_id, metadata: %{email: user.email}, ip_address: ip_address }) # Delete user (cascades to memberships and tokens) Repo.delete!(user) end) end ## Organization Management @doc """ Lists all organizations in the system with their memberships. ## Options * `:limit` - Maximum number of organizations to return (default: 100) * `:offset` - Number of organizations to skip (default: 0) ## Examples iex> list_all_organizations() [%Organization{}, ...] iex> list_all_organizations(limit: 50, offset: 100) [%Organization{}, ...] """ def list_all_organizations(opts \\ []) do limit = Keyword.get(opts, :limit, 100) offset = Keyword.get(opts, :offset, 0) Organization |> order_by([o], desc: o.inserted_at) |> limit(^limit) |> offset(^offset) |> preload(memberships: :user) |> Repo.all() end @doc """ Returns the total count of organizations in the system. ## Examples iex> count_organizations() 15 """ def count_organizations do Repo.aggregate(Organization, :count) end @doc """ Deletes an organization and creates an audit log entry. ## Examples iex> delete_organization(org_id, superuser_id, "127.0.0.1") {:ok, %Organization{}} iex> delete_organization(invalid_id, superuser_id, "127.0.0.1") ** (Ecto.NoResultsError) """ def delete_organization(org_id, superuser_id, ip_address) do org = Repo.get!(Organization, org_id) Repo.transaction(fn -> # Create audit log before deletion create_audit_log(%{ action: "org_delete", superuser_id: superuser_id, target_user_id: nil, metadata: %{name: org.name, slug: org.slug}, ip_address: ip_address }) # Delete organization (cascades to memberships, sites, device) Repo.delete!(org) end) end ## Audit Logging @doc """ Creates an audit log entry. ## Examples iex> create_audit_log(%{action: "impersonate_start", superuser_id: superuser_id, target_user_id: user_id}) {:ok, %AuditLog{}} iex> create_audit_log(%{action: "invalid"}) {:error, %Ecto.Changeset{}} """ def create_audit_log(attrs) do %AuditLog{} |> AuditLog.changeset(attrs) |> Repo.insert() end @doc """ Lists recent audit logs with associated users. ## Options * `:limit` - Maximum number of logs to return (default: 100) ## Examples iex> list_audit_logs() [%AuditLog{}, ...] iex> list_audit_logs(limit: 50) [%AuditLog{}, ...] """ def list_audit_logs(opts \\ []) do limit = Keyword.get(opts, :limit, 100) AuditLog |> order_by([a], desc: a.inserted_at) |> limit(^limit) |> preload([:superuser, :target_user]) |> Repo.all() end end