feat: payload cms
Some checks failed
Build & Deploy / 🔍 Prepare (push) Successful in 8s
Build & Deploy / 🧪 QA (push) Failing after 1m13s
Build & Deploy / 🏗️ Build (push) Failing after 5m53s
Build & Deploy / 🚀 Deploy (push) Has been skipped
Build & Deploy / 🧪 Smoke Test (push) Has been skipped
Build & Deploy / ⚡ Lighthouse (push) Has been skipped
Build & Deploy / ♿ WCAG (push) Has been skipped
Build & Deploy / 🛡️ Quality Gates (push) Has been skipped
Build & Deploy / 🔔 Notify (push) Successful in 4s
Some checks failed
Build & Deploy / 🔍 Prepare (push) Successful in 8s
Build & Deploy / 🧪 QA (push) Failing after 1m13s
Build & Deploy / 🏗️ Build (push) Failing after 5m53s
Build & Deploy / 🚀 Deploy (push) Has been skipped
Build & Deploy / 🧪 Smoke Test (push) Has been skipped
Build & Deploy / ⚡ Lighthouse (push) Has been skipped
Build & Deploy / ♿ WCAG (push) Has been skipped
Build & Deploy / 🛡️ Quality Gates (push) Has been skipped
Build & Deploy / 🔔 Notify (push) Successful in 4s
This commit is contained in:
153
scripts/migrate-products.ts
Normal file
153
scripts/migrate-products.ts
Normal file
@@ -0,0 +1,153 @@
|
||||
import { getPayload } from 'payload';
|
||||
import configPromise from '../payload.config';
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import matter from 'gray-matter';
|
||||
import { parseMarkdownToLexical } from '../src/payload/utils/lexicalParser';
|
||||
|
||||
async function mapImageToMediaId(payload: any, imagePath: string): Promise<string | null> {
|
||||
if (!imagePath) return null;
|
||||
const filename = path.basename(imagePath);
|
||||
|
||||
// Exact match instead of substring to avoid matching "cable-black.jpg" with "cable.jpg"
|
||||
const media = await payload.find({
|
||||
collection: 'media',
|
||||
where: {
|
||||
filename: {
|
||||
equals: filename,
|
||||
},
|
||||
},
|
||||
limit: 1,
|
||||
});
|
||||
|
||||
if (media.docs.length > 0) {
|
||||
return media.docs[0].id;
|
||||
}
|
||||
|
||||
const cleanPath = imagePath.startsWith('/') ? imagePath.slice(1) : imagePath;
|
||||
const fullPath = path.join(process.cwd(), 'public', cleanPath);
|
||||
|
||||
if (fs.existsSync(fullPath)) {
|
||||
try {
|
||||
console.log(`[Products Migration] 📤 Ingesting missing Media into Payload: ${filename}`);
|
||||
const newMedia = await payload.create({
|
||||
collection: 'media',
|
||||
data: {
|
||||
alt: filename.replace(/[-_]/g, ' ').replace(/\.[^/.]+$/, ''),
|
||||
},
|
||||
filePath: fullPath,
|
||||
});
|
||||
return newMedia.id;
|
||||
} catch (err: any) {
|
||||
console.error(`[Products Migration] ❌ Failed to ingest ${filename}:`, err);
|
||||
}
|
||||
} else {
|
||||
console.warn(`[Products Migration] ⚠️ Missing image entirely from disk: ${fullPath}`);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
export async function migrateProducts() {
|
||||
const payload = await getPayload({ config: configPromise });
|
||||
const productLocales = ['en', 'de'];
|
||||
|
||||
for (const locale of productLocales) {
|
||||
const productsDir = path.join(process.cwd(), 'data', 'products', locale);
|
||||
if (!fs.existsSync(productsDir)) continue;
|
||||
|
||||
// Recursive file finder
|
||||
const mdFiles: string[] = [];
|
||||
const walk = (dir: string) => {
|
||||
const files = fs.readdirSync(dir);
|
||||
for (const file of files) {
|
||||
const fullPath = path.join(dir, file);
|
||||
const stat = fs.statSync(fullPath);
|
||||
if (stat.isDirectory()) {
|
||||
walk(fullPath);
|
||||
} else if (file.endsWith('.mdx')) {
|
||||
mdFiles.push(fullPath);
|
||||
}
|
||||
}
|
||||
};
|
||||
walk(productsDir);
|
||||
|
||||
for (const filePath of mdFiles) {
|
||||
const fileContent = fs.readFileSync(filePath, 'utf8');
|
||||
const { data, content } = matter(fileContent);
|
||||
|
||||
console.log(`Processing Product: [${locale.toUpperCase()}] ${data.title}`);
|
||||
|
||||
// 1. Process Images
|
||||
const mediaIds = [];
|
||||
if (data.images && Array.isArray(data.images)) {
|
||||
for (const imgPath of data.images) {
|
||||
const id = await mapImageToMediaId(payload, imgPath);
|
||||
if (id) mediaIds.push(id);
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Map Lexical AST for deeply nested components (like ProductTabs + Technical data)
|
||||
const lexicalContent = parseMarkdownToLexical(content);
|
||||
|
||||
const wrapLexical = (blocks: any[]) => ({
|
||||
root: {
|
||||
type: 'root',
|
||||
format: '',
|
||||
indent: 0,
|
||||
version: 1,
|
||||
children: blocks,
|
||||
direction: 'ltr',
|
||||
},
|
||||
});
|
||||
|
||||
// Payload expects category objects via the 'category' key
|
||||
const formattedCategories = Array.isArray(data.categories)
|
||||
? data.categories.map((c: string) => ({ category: c }))
|
||||
: [];
|
||||
|
||||
const productData = {
|
||||
title: data.title,
|
||||
sku: data.sku || path.basename(filePath, '.mdx'),
|
||||
slug: path.basename(filePath, '.mdx'),
|
||||
locale: locale as 'en' | 'de',
|
||||
categories: formattedCategories,
|
||||
description: data.description || '',
|
||||
featuredImage: mediaIds.length > 0 ? mediaIds[0] : undefined,
|
||||
images: mediaIds.length > 0 ? mediaIds : undefined,
|
||||
content: wrapLexical(lexicalContent) as any,
|
||||
application: data.application
|
||||
? (wrapLexical(parseMarkdownToLexical(data.application)) as any)
|
||||
: undefined,
|
||||
_status: 'published' as any,
|
||||
};
|
||||
|
||||
// Check if product exists (by sku combined with locale, since slug may differ by language)
|
||||
const existing = await payload.find({
|
||||
collection: 'products',
|
||||
where: {
|
||||
and: [{ slug: { equals: productData.slug } }, { locale: { equals: locale } }],
|
||||
},
|
||||
});
|
||||
|
||||
if (existing.docs.length > 0) {
|
||||
console.log(`Updating existing product ${productData.slug} (${locale})`);
|
||||
await payload.update({
|
||||
collection: 'products',
|
||||
id: existing.docs[0].id,
|
||||
data: productData,
|
||||
});
|
||||
} else {
|
||||
console.log(`Creating new product ${productData.slug} (${locale})`);
|
||||
await payload.create({
|
||||
collection: 'products',
|
||||
data: productData,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`\n✅ Products Migration Complete!`);
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
migrateProducts().catch(console.error);
|
||||
Reference in New Issue
Block a user