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
This commit is contained in:
2025-10-26 00:58:10 -05:00
parent 6c043630df
commit 0dcf6f93ba
56 changed files with 6539 additions and 1616 deletions
+442
View File
@@ -0,0 +1,442 @@
/* tslint:disable */
/**
* This file was automatically generated by Payload.
* DO NOT MODIFY IT BY HAND. Instead, modify your source Payload config,
* and re-run `payload generate:types` to regenerate this file.
*/
/**
* Supported timezones in IANA format.
*
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "supportedTimezones".
*/
export type SupportedTimezones =
| 'Pacific/Midway'
| 'Pacific/Niue'
| 'Pacific/Honolulu'
| 'Pacific/Rarotonga'
| 'America/Anchorage'
| 'Pacific/Gambier'
| 'America/Los_Angeles'
| 'America/Tijuana'
| 'America/Denver'
| 'America/Phoenix'
| 'America/Chicago'
| 'America/Guatemala'
| 'America/New_York'
| 'America/Bogota'
| 'America/Caracas'
| 'America/Santiago'
| 'America/Buenos_Aires'
| 'America/Sao_Paulo'
| 'Atlantic/South_Georgia'
| 'Atlantic/Azores'
| 'Atlantic/Cape_Verde'
| 'Europe/London'
| 'Europe/Berlin'
| 'Africa/Lagos'
| 'Europe/Athens'
| 'Africa/Cairo'
| 'Europe/Moscow'
| 'Asia/Riyadh'
| 'Asia/Dubai'
| 'Asia/Baku'
| 'Asia/Karachi'
| 'Asia/Tashkent'
| 'Asia/Calcutta'
| 'Asia/Dhaka'
| 'Asia/Almaty'
| 'Asia/Jakarta'
| 'Asia/Bangkok'
| 'Asia/Shanghai'
| 'Asia/Singapore'
| 'Asia/Tokyo'
| 'Asia/Seoul'
| 'Australia/Brisbane'
| 'Australia/Sydney'
| 'Pacific/Guam'
| 'Pacific/Noumea'
| 'Pacific/Auckland'
| 'Pacific/Fiji';
export interface Config {
auth: {
users: UserAuthOperations;
};
blocks: {};
collections: {
users: User;
media: Media;
projects: Project;
technologies: Technology;
links: Link;
'payload-locked-documents': PayloadLockedDocument;
'payload-preferences': PayloadPreference;
'payload-migrations': PayloadMigration;
};
collectionsJoins: {};
collectionsSelect: {
users: UsersSelect<false> | UsersSelect<true>;
media: MediaSelect<false> | MediaSelect<true>;
projects: ProjectsSelect<false> | ProjectsSelect<true>;
technologies: TechnologiesSelect<false> | TechnologiesSelect<true>;
links: LinksSelect<false> | LinksSelect<true>;
'payload-locked-documents': PayloadLockedDocumentsSelect<false> | PayloadLockedDocumentsSelect<true>;
'payload-preferences': PayloadPreferencesSelect<false> | PayloadPreferencesSelect<true>;
'payload-migrations': PayloadMigrationsSelect<false> | PayloadMigrationsSelect<true>;
};
db: {
defaultIDType: number;
};
globals: {
metadata: Metadatum;
};
globalsSelect: {
metadata: MetadataSelect<false> | MetadataSelect<true>;
};
locale: null;
user: User & {
collection: 'users';
};
jobs: {
tasks: unknown;
workflows: unknown;
};
}
export interface UserAuthOperations {
forgotPassword: {
email: string;
password: string;
};
login: {
email: string;
password: string;
};
registerFirstUser: {
email: string;
password: string;
};
unlock: {
email: string;
password: string;
};
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "users".
*/
export interface User {
id: number;
updatedAt: string;
createdAt: string;
email: string;
resetPasswordToken?: string | null;
resetPasswordExpiration?: string | null;
salt?: string | null;
hash?: string | null;
loginAttempts?: number | null;
lockUntil?: string | null;
sessions?:
| {
id: string;
createdAt?: string | null;
expiresAt: string;
}[]
| null;
password?: string | null;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "media".
*/
export interface Media {
id: number;
alt: string;
updatedAt: string;
createdAt: string;
url?: string | null;
thumbnailURL?: string | null;
filename?: string | null;
mimeType?: string | null;
filesize?: number | null;
width?: number | null;
height?: number | null;
focalX?: number | null;
focalY?: number | null;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "projects".
*/
export interface Project {
id: number;
name: string;
description: string;
shortDescription: string;
icon?: string | null;
status: 'draft' | 'published' | 'archived';
featured?: boolean | null;
/**
* Automatically check GitHub for latest commits and update lastUpdated field
*/
autocheckUpdated?: boolean | null;
/**
* Automatically updated by cron job based on GitHub commits
*/
lastUpdated?: string | null;
/**
* Offset for WakaTime fetched data (optional)
*/
wakatimeOffset?: number | null;
bannerImage?: (number | null) | Media;
technologies?: (number | Technology)[] | null;
updatedAt: string;
createdAt: string;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "technologies".
*/
export interface Technology {
id: number;
name: string;
url?: string | null;
updatedAt: string;
createdAt: string;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "links".
*/
export interface Link {
id: number;
url: string;
icon?: string | null;
description?: string | null;
project: number | Project;
updatedAt: string;
createdAt: string;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "payload-locked-documents".
*/
export interface PayloadLockedDocument {
id: number;
document?:
| ({
relationTo: 'users';
value: number | User;
} | null)
| ({
relationTo: 'media';
value: number | Media;
} | null)
| ({
relationTo: 'projects';
value: number | Project;
} | null)
| ({
relationTo: 'technologies';
value: number | Technology;
} | null)
| ({
relationTo: 'links';
value: number | Link;
} | null);
globalSlug?: string | null;
user: {
relationTo: 'users';
value: number | User;
};
updatedAt: string;
createdAt: string;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "payload-preferences".
*/
export interface PayloadPreference {
id: number;
user: {
relationTo: 'users';
value: number | User;
};
key?: string | null;
value?:
| {
[k: string]: unknown;
}
| unknown[]
| string
| number
| boolean
| null;
updatedAt: string;
createdAt: string;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "payload-migrations".
*/
export interface PayloadMigration {
id: number;
name?: string | null;
batch?: number | null;
updatedAt: string;
createdAt: string;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "users_select".
*/
export interface UsersSelect<T extends boolean = true> {
updatedAt?: T;
createdAt?: T;
email?: T;
resetPasswordToken?: T;
resetPasswordExpiration?: T;
salt?: T;
hash?: T;
loginAttempts?: T;
lockUntil?: T;
sessions?:
| T
| {
id?: T;
createdAt?: T;
expiresAt?: T;
};
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "media_select".
*/
export interface MediaSelect<T extends boolean = true> {
alt?: T;
updatedAt?: T;
createdAt?: T;
url?: T;
thumbnailURL?: T;
filename?: T;
mimeType?: T;
filesize?: T;
width?: T;
height?: T;
focalX?: T;
focalY?: T;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "projects_select".
*/
export interface ProjectsSelect<T extends boolean = true> {
name?: T;
description?: T;
shortDescription?: T;
icon?: T;
status?: T;
featured?: T;
autocheckUpdated?: T;
lastUpdated?: T;
wakatimeOffset?: T;
bannerImage?: T;
technologies?: T;
updatedAt?: T;
createdAt?: T;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "technologies_select".
*/
export interface TechnologiesSelect<T extends boolean = true> {
name?: T;
url?: T;
updatedAt?: T;
createdAt?: T;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "links_select".
*/
export interface LinksSelect<T extends boolean = true> {
url?: T;
icon?: T;
description?: T;
project?: T;
updatedAt?: T;
createdAt?: T;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "payload-locked-documents_select".
*/
export interface PayloadLockedDocumentsSelect<T extends boolean = true> {
document?: T;
globalSlug?: T;
user?: T;
updatedAt?: T;
createdAt?: T;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "payload-preferences_select".
*/
export interface PayloadPreferencesSelect<T extends boolean = true> {
user?: T;
key?: T;
value?: T;
updatedAt?: T;
createdAt?: T;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "payload-migrations_select".
*/
export interface PayloadMigrationsSelect<T extends boolean = true> {
name?: T;
batch?: T;
updatedAt?: T;
createdAt?: T;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "metadata".
*/
export interface Metadatum {
id: number;
tagline: string;
resume: number | Media;
/**
* Optional: Override the filename for the resume (e.g., "resume.pdf")
*/
resumeFilename?: string | null;
updatedAt?: string | null;
createdAt?: string | null;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "metadata_select".
*/
export interface MetadataSelect<T extends boolean = true> {
tagline?: T;
resume?: T;
resumeFilename?: T;
updatedAt?: T;
createdAt?: T;
globalType?: T;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "auth".
*/
export interface Auth {
[k: string]: unknown;
}
declare module 'payload' {
export interface GeneratedTypes extends Config {}
}