defmodule FrenzyWeb.FeverController do use FrenzyWeb, :controller alias Frenzy.{Repo, User, Group, Feed, Item} import Ecto.Query plug :api_check def api_check(conn, _) do if Map.has_key?(conn.params, "api") do conn else conn |> resp(400, "Invalid request") |> halt() end end def get(conn, _params) do json(conn, %{api_version: 2, auth: 0}) end def post(conn, %{"api_key" => api_key} = params) do case validate_key(api_key) do :invalid -> resp(conn, 401, "Invalid API key") {:ok, user} -> user = Repo.preload(user, groups: [:feeds]) res = fever_response(user, params) json(conn, res) end end defp validate_key(api_key) do case Repo.get_by(User, fever_auth_token: api_key) do nil -> :invalid user -> {:ok, user} end # auth = Application.get_env(:frenzy, :auth) # username = auth[:username] # password = auth[:password] # expected = :crypto.hash(:md5, "#{username}:#{password}") |> Base.encode16(case: :lower) # case api_key |> String.downcase() do # ^expected -> :ok # _ -> :invalid # end end defp fever_response(user, params) do %{api_version: 2, auth: 1} |> mark(user, params) |> unread_recently_read(user, params) |> feeds(user, params) |> groups(user, params) |> feeds_groups(user, params) |> favicons(user, params) |> links(user, params) |> unread(user, params) |> saved(user, params) |> items(user, params) end defp get_user_item(user, id) do item = Repo.get(Item, id) |> Repo.preload(:feed) if not is_nil(item) do feeds = Enum.flat_map(user.groups, fn g -> g.feeds end) if Enum.any?(feeds, fn f -> f.id == item.feed_id end) do {:ok, item} else {:error, "item does not belong to given user"} end else {:error, "item does not exist"} end end defp mark(res, user, %{"mark" => "item", "id" => id, "as" => as}) do with {:ok, item} <- get_user_item(user, id) do diff = case as do "read" -> %{read: true, read_date: Timex.now()} "unread" -> %{read: false, read_date: nil} _ -> %{} end changeset = Item.changeset(item, diff) Repo.update(changeset) end res end defp mark(res, _, _), do: res defp unread_recently_read(res, user, %{"unread_recently_read" => 1}) do feed_ids = user.groups |> Enum.flat_map(fn g -> g.feeds end) |> Enum.map(fn f -> f.id end) Repo.all( from i in Item, where: i.feed_id in ^feed_ids, where: i.read, where: i.read_date >= from_now(-1, "hour") ) |> Enum.map(fn i -> Item.changeset(i, %{read: false, read_date: nil}) end) |> Enum.map(&Repo.update/1) res end defp unread_recently_read(res, _, _), do: res defp feeds(res, user, %{"feeds" => _}) do feeds = user.groups |> Enum.flat_map(fn g -> g.feeds end) |> Enum.map(&Feed.to_fever/1) res |> Map.put(:feeds, feeds) end defp feeds(res, _, _), do: res defp groups(res, user, %{"groups" => _}) do groups = user.groups |> Enum.map(&Group.to_fever_group/1) res |> Map.put(:groups, groups) end defp groups(res, _, _), do: res defp feeds_groups(res, user, params) do if Map.has_key?(params, "feeds") or Map.has_key?(params, "groups") do feeds_groups = user.groups |> Enum.map(&Group.to_fever_feeds_group/1) res |> Map.put(:feeds_groups, feeds_groups) else res end end defp favicons(res, _user, %{"favicons" => _}) do res |> Map.put(:favicons, []) end defp favicons(res, _, _), do: res defp links(res, _user, %{"links" => _}) do res |> Map.put(:links, []) end defp links(res, _, _), do: res defp unread(res, user, %{"unread_item_ids" => _}) do feed_ids = user.groups |> Enum.flat_map(fn g -> g.feeds end) |> Enum.map(fn f -> f.id end) unread = Repo.all(from i in Item, where: i.feed_id in ^feed_ids, where: [read: false]) |> Enum.map(fn item -> item.id end) |> Enum.join(",") res |> Map.put(:unread_item_ids, unread) end defp unread(res, _, _), do: res defp saved(res, _user, %{"saved_item_ids" => _}) do res |> Map.put(:saved_item_ids, "") end defp saved(res, _, _), do: res defp items(res, user, %{"items" => _} = params) do feed_ids = user.groups |> Enum.flat_map(fn g -> g.feeds end) |> Enum.map(fn f -> f.id end) items = cond do Map.has_key?(params, "with_ids") -> params["with_ids"] |> String.split(",") |> Enum.map(fn id -> {id, _} = id |> String.trim() |> Integer.parse() item = Repo.get(Item, id) if item.feed_id in feed_ids do item else nil end end) |> Enum.reject(&is_nil/1) Map.has_key?(params, "since_id") -> since = Repo.get(Item, params["since_id"]) {since, _} = Integer.parse(since) Repo.all( from i in Item, where: i.feed_id in ^feed_ids, where: i.inserted_at > ^since.inserted_at, order_by: [asc: :id], limit: 50 ) Map.has_key?(params, "max_id") -> max = Repo.get(Item, params["max_id"]) {max, _} = Integer.parse(max) Repo.all( from i in Item, where: i.feed_id in ^feed_ids, where: i.inserted_at < ^max.inserted_at, order_by: [desc: :id], limit: 50 ) true -> [] end items = items |> Enum.reject(&is_nil/1) |> Enum.reject(fn item -> item.tombstone end) |> Enum.map(&Item.to_fever/1) res |> Map.put(:items, items) |> Map.put(:total_items, Enum.count(items)) end defp items(res, _, _), do: res end