defmodule HuggingfaceClient.Hub.GatedRepos do
@moduledoc """
Manage access requests for gated repositories on the HuggingFace Hub.
Gated repos require users to request access before downloading. As a repo admin
you can view pending requests and accept, reject, or cancel them.
See: https://huggingface.co/docs/hub/models-gated
## Example
# List pending requests for a gated model
{:ok, requests} = HuggingfaceClient.list_pending_access_requests("my-org/gated-model",
access_token: "hf_admin_token"
)
# Accept a specific user's request
:ok = HuggingfaceClient.accept_access_request("my-org/gated-model",
user: "alice",
access_token: "hf_admin_token"
)
"""
alias HuggingfaceClient.Error.InputError
alias HuggingfaceClient.Hub.Client
# ── List Requests ─────────────────────────────────────────────────────────────
@doc """
Lists pending access requests for a gated repository.
"""
@spec list_pending(String.t(), keyword()) :: {:ok, [map()]} | {:error, Exception.t()}
def list_pending(repo, opts \\ []) do
list_requests(repo, "pending", opts)
end
@doc """
Lists accepted access requests for a gated repository.
"""
@spec list_accepted(String.t(), keyword()) :: {:ok, [map()]} | {:error, Exception.t()}
def list_accepted(repo, opts \\ []) do
list_requests(repo, "accepted", opts)
end
@doc """
Lists rejected access requests for a gated repository.
"""
@spec list_rejected(String.t(), keyword()) :: {:ok, [map()]} | {:error, Exception.t()}
def list_rejected(repo, opts \\ []) do
list_requests(repo, "rejected", opts)
end
# ── Manage Requests ───────────────────────────────────────────────────────────
@doc """
Accepts a user's access request for a gated repository.
## Options
- `:user` — username of the user to accept (required)
- `:type` — repo type (default: `:model`)
- `:access_token`
"""
@spec accept(String.t(), keyword()) :: :ok | {:error, Exception.t()}
def accept(repo, opts \\ []) do
user = opts[:user] || raise InputError, ":user is required"
manage_request(repo, user, "accept", opts)
end
@doc """
Rejects a user's access request for a gated repository.
"""
@spec reject(String.t(), keyword()) :: :ok | {:error, Exception.t()}
def reject(repo, opts \\ []) do
user = opts[:user] || raise InputError, ":user is required"
manage_request(repo, user, "reject", opts)
end
@doc """
Cancels a user's accepted access (moves them back to pending).
"""
@spec cancel(String.t(), keyword()) :: :ok | {:error, Exception.t()}
def cancel(repo, opts \\ []) do
user = opts[:user] || raise InputError, ":user is required"
manage_request(repo, user, "cancel", opts)
end
@doc """
Grants access to a user directly (bypassing the request flow).
## Options
- `:user` — username (required)
- `:type` — repo type (default: `:model`)
"""
@spec grant_access(String.t(), keyword()) :: :ok | {:error, Exception.t()}
def grant_access(repo, opts \\ []) do
user = opts[:user] || raise InputError, ":user is required"
token = opts[:access_token]
type = Keyword.get(opts, :type, :model)
url = "#{Client.hub_url()}/api/#{type_prefix(type)}#{repo}/user-access/#{user}/grant"
case Client.post(url, %{}, token, opts) do
{:ok, _} -> :ok
{:error, _} = err -> err
end
end
@doc """
Revokes a user's access to a gated repository.
"""
@spec revoke_access(String.t(), keyword()) :: :ok | {:error, Exception.t()}
def revoke_access(repo, opts \\ []) do
user = opts[:user] || raise InputError, ":user is required"
token = opts[:access_token]
type = Keyword.get(opts, :type, :model)
url = "#{Client.hub_url()}/api/#{type_prefix(type)}#{repo}/user-access/#{user}/revoke"
case Client.post(url, %{}, token, opts) do
{:ok, _} -> :ok
{:error, _} = err -> err
end
end
# ── Private ───────────────────────────────────────────────────────────────────
defp list_requests(repo, status, opts) do
token = opts[:access_token]
type = Keyword.get(opts, :type, :model)
url = "#{Client.hub_url()}/api/#{type_prefix(type)}#{repo}/user-access?status=#{status}"
case Client.get(url, token, opts) do
{:ok, body} when is_list(body) -> {:ok, body}
{:ok, %{"requests" => list}} -> {:ok, list}
{:ok, other} -> {:ok, List.wrap(other)}
err -> err
end
end
defp manage_request(repo, user, action, opts) do
token = opts[:access_token]
type = Keyword.get(opts, :type, :model)
url = "#{Client.hub_url()}/api/#{type_prefix(type)}#{repo}/user-access/#{user}/#{action}"
case Client.post(url, %{}, token, opts) do
{:ok, _} -> :ok
{:error, _} = err -> err
end
end
defp type_prefix(:model), do: ""
defp type_prefix(:dataset), do: "datasets/"
defp type_prefix(:space), do: "spaces/"
defp type_prefix(_), do: ""
end