dc845a8808
Switch the production worker route and all hardcoded URLs from ohmyopenagent.com to omo.dev. Six legacy domains (ohmyopenagent.com, ohmyopencode.org, ulw.dev, ultrawork.ai, ultrawork.dev, ultrawork.engineer) are configured to permanently (301) redirect to omo.dev via Cloudflare Page Rules set up out-of-band via flarectl and the CF API. - wrangler.toml: bind worker to omo.dev + www.omo.dev only - middleware.ts: primaryHost to omo.dev, www to apex redirect retained - layout.tsx: primarySiteUrl + gaTrackedDomain to omo.dev (GA still G-S0QJFKT46Q) - sitemap.ts, robots.ts: BASE_URL to https://omo.dev - npm-downloads/route.ts: usage comment URL refreshed - web-deploy.yml: environment URL to https://omo.dev - manifesto.md: domain reality-check line lists omo.dev as canonical - README*.md (5 langs): npm-downloads badge endpoint to omo.dev
77 lines
2.5 KiB
TypeScript
77 lines
2.5 KiB
TypeScript
import { NextResponse, type NextRequest } from "next/server"
|
|
import createMiddleware from "next-intl/middleware"
|
|
import { locales, type Locale } from "./i18n/config"
|
|
import { routing } from "./i18n/routing"
|
|
|
|
const handleI18nRouting = createMiddleware(routing)
|
|
const oldHosts = new Set(["www.omo.dev"])
|
|
const primaryHost = "omo.dev"
|
|
const installationPaths = new Set([
|
|
"installation",
|
|
"installation.md",
|
|
"docs/installation",
|
|
"docs/installation.md",
|
|
])
|
|
|
|
function getLocaleSegment(segment: string | undefined): Locale | null {
|
|
if (!segment) return null
|
|
return locales.find((locale) => locale === segment) ?? null
|
|
}
|
|
|
|
function getInstallationDocsPath(pathname: string): string | null {
|
|
const segments = pathname.split("/").filter(Boolean)
|
|
const locale = getLocaleSegment(segments[0])
|
|
const routeSegments = locale ? segments.slice(1) : segments
|
|
|
|
if (!installationPaths.has(routeSegments.join("/"))) return null
|
|
|
|
return locale ? `/${locale}/docs` : "/docs"
|
|
}
|
|
|
|
export default function middleware(request: NextRequest): NextResponse {
|
|
const forwardedHost = request.headers.get("x-forwarded-host")
|
|
const requestHost = request.headers.get("host")
|
|
const hostname = (forwardedHost ?? requestHost ?? request.nextUrl.hostname).split(":")[0]
|
|
const redirectUrl = request.nextUrl.clone()
|
|
let shouldRedirect = false
|
|
|
|
if (hostname && oldHosts.has(hostname)) {
|
|
redirectUrl.protocol = "https"
|
|
redirectUrl.host = primaryHost
|
|
shouldRedirect = true
|
|
}
|
|
|
|
const installationDocsPath = getInstallationDocsPath(request.nextUrl.pathname)
|
|
if (installationDocsPath) {
|
|
redirectUrl.pathname = installationDocsPath
|
|
redirectUrl.search = ""
|
|
redirectUrl.hash = "installation"
|
|
shouldRedirect = true
|
|
}
|
|
|
|
if (shouldRedirect) {
|
|
return NextResponse.redirect(redirectUrl, 308)
|
|
}
|
|
|
|
return handleI18nRouting(request)
|
|
}
|
|
|
|
export const config = {
|
|
matcher: [
|
|
"/",
|
|
/*
|
|
* Match everything except:
|
|
* - api routes
|
|
* - Next.js internals (_next, _vercel)
|
|
* - Next.js file-based metadata routes (opengraph-image, twitter-image, icon, apple-icon, manifest, robots, sitemap)
|
|
* These serve images / JSON / XML directly and must NOT be redirected by i18n.
|
|
* - Any path containing a dot (favicon.ico, *.webp, *.png, etc.)
|
|
*/
|
|
"/((?!api|_next|_vercel|opengraph-image|twitter-image|icon|apple-icon|manifest\\.webmanifest|robots\\.txt|sitemap\\.xml|.*\\..*).+)",
|
|
"/installation.md",
|
|
"/:locale(en|ko|ja|zh)/installation.md",
|
|
"/docs/installation.md",
|
|
"/:locale(en|ko|ja|zh)/docs/installation.md",
|
|
],
|
|
}
|