From b37575f369a8d4d7fa888c026a04efa995a13fd6 Mon Sep 17 00:00:00 2001 From: shigahi Date: Thu, 22 Jan 2026 23:46:23 +0100 Subject: [PATCH] Add custom redirect rules (301/302) - Add RedirectRule interface to code.ts - Add REDIRECT_RULES configuration in generated Worker script - Implement redirect handling in fetchAndApply function - Add URL Redirect Rules UI section with from/to/permanent fields Closes #32 --- src/App.tsx | 116 +++++++++++++++++++++++++++++++++++++++++++++++++++- src/code.ts | 33 +++++++++++++++ 2 files changed, 148 insertions(+), 1 deletion(-) diff --git a/src/App.tsx b/src/App.tsx index 6437c31..b70816d 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -47,6 +47,7 @@ import code, { CustomHtmlOptions, Custom404Options, SubdomainRedirect, + RedirectRule, } from "./code"; import "./styles.css"; @@ -171,6 +172,7 @@ export default function App() { const [subdomainRedirects, setSubdomainRedirects] = useState< SubdomainRedirect[] >([]); + const [redirectRules, setRedirectRules] = useState([]); function createInputHandler( setter: React.Dispatch>, @@ -336,6 +338,29 @@ export default function App() { setCopied(false); } + function addRedirectRule(): void { + setRedirectRules([...redirectRules, { from: "", to: "", permanent: true }]); + setCopied(false); + } + + function deleteRedirectRule(index: number): void { + setRedirectRules(redirectRules.filter((_, i) => i !== index)); + setCopied(false); + } + + function handleRedirectRuleChange( + index: number, + field: keyof RedirectRule, + value: string | boolean, + ): void { + setRedirectRules( + redirectRules.map((rule, i) => + i === index ? { ...rule, [field]: value } : rule, + ), + ); + setCopied(false); + } + function toggleSlugMetadata(index: number): void { setSlugMetadataExpanded({ ...slugMetadataExpanded, @@ -403,6 +428,7 @@ export default function App() { customHtml, custom404, subdomainRedirects, + redirectRules, }; const script = noError ? code(codeData) : undefined; @@ -1052,7 +1078,95 @@ export default function App() { startIcon={} sx={{ mt: 1 }} > - Add Redirect + Add Subdomain Redirect + + + + + + URL Redirect Rules + + + Redirect specific paths to other URLs (301 permanent / 302 + temporary) + + {redirectRules.map((rule, index) => ( + + + + handleRedirectRuleChange( + index, + "from", + e.target.value, + ) + } + variant="outlined" + size="small" + sx={{ flex: 1 }} + /> + + handleRedirectRuleChange(index, "to", e.target.value) + } + variant="outlined" + size="small" + sx={{ flex: 2 }} + /> + + handleRedirectRuleChange( + index, + "permanent", + e.target.checked, + ) + } + size="small" + /> + } + label="301" + sx={{ minWidth: 70 }} + /> + + + + ))} + diff --git a/src/code.ts b/src/code.ts index 921232b..292dba2 100644 --- a/src/code.ts +++ b/src/code.ts @@ -51,6 +51,12 @@ export interface SubdomainRedirect { redirectUrl: string; } +export interface RedirectRule { + from: string; + to: string; + permanent: boolean; +} + export interface CodeData { myDomain: string; notionUrl: string; @@ -69,6 +75,7 @@ export interface CodeData { customHtml: CustomHtmlOptions; custom404: Custom404Options; subdomainRedirects: SubdomainRedirect[]; + redirectRules: RedirectRule[]; } function getId(url: string): string { @@ -100,6 +107,7 @@ export default function code(data: CodeData): string { customHtml, custom404, subdomainRedirects, + redirectRules, } = data; let url = myDomain.replace("https://", "").replace("http://", ""); if (url.slice(-1) === "/") url = url.slice(0, url.length - 1); @@ -188,6 +196,21 @@ ${ .join("") || "" } }; + /* + * Step 3.8: custom redirect rules (optional) + * Redirect specific paths to other paths or external URLs (301/302) + */ + const REDIRECT_RULES = [ +${ + redirectRules + ?.filter((r) => r.from && r.to) + .map( + (r) => + ` { from: '${r.from}', to: '${r.to}', permanent: ${r.permanent} },\n`, + ) + .join("") || "" +} ]; + /* Step 4: enter a Google Font name, you can choose from https://fonts.google.com */ const GOOGLE_FONT = '${googleFont || ""}'; @@ -323,6 +346,16 @@ ${ } } + // Handle custom redirect rules (Issue #32) + for (const rule of REDIRECT_RULES) { + if (url.pathname === rule.from) { + const redirectUrl = rule.to.startsWith('http') + ? rule.to + : 'https://' + MY_DOMAIN + rule.to; + return Response.redirect(redirectUrl, rule.permanent ? 301 : 302); + } + } + // Use the original Notion site domain instead of www.notion.so url.hostname = NOTION_SITE_DOMAIN; if (url.pathname === '/robots.txt') {