Use Repo.get_by(schema, id: id, organization_id: organization_id) instead of Repo.get + pattern match so that resources from wrong orgs return :not_found instead of :forbidden, preventing org membership discovery.
148 lines
4.2 KiB
Elixir
148 lines
4.2 KiB
Elixir
defmodule ToweropsWeb.Api.V1.MaintenanceWindowsController do
|
|
@moduledoc """
|
|
API controller for managing maintenance windows.
|
|
|
|
All endpoints require API token authentication and operations are scoped
|
|
to the organization associated with the token.
|
|
"""
|
|
use ToweropsWeb, :controller
|
|
|
|
import ToweropsWeb.Api.ErrorHelpers, only: [translate_errors: 1]
|
|
|
|
alias Towerops.Maintenance
|
|
alias Towerops.Maintenance.MaintenanceWindow
|
|
alias ToweropsWeb.ScopedResource
|
|
|
|
@doc "GET /api/v1/maintenance_windows"
|
|
def index(conn, params) do
|
|
organization_id = conn.assigns.current_organization_id
|
|
filter = Map.get(params, "filter")
|
|
|
|
opts =
|
|
case filter do
|
|
"active" -> [filter: :active]
|
|
"upcoming" -> [filter: :upcoming]
|
|
"past" -> [filter: :past]
|
|
_ -> []
|
|
end
|
|
|
|
windows =
|
|
organization_id
|
|
|> Maintenance.list_windows(opts)
|
|
|> Enum.map(&format_window/1)
|
|
|
|
json(conn, %{maintenance_windows: windows})
|
|
end
|
|
|
|
@doc "POST /api/v1/maintenance_windows"
|
|
def create(conn, %{"maintenance_window" => window_params}) do
|
|
organization_id = conn.assigns.current_organization_id
|
|
user_id = conn.assigns.current_user.id
|
|
|
|
attrs =
|
|
window_params
|
|
|> Map.put("organization_id", organization_id)
|
|
|> Map.put("created_by_id", user_id)
|
|
|
|
case Maintenance.create_window(attrs) do
|
|
{:ok, window} ->
|
|
conn
|
|
|> put_status(:created)
|
|
|> json(format_window(window))
|
|
|
|
{:error, %Ecto.Changeset{} = changeset} ->
|
|
conn
|
|
|> put_status(:unprocessable_entity)
|
|
|> json(%{errors: translate_errors(changeset)})
|
|
end
|
|
end
|
|
|
|
def create(conn, _params) do
|
|
conn
|
|
|> put_status(:bad_request)
|
|
|> json(%{error: "Missing 'maintenance_window' parameter"})
|
|
end
|
|
|
|
@doc "GET /api/v1/maintenance_windows/:id"
|
|
def show(conn, %{"id" => id}) do
|
|
organization_id = conn.assigns.current_organization_id
|
|
|
|
case ScopedResource.fetch(MaintenanceWindow, id, organization_id) do
|
|
{:ok, window} ->
|
|
json(conn, format_window(window))
|
|
|
|
{:error, :not_found} ->
|
|
conn
|
|
|> put_status(:not_found)
|
|
|> json(%{error: "Maintenance window not found"})
|
|
end
|
|
end
|
|
|
|
@doc "PATCH /api/v1/maintenance_windows/:id"
|
|
def update(conn, %{"id" => id, "maintenance_window" => window_params}) do
|
|
organization_id = conn.assigns.current_organization_id
|
|
|
|
case ScopedResource.fetch(MaintenanceWindow, id, organization_id) do
|
|
{:ok, window} ->
|
|
case Maintenance.update_window(window, window_params) do
|
|
{:ok, updated} ->
|
|
json(conn, format_window(updated))
|
|
|
|
{:error, %Ecto.Changeset{} = changeset} ->
|
|
conn
|
|
|> put_status(:unprocessable_entity)
|
|
|> json(%{errors: translate_errors(changeset)})
|
|
end
|
|
|
|
{:error, :not_found} ->
|
|
conn
|
|
|> put_status(:not_found)
|
|
|> json(%{error: "Maintenance window not found"})
|
|
end
|
|
end
|
|
|
|
def update(conn, _params) do
|
|
conn
|
|
|> put_status(:bad_request)
|
|
|> json(%{error: "Missing 'maintenance_window' parameter"})
|
|
end
|
|
|
|
@doc "DELETE /api/v1/maintenance_windows/:id"
|
|
def delete(conn, %{"id" => id}) do
|
|
organization_id = conn.assigns.current_organization_id
|
|
|
|
case ScopedResource.fetch(MaintenanceWindow, id, organization_id) do
|
|
{:ok, window} ->
|
|
case Maintenance.delete_window(window) do
|
|
{:ok, _} ->
|
|
json(conn, %{success: true})
|
|
|
|
{:error, %Ecto.Changeset{} = changeset} ->
|
|
conn
|
|
|> put_status(:unprocessable_entity)
|
|
|> json(%{errors: translate_errors(changeset)})
|
|
end
|
|
|
|
{:error, :not_found} ->
|
|
conn
|
|
|> put_status(:not_found)
|
|
|> json(%{error: "Maintenance window not found"})
|
|
end
|
|
end
|
|
|
|
defp format_window(window) do
|
|
%{
|
|
id: window.id,
|
|
name: window.name,
|
|
reason: window.reason,
|
|
starts_at: window.starts_at,
|
|
ends_at: window.ends_at,
|
|
recurring: window.recurring,
|
|
recurrence_rule: window.recurrence_rule,
|
|
suppress_alerts: window.suppress_alerts,
|
|
site_id: window.site_id,
|
|
device_id: window.device_id,
|
|
inserted_at: window.inserted_at
|
|
}
|
|
end
|
|
end
|