diff --git a/__tests__/path.test.ts b/__tests__/path.test.ts index b994166c..d8e25562 100644 --- a/__tests__/path.test.ts +++ b/__tests__/path.test.ts @@ -28,7 +28,7 @@ const PATH_ADMIN = '/admin/photos'; const PATH_PHOTO = `/p/${PHOTO_ID}`; const PATH_PHOTO_SHARE = `${PATH_PHOTO}/${SHARE}`; -const PATH_TAG = `/t/${TAG}`; +const PATH_TAG = `/tag/${TAG}`; const PATH_TAG_SHARE = `${PATH_TAG}/${SHARE}`; const PATH_TAG_PHOTO = `${PATH_TAG}/${PHOTO_ID}`; const PATH_TAG_PHOTO_SHARE = `${PATH_TAG_PHOTO}/${SHARE}`; diff --git a/src/app/(static)/t/[tag]/[photoId]/layout.tsx b/src/app/(static)/tag/[tag]/[photoId]/layout.tsx similarity index 100% rename from src/app/(static)/t/[tag]/[photoId]/layout.tsx rename to src/app/(static)/tag/[tag]/[photoId]/layout.tsx diff --git a/src/app/(static)/t/[tag]/[photoId]/page.tsx b/src/app/(static)/tag/[tag]/[photoId]/page.tsx similarity index 100% rename from src/app/(static)/t/[tag]/[photoId]/page.tsx rename to src/app/(static)/tag/[tag]/[photoId]/page.tsx diff --git a/src/app/(static)/t/[tag]/[photoId]/share/page.tsx b/src/app/(static)/tag/[tag]/[photoId]/share/page.tsx similarity index 100% rename from src/app/(static)/t/[tag]/[photoId]/share/page.tsx rename to src/app/(static)/tag/[tag]/[photoId]/share/page.tsx diff --git a/src/app/(static)/t/[tag]/image/route.tsx b/src/app/(static)/tag/[tag]/image/route.tsx similarity index 100% rename from src/app/(static)/t/[tag]/image/route.tsx rename to src/app/(static)/tag/[tag]/image/route.tsx diff --git a/src/app/(static)/t/[tag]/page.tsx b/src/app/(static)/tag/[tag]/page.tsx similarity index 100% rename from src/app/(static)/t/[tag]/page.tsx rename to src/app/(static)/tag/[tag]/page.tsx diff --git a/src/app/(static)/t/[tag]/share/page.tsx b/src/app/(static)/tag/[tag]/share/page.tsx similarity index 100% rename from src/app/(static)/t/[tag]/share/page.tsx rename to src/app/(static)/tag/[tag]/share/page.tsx diff --git a/src/middleware.ts b/src/middleware.ts index 7210ac03..2cd56a2c 100644 --- a/src/middleware.ts +++ b/src/middleware.ts @@ -1,6 +1,7 @@ import { auth } from './auth'; import { NextRequest, NextResponse } from 'next/server'; import type { NextApiRequest, NextApiResponse } from 'next'; +import { PREFIX_PHOTO, PREFIX_TAG } from './site/paths'; export default function middleware(req: NextRequest, res:NextResponse) { const pathname = req.nextUrl.pathname; @@ -11,7 +12,14 @@ export default function middleware(req: NextRequest, res:NextResponse) { // Accept /photos/* paths, but serve /p/* const matches = pathname.match(/^\/photos\/(.+)$/); return NextResponse.rewrite(new URL( - `/p/${matches?.[1]}`, + `${PREFIX_PHOTO}/${matches?.[1]}`, + req.url, + )); + } else if (/^\/t\/(.)+$/.test(pathname)) { + // Accept /t/* paths, but serve /tag/* + const matches = pathname.match(/^\/t\/(.+)$/); + return NextResponse.rewrite(new URL( + `${PREFIX_TAG}/${matches?.[1]}`, req.url, )); } diff --git a/src/site/paths.ts b/src/site/paths.ts index 87480fe7..49df5470 100644 --- a/src/site/paths.ts +++ b/src/site/paths.ts @@ -7,9 +7,9 @@ import { } from '@/camera'; // Prefixes -const PREFIX_PHOTO = '/p'; -const PREFIX_TAG = '/t'; -const PREFIX_CAMERA = '/shot-on'; +export const PREFIX_PHOTO = '/p'; +export const PREFIX_TAG = '/tag'; +export const PREFIX_CAMERA = '/shot-on'; // Core paths export const PATH_ROOT = '/'; @@ -112,43 +112,43 @@ export const absolutePathForCameraImage= (camera: Camera) => // p/[photoId] export const isPathPhoto = (pathname = '') => - /^\/p\/[^/]+\/?$/.test(pathname); + new RegExp(`^${PREFIX_PHOTO}/[^/]+/?$`).test(pathname); // p/[photoId]/share export const isPathPhotoShare = (pathname = '') => - /^\/p\/[^/]+\/share\/?$/.test(pathname); + new RegExp(`^${PREFIX_PHOTO}/[^/]+/${SHARE}/?$`).test(pathname); // t/[tag] export const isPathTag = (pathname = '') => - /^\/t\/[^/]+\/?$/.test(pathname); + new RegExp(`^${PREFIX_TAG}/[^/]+/?$`).test(pathname); // t/[tag]/share export const isPathTagShare = (pathname = '') => - /^\/t\/[^/]+\/share\/?$/.test(pathname); + new RegExp(`^${PREFIX_TAG}/[^/]+/${SHARE}/?$`).test(pathname); // t/[tag]/[photoId] export const isPathTagPhoto = (pathname = '') => - /^\/t\/[^/]+\/[^/]+\/?$/.test(pathname); + new RegExp(`^${PREFIX_TAG}/[^/]+/[^/]+/?$`).test(pathname); // t/[tag]/[photoId]/share export const isPathTagPhotoShare = (pathname = '') => - /^\/t\/[^/]+\/[^/]+\/share\/?$/.test(pathname); + new RegExp(`^${PREFIX_TAG}/[^/]+/[^/]+/${SHARE}/?$`).test(pathname); // shot-on/[camera] export const isPathCamera = (pathname = '') => - /^\/shot-on\/[^/]+\/?$/.test(pathname); + new RegExp(`^${PREFIX_CAMERA}/[^/]+/?$`).test(pathname); // shot-on/[camera]/share export const isPathCameraShare = (pathname = '') => - /^\/shot-on\/[^/]+\/share\/?$/.test(pathname); + new RegExp(`^${PREFIX_CAMERA}/[^/]+/${SHARE}/?$`).test(pathname); // shot-on/[camera]/[photoId] export const isPathCameraPhoto = (pathname = '') => - /^\/shot-on\/[^/]+\/[^/]+\/?$/.test(pathname); + new RegExp(`^${PREFIX_CAMERA}/[^/]+/[^/]+/?$`).test(pathname); // shot-on/[camera]/[photoId]/share export const isPathCameraPhotoShare = (pathname = '') => - /^\/shot-on\/[^/]+\/[^/]+\/share\/?$/.test(pathname); + new RegExp(`^${PREFIX_CAMERA}/[^/]+/[^/]+/${SHARE}/?$`).test(pathname); export const isPathGrid = (pathname = '') => pathname.startsWith(PATH_GRID); @@ -168,15 +168,21 @@ export const getPathComponents = (pathname = ''): { tag?: string camera?: Camera } => { - const photoIdFromPhoto = pathname.match(/^\/p\/([^/]+)/)?.[1]; - const photoIdFromTag = pathname.match(/^\/t\/[^/]+\/((?!share)[^/]+)/)?.[1]; // eslint-disable-next-line max-len - const photoIdFromCamera = pathname.match(/^\/shot-on\/[^/]+\/((?!share)[^/]+)/)?.[1]; - const tag = pathname.match(/^\/t\/([^/]+)/)?.[1]; - const cameraString = pathname.match(/^\/shot-on\/([^/]+)/)?.[1]; + const photoIdFromPhoto = pathname.match(new RegExp(`^${PREFIX_PHOTO}/([^/]+)`))?.[1]; + // eslint-disable-next-line max-len + const photoIdFromTag = pathname.match(new RegExp(`^${PREFIX_TAG}/[^/]+/((?!${SHARE})[^/]+)`))?.[1]; + // eslint-disable-next-line max-len + const photoIdFromCamera = pathname.match(new RegExp(`^${PREFIX_CAMERA}/[^/]+/((?!${SHARE})[^/]+)`))?.[1]; + // eslint-disable-next-line max-len + const tag = pathname.match(new RegExp(`^${PREFIX_TAG}/([^/]+)`))?.[1]; + // eslint-disable-next-line max-len + const cameraString = pathname.match(new RegExp(`^${PREFIX_CAMERA}/([^/]+)`))?.[1]; + const camera = cameraString ? getCameraFromKey(cameraString) : undefined; + return { photoId: ( photoIdFromPhoto ||