@devstroupe/devkit-cli 1.1.10 → 1.1.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -672,6 +672,10 @@ export const routes: Routes = [
672
672
  }
673
673
  console.log('[Roteamento] app.entity-routes.ts sincronizado com as entidades ativas.');
674
674
  updateMainLayoutNavigation(frontendRoot, activeEntities, config.layout?.navigation);
675
+ // Atualizar permissões no backend (seed) e frontend (componente de gerenciamento de perfis)
676
+ const backendRoot = path.resolve(process.cwd(), config.backendPath || './backend');
677
+ updateDatabaseSeedPermissions(backendRoot, config);
678
+ updateRolePermissionsComponent(frontendRoot, config);
675
679
  }
676
680
  function updateMainLayoutNavigation(frontendRoot, entities = [], configuredNavigation) {
677
681
  const layoutPath = path.join(frontendRoot, 'src', 'app', 'shared', 'layouts', 'main-layout', 'main-layout.component.ts');
@@ -730,6 +734,57 @@ function updateMainLayoutNavigation(frontendRoot, entities = [], configuredNavig
730
734
  fs.writeFileSync(layoutPath, updated, 'utf-8');
731
735
  console.log('[Navegação] Menu principal sincronizado com as entidades ativas.');
732
736
  }
737
+ function updateDatabaseSeedPermissions(backendRoot, config) {
738
+ const seedPath = path.join(backendRoot, 'src', 'modules', 'user', 'database-seed.service.ts');
739
+ if (!fs.existsSync(seedPath))
740
+ return;
741
+ const entities = config.entities || [];
742
+ try {
743
+ let content = fs.readFileSync(seedPath, 'utf-8');
744
+ // Substituir allPermissions
745
+ const allPermissionsRegex = /const allPermissions = \[[^\]]*\];/s;
746
+ const newAllPermissions = `const allPermissions = [
747
+ '*',
748
+ 'read:role', 'create:role', 'update:role', 'delete:role', 'manage:permissions',
749
+ ${entities.map((e) => {
750
+ const name = e.name.toLowerCase();
751
+ return `'read:${name}', 'create:${name}', 'update:${name}', 'delete:${name}'`;
752
+ }).join(',\n ')}
753
+ ];`;
754
+ content = content.replace(allPermissionsRegex, newAllPermissions);
755
+ // Substituir permissões do Usuário Comum
756
+ const userPermissionsRegex = /name: 'Usuário Comum',\s+permissions: \[[^\]]*\],/s;
757
+ const newUserPermissions = `name: 'Usuário Comum',\n permissions: [${entities.map((e) => `'read:${e.name.toLowerCase()}'`).join(', ')}],`;
758
+ content = content.replace(userPermissionsRegex, newUserPermissions);
759
+ fs.writeFileSync(seedPath, content, 'utf-8');
760
+ console.log('[Permissões] database-seed.service.ts atualizado com as permissões das novas entidades.');
761
+ }
762
+ catch (err) {
763
+ console.error('[Erro] Falha ao atualizar permissões no database-seed.service.ts:', err);
764
+ }
765
+ }
766
+ function updateRolePermissionsComponent(frontendRoot, config) {
767
+ const compPath = path.join(frontendRoot, 'src', 'app', 'modules', 'role', 'role-permissions.component.ts');
768
+ if (!fs.existsSync(compPath))
769
+ return;
770
+ const entities = config.entities || [];
771
+ try {
772
+ let content = fs.readFileSync(compPath, 'utf-8');
773
+ const resourcesRegex = /resources: ResourceItem\[\] = \[[^\]]*\];/s;
774
+ const newResources = `resources: ResourceItem[] = [
775
+ { name: 'role', label: 'Perfis de Acesso' },
776
+ ${entities.map((e) => {
777
+ return `{ name: '${e.name.toLowerCase()}', label: '${e.label || kebabToPascal(e.name)}' }`;
778
+ }).join(',\n ')}
779
+ ];`;
780
+ content = content.replace(resourcesRegex, newResources);
781
+ fs.writeFileSync(compPath, content, 'utf-8');
782
+ console.log('[Permissões] role-permissions.component.ts atualizado com os recursos das novas entidades.');
783
+ }
784
+ catch (err) {
785
+ console.error('[Erro] Falha ao atualizar recursos no role-permissions.component.ts:', err);
786
+ }
787
+ }
733
788
  function writeAppNavigation(frontendRoot, navigation) {
734
789
  const navigationPath = path.join(frontendRoot, 'src', 'app', 'app.navigation.ts');
735
790
  const items = JSON.stringify(navigation, null, 2)
@@ -80,23 +80,39 @@ function nestMigrationTemplate(entity, entities, timestamp, properties = entity.
80
80
  const relationColumns = relationships
81
81
  .filter((relationship) => (0, relationships_1.isSingularRelationship)(relationship.kind))
82
82
  .map((relationship) => ` { name: '${relationship.foreignKey}', type: 'int', isNullable: ${!relationship.property.required}, isUnique: ${relationship.kind === 'one-to-one'} }`);
83
- // Gera SQL idempotente para cada FK: DROP IF EXISTS + ADD CONSTRAINT
83
+ // Gera código TypeORM seguro para cada FK no método UP
84
84
  const foreignKeysSql = relationships
85
85
  .filter((relationship) => (0, relationships_1.isSingularRelationship)(relationship.kind))
86
86
  .map((relationship) => {
87
87
  const fkName = `FK_${tableName}_${relationship.foreignKey}`;
88
88
  const refTable = targetTable(relationship.target, entities);
89
- return [
90
- ` await queryRunner.query(\`ALTER TABLE \\\`${tableName}\\\` DROP FOREIGN KEY IF EXISTS \\\`${fkName}\\\`\`);`,
91
- ` await queryRunner.query(\`ALTER TABLE \\\`${tableName}\\\` ADD CONSTRAINT \\\`${fkName}\\\` FOREIGN KEY (\\\`${relationship.foreignKey}\\\`) REFERENCES \\\`${refTable}\\\`(\\\`${relationship.valueField}\\\`) ON DELETE ${relationship.onDelete}\`);`,
92
- ].join('\n');
89
+ return ` const table_${relationship.foreignKey} = await queryRunner.getTable('${tableName}');
90
+ if (table_${relationship.foreignKey}) {
91
+ const foreignKey = table_${relationship.foreignKey}.foreignKeys.find(fk => fk.name === '${fkName}');
92
+ if (foreignKey) {
93
+ await queryRunner.dropForeignKey('${tableName}', foreignKey);
94
+ }
95
+ }
96
+ await queryRunner.createForeignKey('${tableName}', new TableForeignKey({
97
+ name: '${fkName}',
98
+ columnNames: ['${relationship.foreignKey}'],
99
+ referencedTableName: '${refTable}',
100
+ referencedColumnNames: ['${relationship.valueField}'],
101
+ onDelete: '${relationship.onDelete}'
102
+ }));`;
93
103
  })
94
104
  .join('\n');
95
105
  const dropForeignKeysSql = relationships
96
106
  .filter((relationship) => (0, relationships_1.isSingularRelationship)(relationship.kind))
97
107
  .map((relationship) => {
98
108
  const fkName = `FK_${tableName}_${relationship.foreignKey}`;
99
- return ` await queryRunner.query(\`ALTER TABLE \\\`${tableName}\\\` DROP FOREIGN KEY IF EXISTS \\\`${fkName}\\\`\`);`;
109
+ return ` const table_${relationship.foreignKey} = await queryRunner.getTable('${tableName}');
110
+ if (table_${relationship.foreignKey}) {
111
+ const foreignKey = table_${relationship.foreignKey}.foreignKeys.find(fk => fk.name === '${fkName}');
112
+ if (foreignKey) {
113
+ await queryRunner.dropForeignKey('${tableName}', foreignKey);
114
+ }
115
+ }`;
100
116
  })
101
117
  .join('\n');
102
118
  const junctions = relationships
@@ -163,7 +179,7 @@ function nestMigrationTemplate(entity, entities, timestamp, properties = entity.
163
179
  return ` await queryRunner.query('ALTER TABLE \`${tableName}\` DROP CONSTRAINT \`${checkName}\`');`;
164
180
  }).join('\n')
165
181
  : '';
166
- return `import { MigrationInterface, QueryRunner, Table, TableIndex } from 'typeorm';
182
+ return `import { MigrationInterface, QueryRunner, Table, TableForeignKey, TableIndex } from 'typeorm';
167
183
 
168
184
  export class ${className} implements MigrationInterface {
169
185
  name = '${className}';
@@ -149,9 +149,9 @@ function assertValidTypeScript(source) {
149
149
  strict_1.default.match(companyMigration, /name: 'companies'/);
150
150
  strict_1.default.doesNotMatch(companyMigration, /FK_companies_cabins/);
151
151
  strict_1.default.match(cabinMigration, /name: 'company_id', type: 'int'/);
152
- strict_1.default.match(cabinMigration, /ADD CONSTRAINT/);
152
+ strict_1.default.match(cabinMigration, /createForeignKey/);
153
153
  strict_1.default.match(cabinMigration, /FK_cabins_company_id/);
154
- strict_1.default.match(cabinMigration, /ON DELETE CASCADE/);
154
+ strict_1.default.match(cabinMigration, /onDelete: 'CASCADE'/);
155
155
  strict_1.default.match(dataSource, /migrations: \[path\.join/);
156
156
  strict_1.default.match(dataSource, /synchronize: false/);
157
157
  assertValidTypeScript(companyMigration);
@@ -8,7 +8,7 @@ import { CommonModule } from '@angular/common';
8
8
  import { HlmButton } from '@spartan-ng/helm/button';
9
9
  import { HlmInput } from '@spartan-ng/helm/input';
10
10
  import { NgIconComponent } from '@ng-icons/core';
11
- import type { ThemeMode } from '../../../core/services/theme.service';
11
+ import type { ThemeMode } from '../../../../core/services/theme.service';
12
12
 
13
13
  export interface IUserProfile {
14
14
  name: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@devstroupe/devkit-cli",
3
- "version": "1.1.10",
3
+ "version": "1.1.12",
4
4
  "description": "DevsTroupe Development Kit CLI — scaffold NestJS+Angular projects, inject Spartan UI, generate CRUDs and audit architectural governance",
5
5
  "license": "MIT",
6
6
  "keywords": [
@@ -36,7 +36,7 @@
36
36
  "dependencies": {
37
37
  "commander": "^12.0.0",
38
38
  "fs-extra": "^11.2.0",
39
- "@devstroupe/devkit-core": "1.1.10"
39
+ "@devstroupe/devkit-core": "1.1.12"
40
40
  },
41
41
  "devDependencies": {
42
42
  "@types/fs-extra": "^11.0.4",