defmodule Asciinema.Api.AsciicastController do use Asciinema.Web, :controller import Asciinema.Auth, only: [get_basic_auth: 1, put_basic_auth: 3] alias Asciinema.{Asciicasts, Users, User} plug :parse_v0_params plug :authenticate def create(conn, %{"asciicast" => %Plug.Upload{} = upload}) do do_create(conn, upload) end def create(conn, %{"asciicast" => %{"meta" => %{}, "stdout" => %Plug.Upload{}, "stdout_timing" => %Plug.Upload{}} = asciicast_params}) do do_create(conn, asciicast_params) end defp do_create(conn, params) do user = conn.assigns.current_user user_agent = conn |> get_req_header("user-agent") |> List.first case Asciicasts.create_asciicast(user, params, user_agent) do {:ok, asciicast} -> url = asciicast_url(conn, :show, asciicast) conn |> put_status(:created) |> put_resp_header("location", url) |> text(url) {:error, :parse_error} -> conn |> put_status(:bad_request) |> text("This doesn't look like a valid asciicast file") {:error, :unknown_format} -> conn |> put_status(:unsupported_media_type) |> text("Format not supported") {:error, %Ecto.Changeset{} = changeset} -> conn |> put_status(:unprocessable_entity) |> render("error.json", changeset: changeset) end end defp parse_v0_params(%Plug.Conn{params: %{"asciicast" => %{"meta" => %Plug.Upload{path: meta_path}}}} = conn, _) do with {:ok, json} <- File.read(meta_path), {:ok, attrs} <- Poison.decode(json), {:ok, meta} <- extract_v0_attrs(attrs) do conn |> put_param(["asciicast", "meta"], meta) |> put_basic_auth(attrs["username"], attrs["user_token"]) else {:error, :invalid} -> send_resp(conn, 400, "") end end defp parse_v0_params(conn, _), do: conn defp put_param(%Plug.Conn{params: params} = conn, path, value) do params = put_in(params, path, value) %{conn | params: params} end defp extract_v0_attrs(attrs) do attrs = Map.merge( Map.take(attrs, ["command", "duration", "shell", "title", "uname"]), %{"terminal_columns" => get_in(attrs, ["term", "columns"]), "terminal_lines" => get_in(attrs, ["term", "lines"]), "terminal_type" => get_in(attrs, ["term", "type"])} ) {:ok, attrs} end defp authenticate(conn, _opts) do with {username, api_token} <- get_basic_auth(conn), %User{} = user <- Users.get_user_with_api_token(username, api_token) do assign(conn, :current_user, user) else _otherwise -> conn |> send_resp(401, "Invalid or revoked recorder token") |> halt() end end end