fix: issue #15 calendar index not properly created, also some smaller bugs

This commit is contained in:
2026-07-05 17:33:48 +02:00
parent bb82f1d4a1
commit d944c07838
13 changed files with 218 additions and 70 deletions

View File

@@ -1355,7 +1355,7 @@ defmodule BDS.Generation do
["sitemap.xml"] ->
:core
["feed.xml"] ->
["rss.xml"] ->
:core
["atom.xml"] ->

View File

@@ -375,7 +375,7 @@ defmodule BDS.Generation.Outputs do
main_static_outputs =
[
{"404.html", render_not_found_output(plan, language)},
{"feed.xml", render_feed(plan, language, published_posts)},
{"rss.xml", render_feed(plan, language, published_posts)},
{"atom.xml", render_atom(plan, language, published_posts)},
{"calendar.json", render_calendar(published_posts)}
]
@@ -399,7 +399,7 @@ defmodule BDS.Generation.Outputs do
[
{Path.join(localized_language, "404.html"),
render_not_found_output(plan, localized_language)},
{Path.join(localized_language, "feed.xml"),
{Path.join(localized_language, "rss.xml"),
render_feed(plan, localized_language, localized_source_posts)},
{Path.join(localized_language, "atom.xml"),
render_atom(plan, localized_language, localized_source_posts)}

View File

@@ -237,7 +237,7 @@ defmodule BDS.Generation.Paths do
@spec sitemap_route_output?(String.t()) :: boolean()
def sitemap_route_output?("404.html"), do: false
def sitemap_route_output?("feed.xml"), do: false
def sitemap_route_output?("rss.xml"), do: false
def sitemap_route_output?("atom.xml"), do: false
def sitemap_route_output?("calendar.json"), do: false
def sitemap_route_output?(relative_path), do: String.ends_with?(relative_path, ".html")

View File

@@ -212,13 +212,19 @@ defmodule BDS.Generation.Sitemap do
"<feed><title>#{xml_escape(plan.project_name)} (#{xml_escape(language || "default")})</title>#{entries}</feed>"
end
@doc "Render a JSON calendar of all published posts."
@doc """
Render the JSON calendar archive consumed by the calendar runtime:
post counts keyed by year ("2024"), month ("2024-05"), and day ("2024-05-17").
"""
@spec render_calendar([map()]) :: String.t()
def render_calendar(published_posts) do
published_posts
|> Enum.map(fn post ->
%{date: Paths.local_date_iso8601!(post.created_at), slug: post.slug, title: post.title}
end)
day_keys = Enum.map(published_posts, &Paths.local_date_iso8601!(&1.created_at))
%{
years: Enum.frequencies_by(day_keys, &binary_part(&1, 0, 4)),
months: Enum.frequencies_by(day_keys, &binary_part(&1, 0, 7)),
days: Enum.frequencies(day_keys)
}
|> Jason.encode!()
end

View File

@@ -201,16 +201,21 @@ defmodule BDS.Preview do
query_params: query_params
)
:static ->
serve_file(safe_join(Path.join(server.data_dir, "html"), relative_path),
server: server,
query_params: query_params
)
:generated ->
# Content is always rendered dynamically; generated files on
# disk go stale and must never be served by the preview.
case BDS.Preview.Router.render_route(server.project_id, request_path) do
{:ok, response} ->
{:ok, apply_response_overrides(response, query_params)}
:not_matched ->
serve_file(safe_join(Path.join(server.data_dir, "html"), relative_path),
server: server,
query_params: query_params
)
render_not_found_response(server.project_id, query_params)
end
end
end
@@ -319,6 +324,9 @@ defmodule BDS.Preview do
match?(["media" | _], segments) ->
{:ok, Path.join(tl(segments)), :media}
match?(["pagefind" | _], segments) ->
{:ok, Path.join(segments), :static}
normalized == "/" ->
{:ok, "index.html", :generated}

View File

@@ -4,6 +4,7 @@ defmodule BDS.Preview.Router do
import Ecto.Query
alias BDS.Generation.Paths
alias BDS.Generation.Sitemap
alias BDS.MapUtils
alias BDS.Metadata, as: ProjectMetadata
alias BDS.Posts
@@ -23,6 +24,9 @@ defmodule BDS.Preview.Router do
| {:year, integer(), pos_integer()}
| {:month, integer(), integer(), pos_integer()}
| {:day, integer(), integer(), integer(), pos_integer()}
| :calendar
| :feed
| :atom
| :not_matched
@spec render_route(String.t(), String.t()) :: {:ok, map()} | :not_matched
@@ -40,6 +44,28 @@ defmodule BDS.Preview.Router do
:not_matched ->
:not_matched
:calendar ->
posts = load_published_list_posts(project_id, metadata)
{:ok, %{content_type: "application/json", body: Sitemap.render_calendar(posts)}}
:feed ->
posts = load_published_list_posts(project_id, metadata)
{:ok,
%{
content_type: "application/rss+xml",
body: Sitemap.render_feed(feed_plan(metadata), effective_language, posts)
}}
:atom ->
posts = load_published_list_posts(project_id, metadata)
{:ok,
%{
content_type: "application/atom+xml",
body: Sitemap.render_atom(feed_plan(metadata), effective_language, posts)
}}
route ->
case render(project_id, route, effective_language, main_language, metadata) do
{:ok, body} ->
@@ -53,6 +79,9 @@ defmodule BDS.Preview.Router do
@spec match_route([String.t()]) :: route()
def match_route([]), do: {:home, 1}
def match_route(["calendar.json"]), do: :calendar
def match_route(["rss.xml"]), do: :feed
def match_route(["atom.xml"]), do: :atom
def match_route(["page", n]), do: {:home, parse_page(n)}
def match_route(["category", name]), do: {:category, URI.decode(name), 1}
@@ -130,6 +159,14 @@ defmodule BDS.Preview.Router do
## Rendering
defp feed_plan(metadata) do
%{
base_url: metadata.public_url,
project_name: metadata.name,
language: metadata.main_language || "en"
}
end
defp render(project_id, {:home, page_number}, language, main_language, metadata) do
posts = load_published_list_posts(project_id, metadata)
posts = maybe_resolve_language(posts, language, main_language, project_id)