fixed projects snapshot issue
This commit is contained in:
parent
3d06d927cf
commit
b6cf7a702a
@ -239,12 +239,26 @@ class Project_element_defaultsDBApi extends GenericDBApi {
|
|||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Dedupe by element_type (keep first occurrence)
|
||||||
|
// Prevents unique constraint violations if global defaults have duplicates
|
||||||
|
const seenTypes = new Set();
|
||||||
|
const dedupedDefaults = globalDefaults.rows.filter((row) => {
|
||||||
|
if (seenTypes.has(row.element_type)) {
|
||||||
|
console.warn(
|
||||||
|
`Duplicate element_type in global defaults: ${row.element_type} (skipping)`,
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
seenTypes.add(row.element_type);
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
const currentUserId = options.currentUser?.id || null;
|
const currentUserId = options.currentUser?.id || null;
|
||||||
|
|
||||||
// Create project defaults from global defaults
|
// Create project defaults from global defaults
|
||||||
const projectDefaults = await this.MODEL.bulkCreate(
|
const projectDefaults = await this.MODEL.bulkCreate(
|
||||||
globalDefaults.rows.map((globalDefault) => ({
|
dedupedDefaults.map((globalDefault) => ({
|
||||||
projectId,
|
projectId,
|
||||||
element_type: globalDefault.element_type,
|
element_type: globalDefault.element_type,
|
||||||
name: globalDefault.name,
|
name: globalDefault.name,
|
||||||
|
|||||||
@ -116,19 +116,12 @@ class ProjectsDBApi extends GenericDBApi {
|
|||||||
const project = await super.create(data, options);
|
const project = await super.create(data, options);
|
||||||
|
|
||||||
// Auto-snapshot global element defaults to the new project
|
// Auto-snapshot global element defaults to the new project
|
||||||
try {
|
// Errors propagate to service layer → transaction rollback → proper error to client
|
||||||
const Project_element_defaultsDBApi = require('./project_element_defaults');
|
const Project_element_defaultsDBApi = require('./project_element_defaults');
|
||||||
await Project_element_defaultsDBApi.snapshotGlobalDefaults(project.id, {
|
await Project_element_defaultsDBApi.snapshotGlobalDefaults(project.id, {
|
||||||
...options,
|
...options,
|
||||||
transaction,
|
transaction,
|
||||||
});
|
});
|
||||||
} catch (error) {
|
|
||||||
// Log but don't fail project creation if snapshot fails
|
|
||||||
console.error(
|
|
||||||
'Failed to snapshot global element defaults to project:',
|
|
||||||
error,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return project;
|
return project;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,65 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove duplicate element_type_defaults rows.
|
||||||
|
* Keeps the oldest entry (by createdAt) for each element_type.
|
||||||
|
* This fixes the unique constraint violation during project creation.
|
||||||
|
*/
|
||||||
|
module.exports = {
|
||||||
|
async up(queryInterface, Sequelize) {
|
||||||
|
// Find duplicate element_types
|
||||||
|
const duplicates = await queryInterface.sequelize.query(
|
||||||
|
`SELECT element_type, COUNT(*) as count
|
||||||
|
FROM element_type_defaults
|
||||||
|
WHERE "deletedAt" IS NULL
|
||||||
|
GROUP BY element_type
|
||||||
|
HAVING COUNT(*) > 1`,
|
||||||
|
{ type: Sequelize.QueryTypes.SELECT },
|
||||||
|
);
|
||||||
|
|
||||||
|
if (duplicates.length === 0) {
|
||||||
|
console.log('No duplicate element_type_defaults found.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
`Found ${duplicates.length} element_types with duplicates:`,
|
||||||
|
duplicates.map((d) => d.element_type).join(', '),
|
||||||
|
);
|
||||||
|
|
||||||
|
// For each duplicate element_type, keep oldest and delete others
|
||||||
|
for (const dup of duplicates) {
|
||||||
|
// Get all rows for this element_type, ordered by createdAt
|
||||||
|
const rows = await queryInterface.sequelize.query(
|
||||||
|
`SELECT id, "createdAt"
|
||||||
|
FROM element_type_defaults
|
||||||
|
WHERE element_type = :element_type AND "deletedAt" IS NULL
|
||||||
|
ORDER BY "createdAt" ASC`,
|
||||||
|
{
|
||||||
|
replacements: { element_type: dup.element_type },
|
||||||
|
type: Sequelize.QueryTypes.SELECT,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
// Keep the first (oldest), delete the rest
|
||||||
|
const idsToDelete = rows.slice(1).map((r) => r.id);
|
||||||
|
|
||||||
|
if (idsToDelete.length > 0) {
|
||||||
|
await queryInterface.sequelize.query(
|
||||||
|
`DELETE FROM element_type_defaults WHERE id IN (:ids)`,
|
||||||
|
{ replacements: { ids: idsToDelete } },
|
||||||
|
);
|
||||||
|
console.log(
|
||||||
|
`Deleted ${idsToDelete.length} duplicate(s) for element_type: ${dup.element_type}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('Duplicate removal complete.');
|
||||||
|
},
|
||||||
|
|
||||||
|
async down(_queryInterface, _Sequelize) {
|
||||||
|
// Cannot restore deleted duplicates
|
||||||
|
console.log('Down migration not applicable - duplicates cannot be restored.');
|
||||||
|
},
|
||||||
|
};
|
||||||
Loading…
x
Reference in New Issue
Block a user