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, :forbidden} -> conn |> put_status(:forbidden) |> json(%{error: "Access denied to this maintenance 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, :forbidden} -> conn |> put_status(:forbidden) |> json(%{error: "Access denied to this maintenance window"}) {: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, :forbidden} -> conn |> put_status(:forbidden) |> json(%{error: "Access denied to this maintenance window"}) {: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