201 lines
4.6 KiB
Elixir
201 lines
4.6 KiB
Elixir
defmodule Blogex.Router do
|
|
@moduledoc """
|
|
Plug router that serves blog pages and feeds.
|
|
|
|
Serves HTML to browsers (Accept: text/html) and JSON to API clients.
|
|
|
|
Mount this in your host app's router:
|
|
|
|
# In your Phoenix router
|
|
scope "/blog" do
|
|
pipe_through :browser
|
|
forward "/engineering", Blogex.Router, blog: MyApp.EngineeringBlog
|
|
forward "/releases", Blogex.Router, blog: MyApp.ReleaseNotes
|
|
end
|
|
|
|
Or use the convenience macro:
|
|
|
|
import Blogex.Router, only: [blogex_routes: 2]
|
|
|
|
scope "/blog" do
|
|
pipe_through :browser
|
|
blogex_routes "/engineering", MyApp.EngineeringBlog
|
|
blogex_routes "/releases", MyApp.ReleaseNotes
|
|
end
|
|
|
|
## Routes served
|
|
|
|
* `GET /` — post index (paginated)
|
|
* `GET /:slug` — individual post
|
|
* `GET /tag/:tag` — posts by tag
|
|
* `GET /feed.xml` — RSS feed
|
|
* `GET /atom.xml` — Atom feed
|
|
"""
|
|
|
|
use Plug.Router
|
|
|
|
plug :match
|
|
plug :dispatch
|
|
|
|
get "/feed.xml" do
|
|
blog = conn.private[:blogex_blog]
|
|
base_url = Blogex.Router.Helpers.base_url(conn)
|
|
|
|
xml = Blogex.Feed.rss(blog, base_url)
|
|
|
|
conn
|
|
|> put_resp_content_type("application/rss+xml")
|
|
|> send_resp(200, xml)
|
|
end
|
|
|
|
get "/atom.xml" do
|
|
blog = conn.private[:blogex_blog]
|
|
base_url = Blogex.Router.Helpers.base_url(conn)
|
|
|
|
xml = Blogex.Feed.atom(blog, base_url)
|
|
|
|
conn
|
|
|> put_resp_content_type("application/atom+xml")
|
|
|> send_resp(200, xml)
|
|
end
|
|
|
|
get "/tag/:tag" do
|
|
blog = conn.private[:blogex_blog]
|
|
posts = blog.posts_by_tag(tag)
|
|
|
|
if wants_html?(conn) do
|
|
assigns = %{
|
|
blog_title: blog.title(),
|
|
tag: tag,
|
|
posts: posts,
|
|
base_path: blog.base_path()
|
|
}
|
|
|
|
send_html(conn, Blogex.Layout.tag_page(assigns))
|
|
else
|
|
conn
|
|
|> put_resp_content_type("application/json")
|
|
|> send_resp(
|
|
200,
|
|
Jason.encode!(%{
|
|
blog: blog.blog_id(),
|
|
tag: tag,
|
|
posts: Enum.map(posts, &post_json/1)
|
|
})
|
|
)
|
|
end
|
|
end
|
|
|
|
get "/:slug" do
|
|
blog = conn.private[:blogex_blog]
|
|
|
|
case blog.get_post(slug) do
|
|
nil ->
|
|
conn |> send_resp(404, "Post not found")
|
|
|
|
post ->
|
|
if wants_html?(conn) do
|
|
assigns = %{post: post, base_path: blog.base_path()}
|
|
send_html(conn, Blogex.Layout.show_page(assigns))
|
|
else
|
|
conn
|
|
|> put_resp_content_type("application/json")
|
|
|> send_resp(200, Jason.encode!(post_json(post)))
|
|
end
|
|
end
|
|
end
|
|
|
|
get "/" do
|
|
blog = conn.private[:blogex_blog]
|
|
page = (conn.params["page"] || "1") |> String.to_integer()
|
|
result = blog.paginate(page)
|
|
|
|
if wants_html?(conn) do
|
|
assigns = %{
|
|
blog_title: blog.title(),
|
|
blog_description: blog.description(),
|
|
posts: result.entries,
|
|
base_path: blog.base_path(),
|
|
page: result.page,
|
|
total_pages: result.total_pages
|
|
}
|
|
|
|
send_html(conn, Blogex.Layout.index_page(assigns))
|
|
else
|
|
conn
|
|
|> put_resp_content_type("application/json")
|
|
|> send_resp(
|
|
200,
|
|
Jason.encode!(%{
|
|
blog: blog.blog_id(),
|
|
title: blog.title(),
|
|
posts: Enum.map(result.entries, &post_json/1),
|
|
page: result.page,
|
|
total_pages: result.total_pages,
|
|
total_entries: result.total_entries
|
|
})
|
|
)
|
|
end
|
|
end
|
|
|
|
match _ do
|
|
send_resp(conn, 404, "Not found")
|
|
end
|
|
|
|
@doc false
|
|
def init(opts), do: opts
|
|
|
|
@doc false
|
|
def call(conn, opts) do
|
|
blog = Keyword.fetch!(opts, :blog)
|
|
|
|
conn
|
|
|> Plug.Conn.put_private(:blogex_blog, blog)
|
|
|> super(opts)
|
|
end
|
|
|
|
defp wants_html?(conn) do
|
|
case Plug.Conn.get_req_header(conn, "accept") do
|
|
[accept | _] -> String.contains?(accept, "text/html")
|
|
_ -> false
|
|
end
|
|
end
|
|
|
|
defp send_html(conn, rendered) do
|
|
html =
|
|
rendered
|
|
|> Phoenix.HTML.Safe.to_iodata()
|
|
|> IO.iodata_to_binary()
|
|
|
|
conn
|
|
|> put_resp_content_type("text/html")
|
|
|> send_resp(200, html)
|
|
end
|
|
|
|
defp post_json(post) do
|
|
%{
|
|
id: post.id,
|
|
title: post.title,
|
|
author: post.author,
|
|
date: Date.to_iso8601(post.date),
|
|
description: post.description,
|
|
tags: post.tags,
|
|
body: post.body
|
|
}
|
|
end
|
|
|
|
defmodule Helpers do
|
|
@moduledoc false
|
|
|
|
def base_url(conn) do
|
|
scheme = if conn.scheme == :https, do: "https", else: "http"
|
|
port_suffix = port_suffix(conn.scheme, conn.port)
|
|
"#{scheme}://#{conn.host}#{port_suffix}"
|
|
end
|
|
|
|
defp port_suffix(:http, 80), do: ""
|
|
defp port_suffix(:https, 443), do: ""
|
|
defp port_suffix(_, port), do: ":#{port}"
|
|
end
|
|
end
|