Files
xevion.dev/next.config.mjs
Xevion 0dcf6f93ba feat: migrate from Next.js Pages Router to App Router with Payload CMS
Complete architectural overhaul migrating from Directus+tRPC to Payload CMS with Next.js App Router. This represents a fundamental shift in how the application is structured and how data is managed.

Major changes:
- Migrated from Pages Router (src/pages/) to App Router (src/app/)
- Replaced Directus CMS with Payload CMS as the content management system
- Removed tRPC in favor of Payload's built-in API routes
- Added PostgreSQL database via Docker Compose for local development
- Implemented separate route groups for frontend and Payload admin
- Updated all API routes to App Router conventions
- Added Payload collections for Projects, Technologies, Links, Media, and Users
- Configured ESLint for new project structure

Infrastructure:
- Added docker-compose.yml for PostgreSQL database
- Updated environment variables for Payload CMS configuration
- Integrated @payloadcms/next for seamless Next.js integration
- Added GraphQL API and playground routes

Dependencies:
- Upgraded React from 18.2.0 to 19.2.0
- Upgraded Next.js to 15.5.6
- Added Payload CMS 3.x packages (@payloadcms/db-postgres, @payloadcms/next, etc.)
- Removed Directus SDK and tRPC packages
- Updated Sharp to 0.34.x
- Migrated to @tanstack/react-query v5
2025-10-26 00:58:10 -05:00

96 lines
2.3 KiB
JavaScript

import { withPayload } from "@payloadcms/next/withPayload";
// @ts-check
import withPlaiceholder from "@plaiceholder/next";
/**
* Run `build` or `dev` with `SKIP_ENV_VALIDATION` to skip env validation.
* This is especially useful for Docker builds.
*/
if (!process.env.SKIP_ENV_VALIDATION) await import("./src/env/server.mjs");
/**
*
* @param {string} text The string to search around with the pattern in.
* @param {string} pattern The strict, text only pattern to search for.
* @param {number} nth The index of the pattern to find.
* @returns
*/
function nthIndex(text, pattern, nth) {
const L = text.length;
let i = -1;
while (nth-- && i++ < L) {
i = text.indexOf(pattern, i);
if (i < 0) break;
}
return i;
}
const v2_redirects = [
"/2020/12/04/jekyll-github-pages-and-azabani",
"/2021/02/25/project-facelift-new-and-old",
"/2022/03/29/runnerspace-built-in-under-30-hours",
"/2022/07/16/restricted-memory-and-data-framing-tricks",
"/drafts/2022-09-19-presenting-to-humans/",
"/photography",
].map((url) => {
// If the URL starts with /2, redirect to the new blog. Otherwise, redirect to the old v2 blog to maintain URLs.
if (url.startsWith("/2"))
return {
source: url,
destination: `https://undefined.behavio.rs/posts${url.slice(
nthIndex(url, "/", 4),
)}`,
permanent: false,
};
return {
source: url,
destination: `https://v2.xevion.dev${url}`,
permanent: false,
};
});
/** @type {import("next").NextConfig} */
const config = {
reactStrictMode: true,
images: {
remotePatterns: [
{
protocol: "https",
hostname: "img.walters.to",
},
{
protocol: "https",
hostname: "img.xevion.dev",
},
{
protocol: "https",
hostname: "api.xevion.dev",
},
],
},
async redirects() {
// Source cannot end with / slash
return [
...[
"resume.pdf",
"resume.docx",
"resume.txt",
"resum",
"resumee",
"cv",
"cover.pdf",
"cv.docx",
"cv.pdf",
].map((ext) => ({
source: `/${ext}`,
destination: "/resume",
permanent: true,
})),
...v2_redirects,
];
},
};
export default withPayload(withPlaiceholder(config));