diff --git a/src/App.tsx b/src/App.tsx
index f78b71e..175792d 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -57,6 +57,7 @@ import code, {
Custom404Options,
SubdomainRedirect,
RedirectRule,
+ RssOptions,
} from "./code";
import "./styles.css";
@@ -135,38 +136,6 @@ function FeatureCard({ icon, title, description }: FeatureCardProps) {
);
}
-interface SettingsSectionProps {
- title: string;
- subtitle?: string;
- children: ReactNode;
- isFirst?: boolean;
-}
-
-function SettingsSection({
- title,
- subtitle,
- children,
- isFirst,
-}: SettingsSectionProps) {
- return (
-
-
- {title}
-
- {subtitle && (
-
- {subtitle}
-
- )}
- {children}
-
- );
-}
-
export default function App() {
const [slugs, setSlugs] = useState([]);
const [myDomain, setMyDomain] = useState("");
@@ -228,6 +197,12 @@ export default function App() {
SubdomainRedirect[]
>([]);
const [redirectRules, setRedirectRules] = useState([]);
+ const [rss, setRss] = useState({
+ enabled: false,
+ title: "",
+ description: "",
+ language: "en-us",
+ });
function createInputHandler(
setter: React.Dispatch>,
@@ -328,6 +303,7 @@ export default function App() {
const handleCachingChange = createFieldHandler(setCaching, caching);
const handleCustomHtmlChange = createFieldHandler(setCustomHtml, customHtml);
const handleCustom404Change = createFieldHandler(setCustom404, custom404);
+ const handleRssChange = createFieldHandler(setRss, rss);
function addSubdomainRedirect(): void {
setSubdomainRedirects([
@@ -448,6 +424,7 @@ export default function App() {
custom404,
subdomainRedirects,
redirectRules,
+ rss,
};
const script = noError ? code(codeData) : undefined;
@@ -1455,6 +1432,82 @@ export default function App() {
+
+
+
+
+
+ RSS Feed
+
+
+ Generate RSS 2.0 feed at /rss.xml
+
+
+
+ handleRssChange("enabled", e.target.checked)
+ }
+ />
+
+
+
+ handleRssChange("title", e.target.value)}
+ value={rss.title}
+ variant="outlined"
+ size="small"
+ />
+
+ handleRssChange("description", e.target.value)
+ }
+ value={rss.description}
+ variant="outlined"
+ size="small"
+ />
+
+
+
+
+
+ RSS feed will include all pages defined in Pretty Links.
+ Add page metadata for better feed content.
+
+
+
+
diff --git a/src/code.ts b/src/code.ts
index 9a450e2..7e75aa5 100644
--- a/src/code.ts
+++ b/src/code.ts
@@ -73,6 +73,13 @@ export interface RedirectRule {
permanent: boolean;
}
+export interface RssOptions {
+ enabled: boolean;
+ title?: string;
+ description?: string;
+ language?: string;
+}
+
export interface CodeData {
myDomain: string;
notionUrl: string;
@@ -94,6 +101,7 @@ export interface CodeData {
custom404: Custom404Options;
subdomainRedirects: SubdomainRedirect[];
redirectRules: RedirectRule[];
+ rss: RssOptions;
}
function getId(url: string): string {
@@ -128,6 +136,7 @@ export default function code(data: CodeData): string {
custom404,
subdomainRedirects,
redirectRules,
+ rss,
} = data;
let url = myDomain.replace("https://", "").replace("http://", "");
if (url.slice(-1) === "/") url = url.slice(0, url.length - 1);
@@ -251,6 +260,15 @@ ${
.join("") || ""
} ];
+ /*
+ * Step 3.9: RSS feed configuration (optional)
+ * Generate an RSS 2.0 feed at /rss.xml for blog-style sites
+ */
+ const RSS_ENABLED = ${rss?.enabled || false};
+ const RSS_TITLE = '${rss?.title || ""}';
+ const RSS_DESCRIPTION = '${rss?.description || ""}';
+ const RSS_LANGUAGE = '${rss?.language || "en-us"}';
+
/* Step 4: enter a Google Font name, you can choose from https://fonts.google.com */
const GOOGLE_FONT = '${googleFont || ""}';
@@ -345,6 +363,48 @@ ${
return sitemap;
}
+ function generateRssFeed() {
+ const title = RSS_TITLE || MY_DOMAIN;
+ const description = RSS_DESCRIPTION || \`RSS feed for \${MY_DOMAIN}\`;
+ const buildDate = new Date().toUTCString();
+ let rss = '';
+ rss += '';
+ rss += '';
+ rss += \`\${escapeXml(title)}\`;
+ rss += \`https://\${MY_DOMAIN}\`;
+ rss += \`\${escapeXml(description)}\`;
+ rss += \`\${RSS_LANGUAGE}\`;
+ rss += \`\${buildDate}\`;
+ rss += \`\`;
+ slugs.forEach((slug) => {
+ const pageId = SLUG_TO_PAGE[slug];
+ const metadata = PAGE_METADATA[slug] || {};
+ const itemTitle = metadata.title || PAGE_TITLE || slug || 'Home';
+ const itemDescription = metadata.description || PAGE_DESCRIPTION || '';
+ const itemUrl = 'https://' + MY_DOMAIN + (slug ? '/' + slug : '');
+ rss += '- ';
+ rss += \`\${escapeXml(itemTitle)}\`;
+ rss += \`\${itemUrl}\`;
+ rss += \`\${itemUrl}\`;
+ if (itemDescription) {
+ rss += \`\${escapeXml(itemDescription)}\`;
+ }
+ rss += '
';
+ });
+ rss += '';
+ rss += '';
+ return rss;
+ }
+
+ function escapeXml(str) {
+ return str
+ .replace(/&/g, '&')
+ .replace(//g, '>')
+ .replace(/"/g, '"')
+ .replace(/'/g, ''');
+ }
+
const corsHeaders = {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET, HEAD, POST, PUT, OPTIONS',
@@ -429,6 +489,11 @@ ${
response.headers.set('content-type', 'application/xml');
return response;
}
+ if (url.pathname === '/rss.xml' && RSS_ENABLED) {
+ let response = new Response(generateRssFeed());
+ response.headers.set('content-type', 'application/rss+xml; charset=utf-8');
+ return response;
+ }
let response;
if (url.pathname.startsWith('/app') && url.pathname.endsWith('js')) {
response = await fetch(url.toString());