Skip to content

Commit 4089409

Browse files
committed
more RSS tuning, add support for legacy items
1 parent 3683bb4 commit 4089409

File tree

9 files changed

+124
-39
lines changed

9 files changed

+124
-39
lines changed

src/components/changelog-next/Header.astro

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ const products = await uniqueProducts(notes);
3939
<p>
4040
New updates and improvements at Cloudflare.
4141
<a
42-
href="/changelog-next/rss.xml"
42+
href="/changelog-next/rss/index.xml"
4343
class="dark:!hover:text-[#79B1FF] -mx-2 whitespace-nowrap px-2 text-[#056DFF] no-underline hover:rounded-[4px] hover:bg-[#DCEBFF] hover:!text-[#056DFF] dark:text-[#79B1FF] dark:hover:bg-[#002B66] max-sm:block"
4444
>
4545
Subscribe to RSS

src/content/docs/ddos-protection/change-log/http/scheduled-changes.mdx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ head:
77
- tag: title
88
content: Scheduled changes — HTTP DDoS
99
changelog:
10-
date: 2024-04-29
10+
date: 2024-04-19
11+
scheduled: 2024-04-29
1112
---
1213

1314
import { RuleID } from "~/components"

src/content/docs/ddos-protection/change-log/network/scheduled-changes.mdx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ sidebar:
66
head:
77
- tag: title
88
content: Scheduled changes — Network-layer DDoS
9-
changelog:
10-
date: 2024-10-10
9+
# changelog:
10+
# scheduled: 2024-10-10
1111
---
1212

1313

src/content/docs/waf/change-log/2025-01-06.mdx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ pcx_content_type: changelog
55
sidebar:
66
order: 806
77
tableOfContents: false
8+
changelog:
9+
date: 2025-01-06
810
---
911

1012
import { RuleID } from "~/components";

src/content/docs/waf/change-log/scheduled-changes.mdx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@ sidebar:
66
order: 2
77
tableOfContents: false
88
changelog:
9-
date: 2025-02-10
9+
date: 2025-02-03
10+
scheduled: 2025-02-10
1011
---
1112

1213
import { RuleID } from "~/components";

src/pages/changelog-next/index.astro

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ const props = {
6060
<div>
6161
<h3 class="leading-none">
6262
<a
63-
href={`/changelog-next/${entry.id}/`}
63+
href={entry.data.link}
6464
class="mb-4 !text-black no-underline hover:underline"
6565
>
6666
{entry.data.title}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import rss from "@astrojs/rss";
2+
import type { APIRoute } from "astro";
3+
import { getChangelogs, getRSSItems } from "~/util/changelogs";
4+
5+
export const GET: APIRoute = async ({ locals }) => {
6+
const notes = await getChangelogs({});
7+
8+
const items = await getRSSItems({
9+
notes,
10+
locals,
11+
legacy: true,
12+
});
13+
14+
return rss({
15+
title: "Cloudflare release notes",
16+
description: `Updates to various Cloudflare products.`,
17+
site: "https://developers.cloudflare.com/changelog-next/",
18+
trailingSlash: false,
19+
items,
20+
});
21+
};

src/schemas/base.ts

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -103,9 +103,21 @@ export const baseSchema = z.object({
103103
),
104104
changelog: z
105105
.object({
106-
products: z.array(reference("products")).optional(),
107-
date: z.coerce.date().optional(),
106+
date: z.coerce
107+
.date()
108+
.describe(
109+
"An ISO-8601 date to indicate when this page should be published in RSS feeds.",
110+
),
111+
scheduled: z.coerce
112+
.date()
113+
.optional()
114+
.describe(
115+
"An ISO-8601 date to indicate this page is a future-dated entry in the RSS feed title. The publish date is still taken from the `date` field.",
116+
),
108117
})
109118
.strict()
110-
.optional(),
119+
.optional()
120+
.describe(
121+
"Used for pages in the 'docs' collection that should be used as changelog entries.",
122+
),
111123
});

src/util/changelogs.ts

Lines changed: 78 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,32 @@ import rehypeRemark from "rehype-remark";
1313
import remarkGfm from "remark-gfm";
1414
import remarkStringify from "remark-stringify";
1515

16+
function toISODate(date: Date) {
17+
return date.toISOString().slice(0, 10);
18+
}
19+
1620
type DocsToChangelogOptions = {
17-
name: string;
21+
/**
22+
* An optional title to be prefixed before the date.
23+
* This is only necessary if you require an extra product name.
24+
*
25+
* @example
26+
* `HTTP DDoS managed ruleset`
27+
*/
28+
name?: string;
29+
/**
30+
* Name of a product which must match a filename in the
31+
* src/content/products/ collection, without the
32+
* file extension.
33+
*
34+
* @example
35+
* `ddos-protection`
36+
*/
1837
product: string;
38+
/**
39+
* A changelog entry from the `getChangelogs({})` function.
40+
* @see {@link getChangelogs}
41+
*/
1942
entry: CollectionEntry<"docs">;
2043
};
2144

