#!/usr/bin/env tsx /** * Update posts with heroImage by matching Webflow URLs to uploaded Media */ import { config as dotenvConfig } from 'dotenv' import { resolve, dirname } from 'path' import { fileURLToPath } from 'url' const __filename = fileURLToPath(import.meta.url) const __dirname = dirname(__filename) const envPath = resolve(__dirname, '../../.env') dotenvConfig({ path: envPath }) // Debug console.log('Loading .env from:', envPath) console.log('PAYLOAD_SECRET loaded:', !!process.env.PAYLOAD_SECRET) import { getPayload } from 'payload' import config from '../../src/payload.config' import { parseWebflowCSV } from './csvParser' import { transformPosts } from './transformers' async function main() { const payload = await getPayload({ config }) console.log('πŸ” Loading media files...') const media = await payload.find({ collection: 'media', limit: 100, depth: 0 }) // Create filename to ID mapping const filenameToId = new Map() media.docs.forEach((m: any) => { filenameToId.set(m.filename, m.id) }) console.log(`πŸ“ Found ${filenameToId.size} media files`) console.log('πŸ“‚ Loading CSV data...') const data = await parseWebflowCSV('/Users/pukpuk/Dev/website-enchun-mgr/ζ©ηΎ€ζ•Έδ½θ‘ŒιŠ· - θ‘ŒιŠ·ζ”Ύε€§ι‘ι›†.csv') console.log('πŸ“ Loading posts...') const posts = await payload.find({ collection: 'posts', limit: 100, depth: 0 }) const postsBySlug = new Map() posts.docs.forEach((post: any) => { postsBySlug.set(post.slug, post) }) console.log('\nπŸ”— Matching hero images...\n') let matched = 0 let updated = 0 let notFound = 0 for (const webflowPost of data.posts) { const featuredImage = webflowPost.featuredImage if (!featuredImage) continue // Extract filename from Webflow URL const urlParts = featuredImage.split('/') const webflowFilename = urlParts[urlParts.length - 1] // Find matching media by comparing filename patterns let mediaId: string | null = null for (const [filename, id] of filenameToId.entries()) { // Check if Webflow filename is contained in Payload filename or vice versa // They may have different prefixes but the hash should match if (filename.includes(webflowFilename.split('.')[0].substring(0, 15)) || webflowFilename.includes(filename.split('.')[0].substring(0, 15))) { mediaId = id matched++ break } } if (!mediaId) { notFound++ console.log(`❌ No match: ${webflowPost.title?.substring(0, 40)}`) console.log(` URL: ${webflowFilename}`) continue } // Find and update the post const transformed = transformPosts([webflowPost])[0] const post = postsBySlug.get(transformed.slug) if (post && !post.heroImage) { await payload.update({ collection: 'posts', id: post.id, data: { heroImage: mediaId }, }) updated++ console.log(`βœ“ Updated: ${webflowPost.title?.substring(0, 40)}`) } } console.log('\n=== SUMMARY ===') console.log(`Matched: ${matched}`) console.log(`Updated: ${updated}`) console.log(`Not found: ${notFound}`) } main().catch(console.error)