fix: handle legacy redirects and media previews
This commit is contained in:
@@ -450,15 +450,21 @@ export default async function FAQPage({
|
||||
/>
|
||||
<section className="relative aspect-[16/9] overflow-hidden bg-[linear-gradient(135deg,#f4f1eb_0%,#fbfaf7_52%,#e8e1d4_100%)] px-5 py-5 shadow-[0_18px_55px_rgba(23,23,23,0.05)] md:aspect-auto md:px-10 md:py-10">
|
||||
{heroImageUrl || heroMobileImageUrl ? (
|
||||
<picture>
|
||||
{heroMobileImageUrl ? <source media="(max-width: 767px)" srcSet={heroMobileImageUrl} /> : null}
|
||||
<>
|
||||
<img
|
||||
src={heroImageUrl || heroMobileImageUrl || ''}
|
||||
alt=""
|
||||
className={['absolute inset-0 h-full w-full object-cover', heroOverlay ? 'opacity-100' : 'opacity-22'].join(' ')}
|
||||
decoding="async"
|
||||
/>
|
||||
</picture>
|
||||
{heroImageUrl && heroMobileImageUrl ? (
|
||||
<div
|
||||
aria-hidden="true"
|
||||
className={['absolute inset-0 h-full w-full bg-cover bg-center md:hidden', heroOverlay ? 'opacity-100' : 'opacity-22'].join(' ')}
|
||||
style={{ backgroundImage: `url(${heroMobileImageUrl})` }}
|
||||
/>
|
||||
) : null}
|
||||
</>
|
||||
) : null}
|
||||
{heroOverlay ? (
|
||||
<div className="absolute inset-0 bg-[linear-gradient(135deg,rgba(0,0,0,0.72)_0%,rgba(0,0,0,0.46)_48%,rgba(0,0,0,0.22)_100%)]" />
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
import type { NextRequest } from 'next/server'
|
||||
import { resolveCmsRedirect } from '@/lib/cms-redirect'
|
||||
|
||||
export const dynamic = 'force-dynamic'
|
||||
export const revalidate = 0
|
||||
|
||||
export async function GET(request: NextRequest) {
|
||||
return resolveCmsRedirect({ request })
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
import { NextRequest, NextResponse } from 'next/server'
|
||||
import { getPayloadClient } from '@/lib/payload'
|
||||
import { getSiteUrl } from '@/lib/seo'
|
||||
import { siteNotFoundRedirect } from '@/lib/site-not-found'
|
||||
|
||||
export const dynamic = 'force-dynamic'
|
||||
export const revalidate = 0
|
||||
|
||||
const legacyProductModelRedirects: Record<string, string> = {
|
||||
'amp-f10': '/en/products/amp-f10',
|
||||
'amp-f2': '/en/products/amp-f2',
|
||||
'dac-z10': '/en/products/dac-z10',
|
||||
'dac-z6': '/en/products/dac-z6',
|
||||
'dac-z8': '/en/products/dac-z8',
|
||||
'dmp-a10': '/en/products/dmp-a10',
|
||||
'dmp-a6': '/en/products/dmp-a6-gen-2',
|
||||
'dmp-a6 gen 2': '/en/products/dmp-a6-gen-2',
|
||||
'dmp-a6 master edition': '/en/products/dmp-a6-master-gen-2',
|
||||
'dmp-a6 master gen 2': '/en/products/dmp-a6-master-gen-2',
|
||||
'dmp-a6 master+gen+2': '/en/products/dmp-a6-master-gen-2',
|
||||
'dmp-a6+gen+2': '/en/products/dmp-a6-gen-2',
|
||||
'dmp-a6+master+gen+2': '/en/products/dmp-a6-master-gen-2',
|
||||
'eisa': '/en',
|
||||
'eisa award winners': '/en',
|
||||
'em-01': '/en/products/em-01',
|
||||
'evotune™': '/en/products/evotune',
|
||||
musicservices: '/en/products/musicservices',
|
||||
play: '/en/products/play',
|
||||
se100: '/en/products/se100',
|
||||
t8: '/en/products/t8',
|
||||
'topic-page': '/en/products/evotune',
|
||||
v16: '/en/products/v16',
|
||||
z10: '/en/products/dac-z10',
|
||||
}
|
||||
|
||||
function normalizeLegacyModel(model: string) {
|
||||
return decodeURIComponent(model).replace(/\+/g, ' ').trim().toLowerCase()
|
||||
}
|
||||
|
||||
export async function GET(
|
||||
request: NextRequest,
|
||||
{ params }: { params: Promise<{ model: string; section: string; target: string }> },
|
||||
) {
|
||||
const { model } = await params
|
||||
const payload = await getPayloadClient()
|
||||
|
||||
const rawPathname = request.nextUrl.pathname
|
||||
const productIndexPath = rawPathname.replace(/^\/Product\/[^/]+\/model\//, '/Product/index/model/')
|
||||
|
||||
const redirects = await payload.find({
|
||||
collection: 'redirects',
|
||||
depth: 0,
|
||||
limit: 2,
|
||||
locale: 'en',
|
||||
where: {
|
||||
and: [
|
||||
{ enabled: { equals: true } },
|
||||
{
|
||||
or: [{ from: { equals: rawPathname } }, { from: { equals: productIndexPath } }],
|
||||
},
|
||||
],
|
||||
},
|
||||
})
|
||||
|
||||
const redirectEntry =
|
||||
redirects.docs.find((entry) => entry.from === rawPathname) ??
|
||||
redirects.docs.find((entry) => entry.from === productIndexPath) ??
|
||||
null
|
||||
|
||||
if (!redirectEntry?.to) {
|
||||
const modelFallback = legacyProductModelRedirects[normalizeLegacyModel(model)]
|
||||
if (modelFallback) {
|
||||
return NextResponse.redirect(new URL(modelFallback, getSiteUrl()), 301)
|
||||
}
|
||||
|
||||
return siteNotFoundRedirect()
|
||||
}
|
||||
|
||||
return NextResponse.redirect(new URL(redirectEntry.to, getSiteUrl()), Number(redirectEntry.statusCode))
|
||||
}
|
||||
@@ -1,9 +1,42 @@
|
||||
import { NextRequest, NextResponse } from 'next/server'
|
||||
import { getPayloadClient } from '@/lib/payload'
|
||||
import { getSiteUrl } from '@/lib/seo'
|
||||
import { siteNotFoundRedirect } from '@/lib/site-not-found'
|
||||
|
||||
export const dynamic = 'force-dynamic'
|
||||
export const revalidate = 0
|
||||
|
||||
const legacyProductModelRedirects: Record<string, string> = {
|
||||
'amp-f10': '/en/products/amp-f10',
|
||||
'amp-f2': '/en/products/amp-f2',
|
||||
'dac-z10': '/en/products/dac-z10',
|
||||
'dac-z6': '/en/products/dac-z6',
|
||||
'dac-z8': '/en/products/dac-z8',
|
||||
'dmp-a10': '/en/products/dmp-a10',
|
||||
'dmp-a6': '/en/products/dmp-a6-gen-2',
|
||||
'dmp-a6 gen 2': '/en/products/dmp-a6-gen-2',
|
||||
'dmp-a6 master edition': '/en/products/dmp-a6-master-gen-2',
|
||||
'dmp-a6 master gen 2': '/en/products/dmp-a6-master-gen-2',
|
||||
'dmp-a6 master+gen+2': '/en/products/dmp-a6-master-gen-2',
|
||||
'dmp-a6+gen+2': '/en/products/dmp-a6-gen-2',
|
||||
'dmp-a6+master+gen+2': '/en/products/dmp-a6-master-gen-2',
|
||||
'eisa': '/en',
|
||||
'eisa award winners': '/en',
|
||||
'em-01': '/en/products/em-01',
|
||||
'evotune™': '/en/products/evotune',
|
||||
musicservices: '/en/products/musicservices',
|
||||
play: '/en/products/play',
|
||||
se100: '/en/products/se100',
|
||||
t8: '/en/products/t8',
|
||||
'topic-page': '/en/products/evotune',
|
||||
v16: '/en/products/v16',
|
||||
z10: '/en/products/dac-z10',
|
||||
}
|
||||
|
||||
function normalizeLegacyModel(model: string) {
|
||||
return decodeURIComponent(model).replace(/\+/g, ' ').trim().toLowerCase()
|
||||
}
|
||||
|
||||
export async function GET(
|
||||
request: NextRequest,
|
||||
{ params }: { params: Promise<{ model: string; target: string }> },
|
||||
@@ -11,7 +44,7 @@ export async function GET(
|
||||
const { model, target } = await params
|
||||
const payload = await getPayloadClient()
|
||||
|
||||
const rawPathname = new URL(request.url).pathname
|
||||
const rawPathname = request.nextUrl.pathname
|
||||
const fallbackPath = `/Product/index/model/${model}/target/${target}`
|
||||
|
||||
const redirects = await payload.find({
|
||||
@@ -35,8 +68,13 @@ export async function GET(
|
||||
null
|
||||
|
||||
if (!redirectEntry?.to) {
|
||||
return NextResponse.json({ error: 'Redirect not found' }, { status: 404 })
|
||||
const modelFallback = legacyProductModelRedirects[normalizeLegacyModel(model)]
|
||||
if (modelFallback) {
|
||||
return NextResponse.redirect(new URL(modelFallback, getSiteUrl()), 301)
|
||||
}
|
||||
|
||||
return siteNotFoundRedirect()
|
||||
}
|
||||
|
||||
return NextResponse.redirect(new URL(redirectEntry.to, request.url), Number(redirectEntry.statusCode))
|
||||
return NextResponse.redirect(new URL(redirectEntry.to, getSiteUrl()), Number(redirectEntry.statusCode))
|
||||
}
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
import type { NextRequest } from 'next/server'
|
||||
import { resolveCmsRedirect } from '@/lib/cms-redirect'
|
||||
|
||||
export const dynamic = 'force-dynamic'
|
||||
export const revalidate = 0
|
||||
|
||||
export async function GET(request: NextRequest) {
|
||||
const pathname = request.nextUrl.pathname
|
||||
const normalizedTutorialPage = pathname.match(/^\/Support\/tutorial\/target\/([^/]+)\/p\/\d+\.html$/)
|
||||
|
||||
return resolveCmsRedirect({
|
||||
pathname: normalizedTutorialPage
|
||||
? `/Support/tutorial/target/${normalizedTutorialPage[1]}.html`
|
||||
: undefined,
|
||||
request,
|
||||
})
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
import { NextRequest, NextResponse } from 'next/server'
|
||||
import { getPayloadClient } from '@/lib/payload'
|
||||
import { getSiteUrl } from '@/lib/seo'
|
||||
|
||||
function cleanFilenamePart(value: string | null | undefined, fallback: string) {
|
||||
const cleaned = value
|
||||
@@ -39,9 +40,9 @@ function contentDisposition(filename: string) {
|
||||
return `attachment; filename="${ascii}"; filename*=UTF-8''${encodeURIComponent(filename)}`
|
||||
}
|
||||
|
||||
function resolveDownloadUrl(value: string, request: NextRequest) {
|
||||
function resolveDownloadUrl(value: string) {
|
||||
const trimmed = value.trim()
|
||||
if (trimmed.startsWith('/')) return new URL(trimmed, request.url).toString()
|
||||
if (trimmed.startsWith('/')) return new URL(trimmed, getSiteUrl()).toString()
|
||||
|
||||
const parsed = new URL(trimmed)
|
||||
if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') {
|
||||
@@ -51,8 +52,8 @@ function resolveDownloadUrl(value: string, request: NextRequest) {
|
||||
return parsed.toString()
|
||||
}
|
||||
|
||||
async function proxyAttachment(url: string, filename: string, request: NextRequest) {
|
||||
const resolvedUrl = resolveDownloadUrl(url, request)
|
||||
async function proxyAttachment(url: string, filename: string) {
|
||||
const resolvedUrl = resolveDownloadUrl(url)
|
||||
const upstream = await fetch(resolvedUrl, {
|
||||
cache: 'no-store',
|
||||
headers: {
|
||||
@@ -79,7 +80,7 @@ async function proxyAttachment(url: string, filename: string, request: NextReque
|
||||
}
|
||||
|
||||
export async function GET(
|
||||
request: NextRequest,
|
||||
_request: NextRequest,
|
||||
{ params }: { params: Promise<{ id: string }> },
|
||||
) {
|
||||
const { id } = await params
|
||||
@@ -98,6 +99,5 @@ export async function GET(
|
||||
return proxyAttachment(
|
||||
category.archiveDownloadUrl,
|
||||
buildFilename(category.name, category.archiveFormat),
|
||||
request,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { NextRequest, NextResponse } from 'next/server'
|
||||
import { getPayloadClient } from '@/lib/payload'
|
||||
import { getSiteUrl } from '@/lib/seo'
|
||||
|
||||
function cleanFilenamePart(value: string | null | undefined, fallback: string) {
|
||||
const cleaned = value
|
||||
@@ -39,9 +40,9 @@ function contentDisposition(filename: string) {
|
||||
return `attachment; filename="${ascii}"; filename*=UTF-8''${encodeURIComponent(filename)}`
|
||||
}
|
||||
|
||||
function resolveDownloadUrl(value: string, request: NextRequest) {
|
||||
function resolveDownloadUrl(value: string) {
|
||||
const trimmed = value.trim()
|
||||
if (trimmed.startsWith('/')) return new URL(trimmed, request.url).toString()
|
||||
if (trimmed.startsWith('/')) return new URL(trimmed, getSiteUrl()).toString()
|
||||
|
||||
const parsed = new URL(trimmed)
|
||||
if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') {
|
||||
@@ -51,8 +52,8 @@ function resolveDownloadUrl(value: string, request: NextRequest) {
|
||||
return parsed.toString()
|
||||
}
|
||||
|
||||
async function proxyAttachment(url: string, filename: string, request: NextRequest) {
|
||||
const resolvedUrl = resolveDownloadUrl(url, request)
|
||||
async function proxyAttachment(url: string, filename: string) {
|
||||
const resolvedUrl = resolveDownloadUrl(url)
|
||||
const upstream = await fetch(resolvedUrl, {
|
||||
cache: 'no-store',
|
||||
headers: {
|
||||
@@ -79,7 +80,7 @@ async function proxyAttachment(url: string, filename: string, request: NextReque
|
||||
}
|
||||
|
||||
export async function GET(
|
||||
request: NextRequest,
|
||||
_request: NextRequest,
|
||||
{ params }: { params: Promise<{ id: string }> },
|
||||
) {
|
||||
const { id } = await params
|
||||
@@ -95,5 +96,5 @@ export async function GET(
|
||||
return NextResponse.json({ error: 'Download not found.' }, { status: 404 })
|
||||
}
|
||||
|
||||
return proxyAttachment(asset.externalUrl, buildFilename(asset.title, asset.fileFormat), request)
|
||||
return proxyAttachment(asset.externalUrl, buildFilename(asset.title, asset.fileFormat))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user