@@ -25,23 +48,31 @@ function docsToChangelog({
2548
entry,
2649
}: DocsToChangelogOptions): CollectionEntry<"changelogs-next"> {
2750
const { data } = entry;
28-
const { title, changelog } = data;
2951

30-
let date;
31-
if (changelog?.date) {
32-
date = changelog.date;
52+
// `data.changelog` will exist as the existence of this
53+
// property is part of the `getChangelogs` filter.
54+
const date = data.changelog!.date;
55+
const scheduled = data.changelog!.scheduled;
56+
57+
const iso8601 = toISODate(date);
58+
59+
let title;
60+
if (scheduled) {
61+
title = `Scheduled for ${toISODate(scheduled)}`;
3362
} else {
34-
date = new Date(data.title.split(" ")[0]);
63+
title = iso8601;
3564
}
3665

37-
const iso8601 = date.toISOString().slice(0, 10);
66+
if (name) {
67+
title = `${name} - ${title}`;
68+
}
3869

3970
return {
4071
...entry,
4172
collection: "changelogs-next",
4273
data: {
4374
title,
44-
description: `${name} - ${iso8601}`,
75+
description: `${name} - ${toISODate(date)}`,
4576
date,
4677
products: [{ collection: "products", id: product }],
4778
link: `/${entry.id}/`,
@@ -66,8 +97,7 @@ export async function getChangelogs({
6697

6798
const ddosHttp = await getCollection("docs", (e) => {
6899
return (
69-
e.id.startsWith("ddos-protection/change-log/http/") &&
70-
e.data.pcx_content_type === "changelog"
100+
e.id.startsWith("ddos-protection/change-log/http/") && e.data.changelog
71101
);
72102
});
73103

@@ -83,8 +113,7 @@ export async function getChangelogs({
83113

84114
const ddosNetwork = await getCollection("docs", (e) => {
85115
return (
86-
e.id.startsWith("ddos-protection/change-log/network/") &&
87-
e.data.pcx_content_type === "changelog"
116+
e.id.startsWith("ddos-protection/change-log/network/") && e.data.changelog
88117
);
89118
});
90119

@@ -99,16 +128,12 @@ export async function getChangelogs({
99128
.forEach((e) => entries.push(e));
100129

101130
const waf = await getCollection("docs", (e) => {
102-
return (
103-
e.id.startsWith("waf/change-log/") &&
104-
e.data.pcx_content_type === "changelog"
105-
);
131+
return e.id.startsWith("waf/change-log/") && e.data.changelog;
106132
});
107133

108134
waf
109135
.map((e) =>
110136
docsToChangelog({
111-
name: "WAF",
112137
product: "waf",
113138
entry: e,
114139
}),
@@ -136,41 +161,51 @@ type GetRSSItemsOptions = {
136161
locals: App.Locals;
137162
/**
138163
* Returns Markdown in the `<content:encoded>` field instead of HTML.
139-
* Markdown will contain `\n` in the output, as fast-xml-parser outputs
140-
* single-line strings.
141-
*
142-
* @example
143-
* # heading\n\ntext\ntext
144164
*/
145165
markdown?: boolean;
166+
/**
167+
* Creates items in the same way as historic changelogs. The behaviour of
168+
* this option should not be changed, all improvements should be made
169+
* to the normal RSS feeds.
170+
*
171+
* The differences between normal items and legacy items are:
172+
*
173+
* - HTML is not filtered by our `filter-elements` plugin.
174+
* - HTML is in the `description` element rather than `content:encoded`.
175+
* - Only the first product can be shown in the title and the custom `<product>` element.
176+
*/
177+
legacy?: boolean;
146178
};
147179

148180
export async function getRSSItems({
149181
notes,
150182
locals,
151183
markdown,
184+
legacy,
152185
}: GetRSSItemsOptions): Promise<Array<RSSFeedItem>> {
153186
return await Promise.all(
154187
notes.map(async (note) => {
155188
const { title, description, date, products, link } = note.data;
156189

157190
const productEntries = await getEntries(products);
158-
const productTitles = productEntries.map((p) => p.data.name);
191+
const productTitles = productEntries.map((p) => p.data.name as string);
159192

160193
const html = await entryToString(note, locals);
161194

162-
let plugins: PluggableList = [
163-
rehypeParse,
164-
rehypeBaseUrl,
165-
rehypeFilterElements,
166-
];
195+
const plugins: PluggableList = [rehypeParse, rehypeBaseUrl];
196+
197+
if (!legacy) {
198+
plugins.push(...[rehypeFilterElements]);
199+
}
167200

168201
if (markdown) {
169-
plugins = plugins.concat([rehypeRemark, remarkGfm, remarkStringify]);
202+
plugins.push(...[rehypeRemark, remarkGfm, remarkStringify]);
170203
} else {
171-
plugins = plugins.concat([rehypeStringify]);
204+
plugins.push(...[rehypeStringify]);
172205
}
173206

207+
console.log(plugins);
208+
174209
const file = await unified()
175210
.data("settings", {
176211
fragment: true,
@@ -180,6 +215,19 @@ export async function getRSSItems({
180215

181216
const content = String(file).trim();
182217

218+
if (legacy) {
219+
// Only a single product title is supported in legacy items.
220+
const productTitle = productTitles.at(0);
221+
222+
return {
223+
title: `${productTitle} - ${title}`,
224+
description: content,
225+
pubDate: date,
226+
link,
227+
customData: `<product>${productTitle}</product>`,
228+
};
229+
}
230+
183231
return {
184232
title: `${productTitles.join(", ")} - ${title}`,
185233
description,

0 commit comments

Comments
 (0)