Files
dss/server/migrations/20251208000000-add-rbac.js
Digital Production Factory 276ed71f31 Initial commit: Clean DSS implementation
Migrated from design-system-swarm with fresh git history.
Old project history preserved in /home/overbits/apps/design-system-swarm

Core components:
- MCP Server (Python FastAPI with mcp 1.23.1)
- Claude Plugin (agents, commands, skills, strategies, hooks, core)
- DSS Backend (dss-mvp1 - token translation, Figma sync)
- Admin UI (Node.js/React)
- Server (Node.js/Express)
- Storybook integration (dss-mvp1/.storybook)

Self-contained configuration:
- All paths relative or use DSS_BASE_PATH=/home/overbits/dss
- PYTHONPATH configured for dss-mvp1 and dss-claude-plugin
- .env file with all configuration
- Claude plugin uses ${CLAUDE_PLUGIN_ROOT} for portability

Migration completed: $(date)
🤖 Clean migration with full functionality preserved
2025-12-09 18:45:48 -03:00

186 lines
6.8 KiB
JavaScript

import { DataTypes } from 'sequelize';
import { v4 as uuidv4 } from 'uuid';
const ROLES = ['admin', 'ui_team', 'ux_team', 'qa_team'];
const ACTIONS = ['create', 'read', 'update', 'delete'];
export default {
up: async (queryInterface) => {
const transaction = await queryInterface.sequelize.transaction();
try {
// 1. Add role and team_id columns to Users table
await queryInterface.addColumn('Users', 'team_id', {
type: DataTypes.STRING,
allowNull: true
}, { transaction });
// Update existing users to use new role enum values
// First, add a temporary column
await queryInterface.addColumn('Users', 'role_new', {
type: DataTypes.ENUM(...ROLES),
allowNull: true
}, { transaction });
// Map old roles to new roles (if Users table exists with old roles)
await queryInterface.sequelize.query(`
UPDATE "Users" SET role_new = CASE
WHEN role = 'admin' THEN 'admin'
WHEN role = 'designer' THEN 'ux_team'
WHEN role = 'developer' THEN 'ui_team'
WHEN role = 'viewer' THEN 'qa_team'
ELSE 'ui_team'
END
`, { transaction });
// Drop old role column
await queryInterface.removeColumn('Users', 'role', { transaction });
// Rename new role column to role
await queryInterface.renameColumn('Users', 'role_new', 'role', { transaction });
// Set default value for role
await queryInterface.changeColumn('Users', 'role', {
type: DataTypes.ENUM(...ROLES),
allowNull: false,
defaultValue: 'ui_team'
}, { transaction });
// 2. Create Team Permissions table
await queryInterface.createTable('TeamPermissions', {
id: {
type: DataTypes.UUID,
defaultValue: DataTypes.UUIDV4,
primaryKey: true
},
role: {
type: DataTypes.ENUM(...ROLES),
allowNull: false
},
permission: {
type: DataTypes.STRING,
allowNull: false
},
resource: {
type: DataTypes.STRING,
allowNull: false
},
action: {
type: DataTypes.ENUM(...ACTIONS),
allowNull: false
},
createdAt: {
type: DataTypes.DATE,
allowNull: false,
defaultValue: DataTypes.NOW
},
updatedAt: {
type: DataTypes.DATE,
allowNull: false,
defaultValue: DataTypes.NOW
}
}, { transaction });
// 3. Add Unique Constraint
await queryInterface.addConstraint('TeamPermissions', {
fields: ['role', 'resource', 'action'],
type: 'unique',
name: 'unique_role_resource_action',
transaction
});
// 4. Seed Default Permissions
const timestamp = new Date();
const seeds = [
// UI Team Permissions
{ role: 'ui_team', permission: 'sync_figma', resource: 'figma', action: 'create' },
{ role: 'ui_team', permission: 'view_figma', resource: 'figma', action: 'read' },
{ role: 'ui_team', permission: 'quickwins', resource: 'analysis', action: 'read' },
{ role: 'ui_team', permission: 'regression', resource: 'analysis', action: 'create' },
{ role: 'ui_team', permission: 'view_metrics', resource: 'metrics', action: 'read' },
// UX Team Permissions
{ role: 'ux_team', permission: 'view_components', resource: 'components', action: 'read' },
{ role: 'ux_team', permission: 'update_components', resource: 'components', action: 'update' },
{ role: 'ux_team', permission: 'view_tokens', resource: 'tokens', action: 'read' },
{ role: 'ux_team', permission: 'update_tokens', resource: 'tokens', action: 'update' },
{ role: 'ux_team', permission: 'view_icons', resource: 'icons', action: 'read' },
{ role: 'ux_team', permission: 'update_icons', resource: 'icons', action: 'update' },
{ role: 'ux_team', permission: 'customize_figma_plugin', resource: 'figma', action: 'update' },
{ role: 'ux_team', permission: 'view_metrics', resource: 'metrics', action: 'read' },
// QA Team Permissions
{ role: 'qa_team', permission: 'test_components', resource: 'components', action: 'read' },
{ role: 'qa_team', permission: 'create_issue', resource: 'issues', action: 'create' },
{ role: 'qa_team', permission: 'view_metrics', resource: 'metrics', action: 'read' },
{ role: 'qa_team', permission: 'run_esre', resource: 'testing', action: 'create' }
].map(s => ({
...s,
id: uuidv4(),
createdAt: timestamp,
updatedAt: timestamp
}));
if (seeds.length > 0) {
await queryInterface.bulkInsert('TeamPermissions', seeds, { transaction });
}
await transaction.commit();
console.log('✓ RBAC migration completed successfully');
} catch (err) {
await transaction.rollback();
console.error('✗ RBAC migration failed:', err);
throw err;
}
},
down: async (queryInterface) => {
const transaction = await queryInterface.sequelize.transaction();
try {
// Drop team_permissions table
await queryInterface.dropTable('TeamPermissions', { transaction });
// Remove team_id column
await queryInterface.removeColumn('Users', 'team_id', { transaction });
// Restore old role enum
await queryInterface.addColumn('Users', 'role_old', {
type: DataTypes.ENUM('admin', 'designer', 'developer', 'viewer'),
allowNull: true
}, { transaction });
// Map new roles back to old roles
await queryInterface.sequelize.query(`
UPDATE "Users" SET role_old = CASE
WHEN role = 'admin' THEN 'admin'
WHEN role = 'ux_team' THEN 'designer'
WHEN role = 'ui_team' THEN 'developer'
WHEN role = 'qa_team' THEN 'viewer'
ELSE 'designer'
END
`, { transaction });
await queryInterface.removeColumn('Users', 'role', { transaction });
await queryInterface.renameColumn('Users', 'role_old', 'role', { transaction });
await queryInterface.changeColumn('Users', 'role', {
type: DataTypes.ENUM('admin', 'designer', 'developer', 'viewer'),
allowNull: false,
defaultValue: 'designer'
}, { transaction });
// Cleanup Enums
await queryInterface.sequelize.query('DROP TYPE IF EXISTS "enum_Users_role_new";', { transaction });
await queryInterface.sequelize.query('DROP TYPE IF EXISTS "enum_TeamPermissions_role";', { transaction });
await queryInterface.sequelize.query('DROP TYPE IF EXISTS "enum_TeamPermissions_action";', { transaction });
await transaction.commit();
console.log('✓ RBAC migration rolled back successfully');
} catch (err) {
await transaction.rollback();
console.error('✗ RBAC migration rollback failed:', err);
throw err;
}
}
};