Fresh repository history for elektrine/tarakan hosted at https://git.elektrine.com/elektrine/tarakan.
152 lines
4.3 KiB
Elixir
152 lines
4.3 KiB
Elixir
defmodule TarakanWeb.API.RepositoryController do
|
|
@moduledoc """
|
|
Discovery and registration of public repositories for scanning clients.
|
|
|
|
`GET` lists reviewable repositories (`status=unscanned` for the work queue).
|
|
`POST` registers a public GitHub repository by URL or `owner/name` so mass
|
|
importers (awesome lists, OSINT harvests) can feed the registry.
|
|
"""
|
|
use TarakanWeb, :controller
|
|
|
|
alias Tarakan.Repositories
|
|
|
|
def index(conn, params) do
|
|
opts =
|
|
[
|
|
status: params["status"],
|
|
limit: parse_limit(params["limit"]),
|
|
min_stars: parse_min_stars(params["min_stars"]),
|
|
language: parse_language(params["language"] || params["lang"])
|
|
]
|
|
|> Enum.reject(fn {_k, v} -> is_nil(v) or v == "" end)
|
|
|
|
repositories = Repositories.list_reviewable_repositories(opts)
|
|
|
|
json(conn, %{repositories: Enum.map(repositories, &repository_json/1)})
|
|
end
|
|
|
|
@doc """
|
|
Registers a public GitHub repository.
|
|
|
|
Body: `{"url": "owner/name"}` or `{"url": "https://github.com/owner/name"}`.
|
|
Idempotent: an already-registered repository is returned as 200.
|
|
"""
|
|
def create(conn, params) do
|
|
url =
|
|
params
|
|
|> Map.get("url")
|
|
|> Kernel.||(Map.get(params, "repository"))
|
|
|> case do
|
|
value when is_binary(value) -> String.trim(value)
|
|
_ -> ""
|
|
end
|
|
|
|
if url == "" do
|
|
conn
|
|
|> put_status(:unprocessable_entity)
|
|
|> json(%{errors: %{url: ["is required"]}})
|
|
else
|
|
case Repositories.register_github_repository(url, conn.assigns.current_scope) do
|
|
{:ok, repository} ->
|
|
# Idempotent: 200 whether newly inserted or already present.
|
|
json(conn, %{repository: repository_json(repository)})
|
|
|
|
{:error, reason} ->
|
|
registration_error(conn, reason)
|
|
end
|
|
end
|
|
end
|
|
|
|
defp registration_error(conn, :invalid_github_repository) do
|
|
conn
|
|
|> put_status(:unprocessable_entity)
|
|
|> json(%{error: "url must be a public GitHub owner/name or repository URL"})
|
|
end
|
|
|
|
defp registration_error(conn, reason) when reason in [:not_found, :not_public] do
|
|
conn
|
|
|> put_status(:not_found)
|
|
|> json(%{error: "repository was not found or is not public"})
|
|
end
|
|
|
|
defp registration_error(conn, :registration_limit) do
|
|
conn
|
|
|> put_status(:too_many_requests)
|
|
|> json(%{error: "daily repository registration limit reached"})
|
|
end
|
|
|
|
defp registration_error(conn, reason) when reason in [:rate_limited, :request_limited] do
|
|
conn
|
|
|> put_status(:too_many_requests)
|
|
|> json(%{error: "too many registration requests; try again shortly"})
|
|
end
|
|
|
|
defp registration_error(conn, :unavailable) do
|
|
conn
|
|
|> put_status(:service_unavailable)
|
|
|> json(%{error: "could not reach GitHub to verify the repository"})
|
|
end
|
|
|
|
defp registration_error(conn, :unauthorized) do
|
|
conn
|
|
|> put_status(:forbidden)
|
|
|> json(%{error: "this credential cannot register repositories"})
|
|
end
|
|
|
|
defp registration_error(conn, _reason) do
|
|
conn
|
|
|> put_status(:unprocessable_entity)
|
|
|> json(%{error: "repository could not be registered"})
|
|
end
|
|
|
|
defp parse_limit(nil), do: 100
|
|
|
|
defp parse_limit(value) when is_binary(value) do
|
|
case Integer.parse(value) do
|
|
{limit, ""} when limit > 0 -> min(limit, 500)
|
|
_other -> 100
|
|
end
|
|
end
|
|
|
|
defp parse_limit(_), do: 100
|
|
|
|
defp parse_min_stars(nil), do: nil
|
|
|
|
defp parse_min_stars(value) do
|
|
case Integer.parse(to_string(value)) do
|
|
{n, _} when n > 0 -> n
|
|
_ -> nil
|
|
end
|
|
end
|
|
|
|
defp parse_language(nil), do: nil
|
|
|
|
defp parse_language(value) when is_binary(value) do
|
|
case String.trim(value) do
|
|
"" -> nil
|
|
lang -> lang
|
|
end
|
|
end
|
|
|
|
defp parse_language(_), do: nil
|
|
|
|
defp repository_json(repository) do
|
|
status = Map.get(repository, :status) || repository.listing_status
|
|
|
|
%{
|
|
host: repository.host,
|
|
owner: repository.owner,
|
|
name: repository.name,
|
|
status: status,
|
|
listing_status: repository.listing_status,
|
|
default_branch: repository.default_branch,
|
|
primary_language: repository.primary_language,
|
|
stars_count: repository.stars_count,
|
|
scan_count: repository.scan_count,
|
|
last_scanned_at: repository.last_scanned_at,
|
|
registered_at: repository.inserted_at,
|
|
record_url:
|
|
TarakanWeb.Endpoint.url() <> TarakanWeb.RepositoryPaths.repository_path(repository)
|
|
}
|
|
end
|
|
end
|