11import { NextRequest , NextResponse } from "next/server" ;
22import { SigstoreEntry } from "@/types/sigstore" ;
33import client from "@/lib/clickhouse" ;
4-
5- function escapeXml ( unsafe : string ) : string {
6- return unsafe . replace ( / [ < > & ' " ] / g, ( c ) => {
7- switch ( c ) {
8- case '<' : return '<' ;
9- case '>' : return '>' ;
10- case '&' : return '&' ;
11- case '\'' : return ''' ;
12- case '"' : return '"' ;
13- default : return c ;
14- }
15- } ) ;
16- }
4+ import { generateRSSFeed , getRSSResponse , getBaseUrl , RSSItem } from "@/lib/rss" ;
175
186interface FeedParams {
197 params : Promise < { org : string ; repo : string } > ;
@@ -47,15 +35,12 @@ async function getSigstoreEntriesForRepo(org: string, repo: string, limit: numbe
4735 return await resultSet . json < SigstoreEntry > ( ) ;
4836}
4937
50- function generateRSSFeed ( entries : SigstoreEntry [ ] , org : string , repo : string ) : string {
51- const now = new Date ( ) . toUTCString ( ) ;
52- const repositoryName = `${ org } /${ repo } ` ;
53- const feedUrl = `${ process . env . NEXT_PUBLIC_BASE_URL || 'https://transparency.cafe' } /api/sigstore/feed/${ org } /${ repo } ` ;
54- const webUrl = `${ process . env . NEXT_PUBLIC_BASE_URL || 'https://transparency.cafe' } /sigstore/search/${ encodeURIComponent ( repositoryName ) } ?type=github_repository` ;
55-
56- const items = entries . map ( entry => {
38+ function createSigstoreRepoRSSItems ( entries : SigstoreEntry [ ] , repositoryName : string ) : RSSItem [ ] {
39+ const baseUrl = getBaseUrl ( ) ;
40+
41+ return entries . map ( entry => {
5742 const pubDate = new Date ( entry . integrated_time ) . toUTCString ( ) ;
58- const entryUrl = `${ process . env . NEXT_PUBLIC_BASE_URL || 'https://transparency.cafe' } /sigstore/entry/${ entry . entry_uuid } ` ;
43+ const entryUrl = `${ baseUrl } /sigstore/entry/${ entry . entry_uuid } ` ;
5944
6045 const getTitle = ( ) => {
6146 return `Rekor: ${ repositoryName } ` ;
@@ -69,30 +54,14 @@ function generateRSSFeed(entries: SigstoreEntry[], org: string, repo: string): s
6954 return desc ;
7055 } ;
7156
72- return `
73- <item>
74- <title>${ escapeXml ( getTitle ( ) ) } </title>
75- <description>${ escapeXml ( getDescription ( ) ) } </description>
76- <link>${ escapeXml ( entryUrl ) } </link>
77- <guid isPermaLink="true">${ escapeXml ( entryUrl ) } </guid>
78- <pubDate>${ pubDate } </pubDate>
79- </item>` ;
80- } ) . join ( '\n' ) ;
81-
82- return `<?xml version="1.0" encoding="UTF-8"?>
83- <rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
84- <channel>
85- <title>${ escapeXml ( `Sigstore Rekor Entries for ${ repositoryName } ` ) } </title>
86- <description>${ escapeXml ( `Recent Sigstore Rekor transparency log entries for GitHub repository ${ repositoryName } ` ) } </description>
87- <link>${ escapeXml ( webUrl ) } </link>
88- <atom:link href="${ escapeXml ( feedUrl ) } " rel="self" type="application/rss+xml" />
89- <lastBuildDate>${ now } </lastBuildDate>
90- <generator>transparency.cafe</generator>
91- <language>en-us</language>
92- <ttl>60</ttl>
93- ${ items }
94- </channel>
95- </rss>` ;
57+ return {
58+ title : getTitle ( ) ,
59+ description : getDescription ( ) ,
60+ link : entryUrl ,
61+ guid : entryUrl ,
62+ pubDate : pubDate ,
63+ } ;
64+ } ) ;
9665}
9766
9867export async function GET ( request : NextRequest , { params } : FeedParams ) {
@@ -102,14 +71,23 @@ export async function GET(request: NextRequest, { params }: FeedParams) {
10271 const limit = Math . min ( parseInt ( searchParams . get ( 'limit' ) || '50' ) , 100 ) ;
10372
10473 const entries = await getSigstoreEntriesForRepo ( org , repo , limit ) ;
105- const rssXml = generateRSSFeed ( entries , org , repo ) ;
74+ const repositoryName = `${ org } /${ repo } ` ;
75+ const items = createSigstoreRepoRSSItems ( entries , repositoryName ) ;
76+
77+ const baseUrl = getBaseUrl ( ) ;
78+ const feedUrl = `${ baseUrl } /api/sigstore/feed/${ org } /${ repo } ` ;
79+ const webUrl = `${ baseUrl } /sigstore/search/${ encodeURIComponent ( repositoryName ) } ?type=github_repository` ;
10680
107- return new NextResponse ( rssXml , {
108- headers : {
109- 'Content-Type' : 'application/rss+xml; charset=utf-8' ,
110- 'Cache-Control' : 'public, max-age=300, s-maxage=300' , // Cache for 5 minutes
111- } ,
81+ const rssXml = generateRSSFeed ( {
82+ title : `Sigstore Rekor Entries for ${ repositoryName } ` ,
83+ description : `Recent Sigstore Rekor transparency log entries for GitHub repository ${ repositoryName } ` ,
84+ link : webUrl ,
85+ feedUrl : feedUrl ,
86+ items : items ,
11287 } ) ;
88+
89+ const response = getRSSResponse ( rssXml ) ;
90+ return new NextResponse ( response . body , { headers : response . headers } ) ;
11391 } catch ( error ) {
11492 console . error ( 'RSS feed generation error:' , error ) ;
11593 return NextResponse . json (
0 commit comments