api specs
This commit is contained in:
221
phoenix/lib/shooting_event_phx_web/controllers/api_controller.ex
Normal file
221
phoenix/lib/shooting_event_phx_web/controllers/api_controller.ex
Normal file
@@ -0,0 +1,221 @@
|
||||
defmodule ShootingEventPhxWeb.ApiController do
|
||||
use ShootingEventPhxWeb, :controller
|
||||
alias ShootingEventPhx.{Auth, Events}
|
||||
alias ShootingEventPhx.Auth.SessionStore
|
||||
alias ShootingEventPhx.Domain.{EventData, Settings, Stages, StateService}
|
||||
alias ShootingEventPhx.AI.Gemini
|
||||
|
||||
def health(conn, _params) do
|
||||
json(conn, %{"status" => "ok"})
|
||||
end
|
||||
|
||||
def state(conn, _params) do
|
||||
state = StateService.read_state(Auth.admin_request?(conn))
|
||||
json(conn, state)
|
||||
end
|
||||
|
||||
def admin_state(conn, _params) do
|
||||
json(conn, StateService.read_state(true))
|
||||
end
|
||||
|
||||
def admin_login(conn, params) do
|
||||
username = Map.get(params, "username", "")
|
||||
password = Map.get(params, "password", "")
|
||||
|
||||
if Auth.verify_admin(username, password) do
|
||||
{:ok, token, expires_at} = SessionStore.create_token()
|
||||
|
||||
json(conn, %{
|
||||
"token" => token,
|
||||
"expiresAt" => DateTime.to_iso8601(expires_at),
|
||||
"username" => Auth.admin_user()
|
||||
})
|
||||
else
|
||||
error(conn, :unauthorized, "invalid credentials")
|
||||
end
|
||||
end
|
||||
|
||||
def admin_logout(conn, _params) do
|
||||
case Auth.bearer_token(conn) do
|
||||
nil -> :ok
|
||||
token -> SessionStore.delete_token(token)
|
||||
end
|
||||
|
||||
json(conn, %{"ok" => true})
|
||||
end
|
||||
|
||||
def update_settings(conn, params) do
|
||||
case Settings.update_settings(params) do
|
||||
:ok ->
|
||||
Events.broadcast()
|
||||
json(conn, StateService.read_state(true))
|
||||
|
||||
{:error, message} ->
|
||||
error(conn, :bad_request, message)
|
||||
end
|
||||
end
|
||||
|
||||
def create_player(conn, params) do
|
||||
case EventData.create_player(params) do
|
||||
:ok ->
|
||||
Events.broadcast()
|
||||
conn |> put_status(:created) |> json(StateService.read_state(true))
|
||||
|
||||
{:error, :bad_request, message} ->
|
||||
error(conn, :bad_request, message)
|
||||
|
||||
{:error, _, message} ->
|
||||
error(conn, :internal_server_error, message)
|
||||
end
|
||||
end
|
||||
|
||||
def update_player(conn, %{"id" => id} = params) do
|
||||
with {player_id, ""} <- Integer.parse(to_string(id)),
|
||||
result <- EventData.update_player(player_id, params),
|
||||
:ok <- result do
|
||||
Events.broadcast()
|
||||
json(conn, StateService.read_state(true))
|
||||
else
|
||||
:error -> error(conn, :bad_request, "invalid player id")
|
||||
{:error, :bad_request, message} -> error(conn, :bad_request, message)
|
||||
{:error, :not_found, message} -> error(conn, :not_found, message)
|
||||
{:error, _, message} -> error(conn, :internal_server_error, message)
|
||||
end
|
||||
end
|
||||
|
||||
def delete_player(conn, %{"id" => id}) do
|
||||
with {player_id, ""} <- Integer.parse(to_string(id)),
|
||||
:ok <- EventData.delete_player(player_id) do
|
||||
Events.broadcast()
|
||||
json(conn, StateService.read_state(true))
|
||||
else
|
||||
:error -> error(conn, :bad_request, "invalid player id")
|
||||
{:error, :not_found, message} -> error(conn, :not_found, message)
|
||||
{:error, _, message} -> error(conn, :internal_server_error, message)
|
||||
end
|
||||
end
|
||||
|
||||
def auto_group_players(conn, params) do
|
||||
groups = Map.get(params, "groups", [])
|
||||
|
||||
case EventData.auto_group_players(groups) do
|
||||
:ok ->
|
||||
Events.broadcast()
|
||||
json(conn, StateService.read_state(true))
|
||||
|
||||
{:error, :bad_request, message} ->
|
||||
error(conn, :bad_request, message)
|
||||
|
||||
{:error, _, message} ->
|
||||
error(conn, :internal_server_error, message)
|
||||
end
|
||||
end
|
||||
|
||||
def update_score(conn, %{"stage" => stage, "id" => id} = params) do
|
||||
with {:ok, normalized_stage} <- Stages.validate_stage(stage),
|
||||
{player_id, ""} <- Integer.parse(to_string(id)),
|
||||
score <- parse_score(Map.get(params, "score")),
|
||||
:ok <- EventData.update_score(normalized_stage, player_id, score) do
|
||||
Events.broadcast()
|
||||
json(conn, StateService.read_state(true))
|
||||
else
|
||||
{:error, message} -> error(conn, :bad_request, message)
|
||||
:error -> error(conn, :bad_request, "invalid player id")
|
||||
{:invalid_score, message} -> error(conn, :bad_request, message)
|
||||
{:error, :bad_request, message} -> error(conn, :bad_request, message)
|
||||
{:error, _, message} -> error(conn, :internal_server_error, message)
|
||||
end
|
||||
end
|
||||
|
||||
def reset_stage(conn, %{"stage" => stage} = params) do
|
||||
with {:ok, target_stages} <- Stages.resolve_reset_stages(stage),
|
||||
reset_proofs <- truthy?(Map.get(params, "resetProofs")),
|
||||
:ok <- EventData.reset_stage_scores(target_stages, reset_proofs) do
|
||||
Events.broadcast()
|
||||
json(conn, StateService.read_state(true))
|
||||
else
|
||||
{:error, message} -> error(conn, :bad_request, message)
|
||||
{:error, _, message} -> error(conn, :internal_server_error, message)
|
||||
end
|
||||
end
|
||||
|
||||
def update_score_proof(conn, %{"stage" => stage, "id" => id} = params) do
|
||||
with {:ok, normalized_stage} <- Stages.validate_stage(stage),
|
||||
{player_id, ""} <- Integer.parse(to_string(id)),
|
||||
image_data when is_binary(image_data) <- Map.get(params, "imageData"),
|
||||
:ok <- EventData.update_score_proof(normalized_stage, player_id, image_data) do
|
||||
Events.broadcast()
|
||||
json(conn, StateService.read_state(true))
|
||||
else
|
||||
{:error, message} -> error(conn, :bad_request, message)
|
||||
:error -> error(conn, :bad_request, "invalid player id")
|
||||
nil -> error(conn, :bad_request, "imageData is required")
|
||||
{:error, :bad_request, message} -> error(conn, :bad_request, message)
|
||||
{:error, _, message} -> error(conn, :internal_server_error, message)
|
||||
end
|
||||
end
|
||||
|
||||
def delete_score_proof(conn, %{"stage" => stage, "id" => id}) do
|
||||
with {:ok, normalized_stage} <- Stages.validate_stage(stage),
|
||||
{player_id, ""} <- Integer.parse(to_string(id)),
|
||||
:ok <- EventData.delete_score_proof(normalized_stage, player_id) do
|
||||
Events.broadcast()
|
||||
json(conn, StateService.read_state(true))
|
||||
else
|
||||
{:error, message} -> error(conn, :bad_request, message)
|
||||
:error -> error(conn, :bad_request, "invalid player id")
|
||||
{:error, _, message} -> error(conn, :internal_server_error, message)
|
||||
end
|
||||
end
|
||||
|
||||
def score_advice(conn, %{"stage" => stage, "id" => id}) do
|
||||
with {:ok, normalized_stage} <- Stages.validate_stage(stage),
|
||||
{player_id, ""} <- Integer.parse(to_string(id)),
|
||||
{:ok, image_data} <- EventData.load_score_proof_image(normalized_stage, player_id),
|
||||
{:ok, current_score} <- EventData.load_current_score(normalized_stage, player_id),
|
||||
{:ok, advice} <-
|
||||
Gemini.generate_score_advice(normalized_stage, player_id, image_data, current_score) do
|
||||
json(conn, advice)
|
||||
else
|
||||
{:error, :not_found} -> error(conn, :bad_request, "no proof image found for this score")
|
||||
{:error, :service_unavailable, message} -> error(conn, :service_unavailable, message)
|
||||
{:error, :bad_gateway, message} -> error(conn, :bad_gateway, message)
|
||||
{:error, message} when is_binary(message) -> error(conn, :bad_request, message)
|
||||
:error -> error(conn, :bad_request, "invalid player id")
|
||||
end
|
||||
end
|
||||
|
||||
defp parse_score(value) do
|
||||
cond do
|
||||
is_integer(value) ->
|
||||
value
|
||||
|
||||
is_binary(value) ->
|
||||
case Integer.parse(String.trim(value)) do
|
||||
{v, ""} -> v
|
||||
_ -> {:invalid_score, "invalid request body"}
|
||||
end
|
||||
|
||||
true ->
|
||||
{:invalid_score, "invalid request body"}
|
||||
end
|
||||
end
|
||||
|
||||
defp truthy?(value) do
|
||||
case value do
|
||||
true -> true
|
||||
1 -> true
|
||||
"1" -> true
|
||||
"true" -> true
|
||||
"yes" -> true
|
||||
"on" -> true
|
||||
_ -> false
|
||||
end
|
||||
end
|
||||
|
||||
defp error(conn, status, message) do
|
||||
conn
|
||||
|> put_status(status)
|
||||
|> json(%{"message" => message})
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user