payload
Version:
Node, React, Headless CMS and Application Framework built on Next.js
220 lines (219 loc) • 9.17 kB
JavaScript
import { calculateVersionLocaleStatuses } from '../shared.js';
export async function up(args) {
const { collectionSlug, globalSlug, payload, req } = args;
if (!collectionSlug && !globalSlug) {
throw new Error('Either collectionSlug or globalSlug must be provided');
}
if (collectionSlug && globalSlug) {
throw new Error('Cannot provide both collectionSlug and globalSlug');
}
const entitySlug = collectionSlug || globalSlug;
// MongoDB collection names are case-insensitive and stored as lowercase
const versionsCollection = `_${entitySlug}_versions`.toLowerCase();
if (!payload.config.localization) {
throw new Error('Localization is not enabled in payload config');
}
// Check if versions are enabled on this collection/global
let entityConfig;
if (collectionSlug) {
const collection = payload.config.collections.find((c)=>c.slug === collectionSlug);
if (collection) {
entityConfig = collection;
}
} else if (globalSlug) {
const global = payload.config.globals.find((g)=>g.slug === globalSlug);
if (global) {
entityConfig = global;
}
}
if (!entityConfig) {
throw new Error(`${collectionSlug ? 'Collection' : 'Global'} not found: ${collectionSlug || globalSlug}`);
}
payload.logger.info({
msg: `Starting _status localization migration for ${collectionSlug ? 'collection' : 'global'}: ${entitySlug}`
});
// Check if versions are enabled in config (skip if not)
if (!entityConfig.versions) {
payload.logger.info({
msg: `Skipping migration for ${collectionSlug ? 'collection' : 'global'}: ${entitySlug} - versions not enabled`
});
return;
}
// Get MongoDB connection
const connection = payload.db.connection;
// Get filtered locales if filterAvailableLocales is defined
let locales = payload.config.localization.localeCodes;
if (typeof payload.config.localization.filterAvailableLocales === 'function') {
const filteredLocaleObjects = await payload.config.localization.filterAvailableLocales({
locales: payload.config.localization.locales,
req
});
locales = filteredLocaleObjects.map((locale)=>locale.code);
}
payload.logger.info({
msg: `Locales: ${locales.join(', ')}`
});
// Check if version._status exists and is NOT already localized
const sampleDoc = await connection.collection(versionsCollection).findOne({});
if (!sampleDoc) {
payload.logger.info({
msg: 'No version documents found, nothing to migrate'
});
return;
}
// Check if _status is already localized
if (sampleDoc.version?._status && typeof sampleDoc.version._status === 'object' && !Array.isArray(sampleDoc.version._status)) {
payload.logger.info({
msg: 'version._status is already localized, migration already completed'
});
return;
}
// Validate that version._status exists and is a string
if (!sampleDoc.version || typeof sampleDoc.version._status !== 'string' || Array.isArray(sampleDoc.version._status)) {
throw new Error(`Migration aborted: version._status field not found or has unexpected format in ${versionsCollection}. ` + `This migration should only run on schemas that have NOT yet been migrated to per-locale status.`);
}
payload.logger.info({
msg: 'Fetching all version documents...'
});
// Get all versions, sorted chronologically
const allVersions = await connection.collection(versionsCollection).find({}).sort({
createdAt: 1,
parent: 1
}).toArray();
payload.logger.info({
msg: `Found ${allVersions.length} version documents`
});
// Transform MongoDB documents to VersionRecord format
const versionRecords = allVersions.map((doc)=>({
id: doc._id.toString(),
_status: doc.version._status,
createdAt: doc.createdAt,
parent: doc.parent?.toString(),
publishedLocale: doc.publishedLocale,
snapshot: doc.snapshot || false
}));
// Calculate status per locale using shared logic
const versionLocaleStatus = calculateVersionLocaleStatuses(versionRecords, locales, payload);
payload.logger.info({
msg: 'Updating version documents with per-locale status...'
});
// Update each version document
let updateCount = 0;
for (const doc of allVersions){
const versionId = doc._id.toString();
const localeStatusMap = versionLocaleStatus.get(versionId);
if (!localeStatusMap) {
payload.logger.warn({
msg: `No status map found for version ${versionId}, skipping`
});
continue;
}
// Build the new _status object: { en: 'published', es: 'draft', ... }
const newStatus = {};
for (const [locale, status] of localeStatusMap.entries()){
newStatus[locale] = status;
}
// Update the document: change version._status from string to object
await connection.collection(versionsCollection).updateOne({
_id: doc._id
}, {
$set: {
'version._status': newStatus
}
});
updateCount++;
}
payload.logger.info({
msg: `Updated ${updateCount} version documents`
});
// Migrate main collection/global document _status to per-locale status object
// Only if it has a status field
if (collectionSlug) {
const mainCollection = collectionSlug;
const mainDoc = await connection.collection(mainCollection).findOne({});
if (mainDoc && '_status' in mainDoc) {
payload.logger.info({
msg: `Migrating main collection documents for: ${mainCollection}`
});
const allDocs = await connection.collection(mainCollection).find({}).toArray();
for (const doc of allDocs){
if (!doc._id) {
continue;
}
// Get the latest version for this document to determine status per locale
const latestVersions = await connection.collection(versionsCollection).find({
parent: doc._id
}).sort({
createdAt: -1
}).limit(1).toArray();
let statusObj = {};
if (latestVersions.length > 0 && latestVersions[0]?.version?._status) {
// Use the status from the latest version
statusObj = latestVersions[0].version._status;
} else {
// Fallback: set all locales to draft
for (const locale of locales){
statusObj[locale] = 'draft';
}
}
// Update main document
await connection.collection(mainCollection).updateOne({
_id: doc._id
}, {
$set: {
_status: statusObj
}
});
}
payload.logger.info({
msg: `Migrated ${allDocs.length} collection documents`
});
} else {
payload.logger.info({
msg: 'Skipping main document status migration (no status field found)'
});
}
} else if (globalSlug) {
// Globals are stored in a single 'globals' collection with globalType discriminator
const globalDoc = await connection.collection('globals').findOne({
globalType: globalSlug
});
if (globalDoc && '_status' in globalDoc && globalDoc._id) {
payload.logger.info({
msg: `Migrating main global document for: ${globalSlug}`
});
// Get the latest version for the global
const latestVersions = await connection.collection(versionsCollection).find({}).sort({
createdAt: -1
}).limit(1).toArray();
let statusObj = {};
if (latestVersions.length > 0 && latestVersions[0]?.version?._status) {
statusObj = latestVersions[0].version._status;
} else {
for (const locale of locales){
statusObj[locale] = 'draft';
}
}
// Update global document
await connection.collection('globals').updateOne({
_id: globalDoc._id,
globalType: globalSlug
}, {
$set: {
_status: statusObj
}
});
payload.logger.info({
msg: 'Migrated global document'
});
} else {
payload.logger.info({
msg: 'Skipping main document status migration (no status field found)'
});
}
}
payload.logger.info({
msg: 'Migration completed successfully'
});
}
//# sourceMappingURL=up.js.map