267 lines
8.1 KiB
JavaScript
267 lines
8.1 KiB
JavaScript
'use strict';
|
|
|
|
/**
|
|
* Migration: Backfill project_element_defaults for existing projects
|
|
*
|
|
* For each existing project that doesn't have project_element_defaults,
|
|
* create a snapshot of the current global element_type_defaults.
|
|
*/
|
|
|
|
// Default element types to ensure they exist before backfilling
|
|
const DEFAULT_ELEMENT_TYPES = [
|
|
{
|
|
element_type: 'navigation_next',
|
|
name: 'Navigation Forward Button',
|
|
sort_order: 1,
|
|
settings_json: JSON.stringify({
|
|
label: 'Navigation: Forward',
|
|
navLabel: 'Forward',
|
|
navType: 'forward',
|
|
navDisabled: false,
|
|
transitionReverseMode: 'auto_reverse',
|
|
transitionDurationSec: 0.7,
|
|
appearDelaySec: 0,
|
|
appearDurationSec: null,
|
|
}),
|
|
},
|
|
{
|
|
element_type: 'navigation_prev',
|
|
name: 'Navigation Back Button',
|
|
sort_order: 2,
|
|
settings_json: JSON.stringify({
|
|
label: 'Navigation: Back',
|
|
navLabel: 'Back',
|
|
navType: 'back',
|
|
navDisabled: false,
|
|
transitionReverseMode: 'auto_reverse',
|
|
transitionDurationSec: 0.7,
|
|
appearDelaySec: 0,
|
|
appearDurationSec: null,
|
|
}),
|
|
},
|
|
{
|
|
element_type: 'tooltip',
|
|
name: 'Tooltip',
|
|
sort_order: 3,
|
|
settings_json: JSON.stringify({
|
|
label: 'Tooltip',
|
|
tooltipTitle: 'Tooltip title',
|
|
tooltipText: 'Tooltip text',
|
|
appearDelaySec: 0,
|
|
appearDurationSec: null,
|
|
}),
|
|
},
|
|
{
|
|
element_type: 'description',
|
|
name: 'Description',
|
|
sort_order: 4,
|
|
settings_json: JSON.stringify({
|
|
label: 'Description',
|
|
descriptionTitle: 'TITLE',
|
|
descriptionText: '',
|
|
descriptionTitleFontSize: '48px',
|
|
descriptionTextFontSize: '36px',
|
|
descriptionTitleFontFamily: 'inherit',
|
|
descriptionTextFontFamily: 'inherit',
|
|
descriptionTitleColor: '#000000',
|
|
descriptionTextColor: '#4B5563',
|
|
descriptionBackgroundColor: 'transparent',
|
|
appearDelaySec: 0,
|
|
appearDurationSec: null,
|
|
}),
|
|
},
|
|
{
|
|
element_type: 'gallery',
|
|
name: 'Gallery',
|
|
sort_order: 5,
|
|
settings_json: JSON.stringify({
|
|
label: 'Gallery',
|
|
galleryCards: [{ imageUrl: '', title: 'Card 1', description: '' }],
|
|
appearDelaySec: 0,
|
|
appearDurationSec: null,
|
|
}),
|
|
},
|
|
{
|
|
element_type: 'carousel',
|
|
name: 'Carousel',
|
|
sort_order: 6,
|
|
settings_json: JSON.stringify({
|
|
label: 'Carousel',
|
|
carouselSlides: [{ imageUrl: '', caption: 'Slide 1' }],
|
|
carouselPrevIconUrl: '',
|
|
carouselNextIconUrl: '',
|
|
appearDelaySec: 0,
|
|
appearDurationSec: null,
|
|
}),
|
|
},
|
|
{
|
|
element_type: 'video_player',
|
|
name: 'Video Player',
|
|
sort_order: 7,
|
|
settings_json: JSON.stringify({
|
|
label: 'Video Player',
|
|
mediaUrl: '',
|
|
mediaAutoplay: true,
|
|
mediaLoop: true,
|
|
mediaMuted: true,
|
|
appearDelaySec: 0,
|
|
appearDurationSec: null,
|
|
}),
|
|
},
|
|
{
|
|
element_type: 'audio_player',
|
|
name: 'Audio Player',
|
|
sort_order: 8,
|
|
settings_json: JSON.stringify({
|
|
label: 'Audio Player',
|
|
mediaUrl: '',
|
|
mediaAutoplay: true,
|
|
mediaLoop: true,
|
|
mediaMuted: false,
|
|
appearDelaySec: 0,
|
|
appearDurationSec: null,
|
|
}),
|
|
},
|
|
];
|
|
|
|
module.exports = {
|
|
async up(queryInterface, Sequelize) {
|
|
// First, ensure element_type_defaults has all default rows
|
|
// This is needed because the API's lazy initialization won't have run yet during migration
|
|
const [existingTypes] = await queryInterface.sequelize.query(
|
|
`SELECT element_type FROM element_type_defaults WHERE "deletedAt" IS NULL`,
|
|
{ type: Sequelize.QueryTypes.SELECT }
|
|
);
|
|
|
|
const existingTypeSet = new Set(
|
|
Array.isArray(existingTypes)
|
|
? existingTypes.map((t) => t.element_type)
|
|
: existingTypes
|
|
? [existingTypes.element_type]
|
|
: []
|
|
);
|
|
|
|
// Insert missing element types
|
|
for (const defaultType of DEFAULT_ELEMENT_TYPES) {
|
|
if (!existingTypeSet.has(defaultType.element_type)) {
|
|
await queryInterface.sequelize.query(
|
|
`INSERT INTO element_type_defaults (id, element_type, name, sort_order, settings_json, "createdAt", "updatedAt")
|
|
VALUES (gen_random_uuid(), :element_type, :name, :sort_order, :settings_json, NOW(), NOW())`,
|
|
{
|
|
replacements: {
|
|
element_type: defaultType.element_type,
|
|
name: defaultType.name,
|
|
sort_order: defaultType.sort_order,
|
|
settings_json: defaultType.settings_json,
|
|
},
|
|
}
|
|
);
|
|
console.log(`Created missing element_type_default: ${defaultType.element_type}`);
|
|
}
|
|
}
|
|
|
|
// Get all existing projects
|
|
const [projects] = await queryInterface.sequelize.query(
|
|
`SELECT id FROM projects WHERE "deletedAt" IS NULL`,
|
|
{ type: Sequelize.QueryTypes.SELECT }
|
|
);
|
|
|
|
if (!projects || projects.length === 0) {
|
|
console.log('No projects found, skipping backfill');
|
|
return;
|
|
}
|
|
|
|
// Get all global element type defaults (now guaranteed to have all types)
|
|
const [globalDefaults] = await queryInterface.sequelize.query(
|
|
`SELECT id, element_type, name, sort_order, settings_json
|
|
FROM element_type_defaults
|
|
WHERE "deletedAt" IS NULL`,
|
|
{ type: Sequelize.QueryTypes.SELECT }
|
|
);
|
|
|
|
if (!globalDefaults || globalDefaults.length === 0) {
|
|
console.log('No global element type defaults found, skipping backfill');
|
|
return;
|
|
}
|
|
|
|
const projectIds = Array.isArray(projects) ? projects.map((p) => p.id) : [projects.id];
|
|
const globalDefaultRows = Array.isArray(globalDefaults) ? globalDefaults : [globalDefaults];
|
|
|
|
// For each project, add any missing element type defaults
|
|
for (const projectId of projectIds) {
|
|
// Get existing element types for this project
|
|
const [existingDefaults] = await queryInterface.sequelize.query(
|
|
`SELECT element_type FROM project_element_defaults
|
|
WHERE "projectId" = :projectId AND "deletedAt" IS NULL`,
|
|
{
|
|
replacements: { projectId },
|
|
type: Sequelize.QueryTypes.SELECT,
|
|
}
|
|
);
|
|
|
|
const existingProjectTypes = new Set(
|
|
Array.isArray(existingDefaults)
|
|
? existingDefaults.map((d) => d.element_type)
|
|
: existingDefaults
|
|
? [existingDefaults.element_type]
|
|
: []
|
|
);
|
|
|
|
// Create project element defaults for missing types
|
|
let addedCount = 0;
|
|
for (const globalDefault of globalDefaultRows) {
|
|
if (existingProjectTypes.has(globalDefault.element_type)) {
|
|
continue; // Already has this type
|
|
}
|
|
|
|
await queryInterface.sequelize.query(
|
|
`INSERT INTO project_element_defaults
|
|
(id, element_type, name, sort_order, settings_json, source_element_id, snapshot_version, "projectId", "createdAt", "updatedAt")
|
|
VALUES (
|
|
gen_random_uuid(),
|
|
:element_type,
|
|
:name,
|
|
:sort_order,
|
|
:settings_json,
|
|
:source_element_id,
|
|
1,
|
|
:projectId,
|
|
NOW(),
|
|
NOW()
|
|
)`,
|
|
{
|
|
replacements: {
|
|
element_type: globalDefault.element_type,
|
|
name: globalDefault.name,
|
|
sort_order: globalDefault.sort_order,
|
|
settings_json: globalDefault.settings_json,
|
|
source_element_id: globalDefault.id,
|
|
projectId,
|
|
},
|
|
type: Sequelize.QueryTypes.INSERT,
|
|
}
|
|
);
|
|
addedCount++;
|
|
}
|
|
|
|
if (addedCount > 0) {
|
|
console.log(`Backfilled ${addedCount} element defaults for project ${projectId}`);
|
|
} else {
|
|
console.log(`Project ${projectId} already has all element defaults`);
|
|
}
|
|
}
|
|
|
|
console.log('Successfully backfilled project_element_defaults for existing projects');
|
|
},
|
|
|
|
async down(queryInterface, _Sequelize) {
|
|
// Delete all project_element_defaults with snapshot_version = 1
|
|
// (only the ones we created during backfill)
|
|
await queryInterface.sequelize.query(
|
|
`DELETE FROM project_element_defaults WHERE snapshot_version = 1`
|
|
);
|
|
|
|
console.log('Successfully removed backfilled project_element_defaults');
|
|
},
|
|
};
|