feat(backend): update collections, config and migration tools
Update Payload CMS configuration, collections (Audit, Posts), and add migration scripts/reports.
This commit is contained in:
188
apps/backend/scripts/migration/reporter.ts
Normal file
188
apps/backend/scripts/migration/reporter.ts
Normal file
@@ -0,0 +1,188 @@
|
||||
/**
|
||||
* Migration Reporter
|
||||
* Story 1.3: Content Migration Script
|
||||
*
|
||||
* Generates migration reports in JSON and Markdown formats
|
||||
*/
|
||||
|
||||
import type { MigrationReport, CollectionMigrationResult } from './types'
|
||||
import { writeFile, mkdir } from 'fs/promises'
|
||||
import { join } from 'path'
|
||||
import { existsSync } from 'fs'
|
||||
|
||||
// ============================================================
|
||||
// REPORT GENERATION
|
||||
// ============================================================
|
||||
|
||||
/**
|
||||
* Create a new empty report
|
||||
*/
|
||||
export function createReport(dryRun: boolean = false): MigrationReport {
|
||||
return {
|
||||
timestamp: new Date().toISOString(),
|
||||
dryRun,
|
||||
summary: {
|
||||
total: 0,
|
||||
created: 0,
|
||||
skipped: 0,
|
||||
failed: 0,
|
||||
},
|
||||
byCollection: {},
|
||||
details: {},
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update report with collection results
|
||||
*/
|
||||
export function updateReport(
|
||||
report: MigrationReport,
|
||||
collectionResult: CollectionMigrationResult,
|
||||
): MigrationReport {
|
||||
const { collection, created, skipped, failed } = collectionResult
|
||||
|
||||
// Update byCollection stats
|
||||
report.byCollection[collection] = { created, skipped, failed }
|
||||
|
||||
// Store details
|
||||
report.details[collection] = collectionResult
|
||||
|
||||
// Update summary
|
||||
report.summary.total += created + skipped + failed
|
||||
report.summary.created += created
|
||||
report.summary.skipped += skipped
|
||||
report.summary.failed += failed
|
||||
|
||||
return report
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate markdown report
|
||||
*/
|
||||
export function generateMarkdownReport(report: MigrationReport): string {
|
||||
const lines: string[] = []
|
||||
|
||||
lines.push(`# Migration Report`)
|
||||
lines.push(``)
|
||||
lines.push(`**Generated:** ${new Date(report.timestamp).toLocaleString('zh-TW')}`)
|
||||
lines.push(`**Mode:** ${report.dryRun ? '🧪 Dry Run (no changes made)' : '✅ Live Migration'}`)
|
||||
lines.push(``)
|
||||
lines.push(`---`)
|
||||
lines.push(``)
|
||||
|
||||
// Summary section
|
||||
lines.push(`## Summary`)
|
||||
lines.push(``)
|
||||
lines.push(`| Metric | Count |`)
|
||||
lines.push(`|--------|-------|`)
|
||||
lines.push(`| Total Items | ${report.summary.total} |`)
|
||||
lines.push(`| ✅ Created | ${report.summary.created} |`)
|
||||
lines.push(`| ⏭️ Skipped | ${report.summary.skipped} |`)
|
||||
lines.push(`| ❌ Failed | ${report.summary.failed} |`)
|
||||
lines.push(``)
|
||||
|
||||
// By collection section
|
||||
lines.push(`## By Collection`)
|
||||
lines.push(``)
|
||||
|
||||
for (const [collection, stats] of Object.entries(report.byCollection)) {
|
||||
lines.push(`### ${collection.charAt(0).toUpperCase() + collection.slice(1)}`)
|
||||
lines.push(``)
|
||||
lines.push(`| Metric | Count |`)
|
||||
lines.push(`|--------|-------|`)
|
||||
lines.push(`| Created | ${stats.created} |`)
|
||||
lines.push(`| Skipped | ${stats.skipped} |`)
|
||||
lines.push(`| Failed | ${stats.failed} |`)
|
||||
lines.push(``)
|
||||
}
|
||||
|
||||
// Details section
|
||||
if (report.details) {
|
||||
lines.push(`## Details`)
|
||||
lines.push(``)
|
||||
|
||||
for (const [collection, result] of Object.entries(report.details)) {
|
||||
lines.push(`### ${collection.charAt(0).toUpperCase() + collection.slice(1)}`)
|
||||
lines.push(``)
|
||||
|
||||
// Created items
|
||||
if (result.results.some((r) => r.success)) {
|
||||
lines.push(`#### ✅ Created (${result.results.filter((r) => r.success).length})`)
|
||||
lines.push(``)
|
||||
for (const item of result.results.filter((r) => r.success)) {
|
||||
lines.push(`- \`${item.slug}\` (ID: ${item.id})`)
|
||||
}
|
||||
lines.push(``)
|
||||
}
|
||||
|
||||
// Failed items
|
||||
if (result.results.some((r) => !r.success)) {
|
||||
lines.push(`#### ❌ Failed (${result.results.filter((r) => !r.success).length})`)
|
||||
lines.push(``)
|
||||
for (const item of result.results.filter((r) => !r.success)) {
|
||||
lines.push(`- \`${item.slug}\`: ${item.error}`)
|
||||
}
|
||||
lines.push(``)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return lines.join('\n')
|
||||
}
|
||||
|
||||
/**
|
||||
* Save report to files
|
||||
*/
|
||||
export async function saveReport(
|
||||
report: MigrationReport,
|
||||
outputDir: string = './reports',
|
||||
): Promise<void> {
|
||||
// Ensure directory exists
|
||||
if (!existsSync(outputDir)) {
|
||||
await mkdir(outputDir, { recursive: true })
|
||||
}
|
||||
|
||||
const timestamp = new Date().toISOString().replace(/[:.]/g, '-').split('T')[0]
|
||||
const baseName = `migration-${timestamp}`
|
||||
|
||||
// Save JSON report
|
||||
const jsonPath = join(outputDir, `${baseName}.json`)
|
||||
await writeFile(jsonPath, JSON.stringify(report, null, 2), 'utf-8')
|
||||
|
||||
// Save Markdown report
|
||||
const mdPath = join(outputDir, `${baseName}.md`)
|
||||
await writeFile(mdPath, generateMarkdownReport(report), 'utf-8')
|
||||
|
||||
console.log(`\n📄 Reports saved:`)
|
||||
console.log(` - JSON: ${jsonPath}`)
|
||||
console.log(` - Markdown: ${mdPath}`)
|
||||
}
|
||||
|
||||
/**
|
||||
* Print report summary to console
|
||||
*/
|
||||
export function printReportSummary(report: MigrationReport): void {
|
||||
console.log(`\n${'='.repeat(60)}`)
|
||||
console.log(`📊 MIGRATION REPORT`)
|
||||
console.log(`${'='.repeat(60)}`)
|
||||
console.log(``)
|
||||
console.log(`Mode: ${report.dryRun ? '🧪 Dry Run' : '✅ Live'}`)
|
||||
console.log(``)
|
||||
console.log(`Summary:`)
|
||||
console.log(` Total: ${report.summary.total}`)
|
||||
console.log(` Created: ${report.summary.created} ✅`)
|
||||
console.log(` Skipped: ${report.summary.skipped} ⏭️`)
|
||||
console.log(` Failed: ${report.summary.failed} ❌`)
|
||||
console.log(``)
|
||||
|
||||
if (report.byCollection) {
|
||||
console.log(`By Collection:`)
|
||||
for (const [collection, stats] of Object.entries(report.byCollection)) {
|
||||
console.log(
|
||||
` ${collection}: ${stats.created} created, ${stats.skipped} skipped, ${stats.failed} failed`,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`${'='.repeat(60)}`)
|
||||
}
|
||||
Reference in New Issue
Block a user