246 lines
6.9 KiB
JavaScript
246 lines
6.9 KiB
JavaScript
/**
|
|
* Strapi Content Migration Script
|
|
*
|
|
* This script exports content from the old Strapi server and imports it to the new one.
|
|
*
|
|
* Usage:
|
|
* 1. Set environment variables:
|
|
*
|
|
* NEW_STRAPI_URL=https://your-new-strapi-server.com
|
|
* NEW_STRAPI_API_TOKEN=your-api-token (if authentication is required)
|
|
*
|
|
* 2. Run: node scripts/migrate-strapi.js
|
|
*/
|
|
|
|
const fs = require('fs');
|
|
const path = require('path');
|
|
|
|
// Configuration from environment variables
|
|
const OLD_STRAPI_URL = process.env.NEXT_PUBLIC_STRAPI_BASE_URL;
|
|
const NEW_STRAPI_URL = process.env.NEW_STRAPI_URL || '';
|
|
const NEW_STRAPI_API_TOKEN = process.env.NEW_STRAPI_API_TOKEN || '';
|
|
|
|
// Content types to migrate
|
|
const CONTENT_TYPES = [
|
|
'schoolforschools',
|
|
'whyschoolforschools'
|
|
];
|
|
|
|
// Directory to store exported data
|
|
const EXPORT_DIR = path.join(__dirname, 'strapi-export');
|
|
|
|
/**
|
|
* Fetch all entries from a Strapi content type (handles pagination)
|
|
*/
|
|
async function fetchStrapiContent(contentType, baseUrl) {
|
|
console.log(`\n📥 Fetching ${contentType} from ${baseUrl}...`);
|
|
|
|
try {
|
|
let allData = [];
|
|
let page = 1;
|
|
let hasMore = true;
|
|
const pageSize = 100;
|
|
|
|
while (hasMore) {
|
|
const url = `${baseUrl}/api/${contentType}?pagination[page]=${page}&pagination[pageSize]=${pageSize}&populate=*`;
|
|
const response = await fetch(url, {
|
|
headers: {
|
|
'Accept': 'application/json',
|
|
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
|
|
}
|
|
});
|
|
|
|
if (!response.ok) {
|
|
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
}
|
|
|
|
const data = await response.json();
|
|
|
|
// Handle Strapi v4 format with pagination
|
|
if (data.data) {
|
|
allData = allData.concat(data.data);
|
|
|
|
// Check if there are more pages
|
|
if (data.meta?.pagination) {
|
|
const { page: currentPage, pageCount } = data.meta.pagination;
|
|
hasMore = currentPage < pageCount;
|
|
page++;
|
|
} else {
|
|
hasMore = false;
|
|
}
|
|
} else if (Array.isArray(data)) {
|
|
// Handle Strapi v3 format or direct array (no pagination)
|
|
allData = data;
|
|
hasMore = false;
|
|
} else {
|
|
hasMore = false;
|
|
}
|
|
}
|
|
|
|
console.log(` Found ${allData.length} entries`);
|
|
return allData;
|
|
} catch (error) {
|
|
console.error(`❌ Error fetching ${contentType}:`, error.message);
|
|
return [];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Export content to JSON files
|
|
*/
|
|
async function exportContent() {
|
|
console.log('🚀 Starting Strapi content export...');
|
|
console.log(`📡 Source: ${OLD_STRAPI_URL}\n`);
|
|
|
|
// Create export directory
|
|
if (!fs.existsSync(EXPORT_DIR)) {
|
|
fs.mkdirSync(EXPORT_DIR, { recursive: true });
|
|
}
|
|
|
|
const exportData = {};
|
|
|
|
for (const contentType of CONTENT_TYPES) {
|
|
const content = await fetchStrapiContent(contentType, OLD_STRAPI_URL);
|
|
exportData[contentType] = content;
|
|
|
|
// Save individual file
|
|
const filePath = path.join(EXPORT_DIR, `${contentType}.json`);
|
|
fs.writeFileSync(filePath, JSON.stringify(content, null, 2));
|
|
console.log(`✅ Exported ${content.length} entries for ${contentType}`);
|
|
}
|
|
|
|
// Save combined export
|
|
const combinedPath = path.join(EXPORT_DIR, 'all-content.json');
|
|
fs.writeFileSync(combinedPath, JSON.stringify(exportData, null, 2));
|
|
|
|
console.log(`\n✅ Export complete! Data saved to: ${EXPORT_DIR}`);
|
|
return exportData;
|
|
}
|
|
|
|
/**
|
|
* Upload content to new Strapi server
|
|
*/
|
|
async function uploadToStrapi(contentType, entries, baseUrl, apiToken) {
|
|
console.log(`\n📤 Uploading ${entries.length} entries for ${contentType}...`);
|
|
|
|
const headers = {
|
|
'Content-Type': 'application/json',
|
|
'Accept': 'application/json'
|
|
};
|
|
|
|
if (apiToken) {
|
|
headers['Authorization'] = `Bearer ${apiToken}`;
|
|
}
|
|
|
|
let successCount = 0;
|
|
let errorCount = 0;
|
|
|
|
for (const entry of entries) {
|
|
try {
|
|
// Remove id and other Strapi-specific fields that shouldn't be sent
|
|
const { id, createdAt, updatedAt, publishedAt, ...entryData } = entry;
|
|
|
|
// Handle nested relations - you may need to adjust this based on your schema
|
|
const payload = {
|
|
data: entryData
|
|
};
|
|
|
|
const response = await fetch(`${baseUrl}/api/${contentType}`, {
|
|
method: 'POST',
|
|
headers,
|
|
body: JSON.stringify(payload)
|
|
});
|
|
|
|
if (response.ok) {
|
|
successCount++;
|
|
process.stdout.write('.');
|
|
} else {
|
|
const errorText = await response.text();
|
|
console.error(`\n❌ Failed to upload entry ${id}: ${response.status} - ${errorText}`);
|
|
errorCount++;
|
|
}
|
|
} catch (error) {
|
|
console.error(`\n❌ Error uploading entry:`, error.message);
|
|
errorCount++;
|
|
}
|
|
}
|
|
|
|
console.log(`\n✅ ${successCount} entries uploaded, ${errorCount} errors`);
|
|
return { successCount, errorCount };
|
|
}
|
|
|
|
/**
|
|
* Import content from exported files
|
|
*/
|
|
async function importContent() {
|
|
if (!NEW_STRAPI_URL) {
|
|
console.error('❌ NEW_STRAPI_URL environment variable is required for import');
|
|
return;
|
|
}
|
|
|
|
console.log('\n🚀 Starting Strapi content import...');
|
|
console.log(`📡 Destination: ${NEW_STRAPI_URL}\n`);
|
|
|
|
const combinedPath = path.join(EXPORT_DIR, 'all-content.json');
|
|
|
|
if (!fs.existsSync(combinedPath)) {
|
|
console.error(`❌ Export file not found: ${combinedPath}`);
|
|
console.log('💡 Run export first by calling exportContent()');
|
|
return;
|
|
}
|
|
|
|
const exportData = JSON.parse(fs.readFileSync(combinedPath, 'utf8'));
|
|
|
|
for (const contentType of CONTENT_TYPES) {
|
|
if (exportData[contentType]) {
|
|
await uploadToStrapi(contentType, exportData[contentType], NEW_STRAPI_URL, NEW_STRAPI_API_TOKEN);
|
|
}
|
|
}
|
|
|
|
console.log('\n✅ Import complete!');
|
|
}
|
|
|
|
/**
|
|
* Main execution
|
|
*/
|
|
async function main() {
|
|
const command = process.argv[2] || 'export';
|
|
|
|
if (command === 'export') {
|
|
await exportContent();
|
|
} else if (command === 'import') {
|
|
await importContent();
|
|
} else if (command === 'migrate') {
|
|
// Export first, then import
|
|
const exportData = await exportContent();
|
|
console.log('\n⏳ Waiting 2 seconds before import...\n');
|
|
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
await importContent();
|
|
} else {
|
|
console.log(`
|
|
Usage: node scripts/migrate-strapi.js [command]
|
|
|
|
Commands:
|
|
export - Export content from old Strapi server (default)
|
|
import - Import content to new Strapi server
|
|
migrate - Export and import in one go
|
|
|
|
Environment Variables:
|
|
OLD_STRAPI_URL - Source Strapi server URL
|
|
NEW_STRAPI_URL - Destination Strapi server URL (required for import)
|
|
NEW_STRAPI_API_TOKEN - API token for new Strapi server (if authentication required)
|
|
|
|
Example:
|
|
OLD_STRAPI_URL=https://old-server.com NEW_STRAPI_URL=https://new-server.com NEW_STRAPI_API_TOKEN=xxx node scripts/migrate-strapi.js migrate
|
|
`);
|
|
}
|
|
}
|
|
|
|
// Run if executed directly
|
|
if (require.main === module) {
|
|
main().catch(console.error);
|
|
}
|
|
|
|
module.exports = { exportContent, importContent };
|
|
|