@solidxai/core 0.1.11-beta.1 → 0.1.11-beta.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/commands/migrate-removed-fields.command.d.ts +19 -0
- package/dist/commands/migrate-removed-fields.command.d.ts.map +1 -0
- package/dist/commands/migrate-removed-fields.command.js +77 -0
- package/dist/commands/migrate-removed-fields.command.js.map +1 -0
- package/dist/controllers/datasource-introspection.controller.d.ts +46 -1
- package/dist/controllers/datasource-introspection.controller.d.ts.map +1 -1
- package/dist/controllers/datasource-introspection.controller.js +38 -0
- package/dist/controllers/datasource-introspection.controller.js.map +1 -1
- package/dist/controllers/service.controller.d.ts +1 -0
- package/dist/controllers/service.controller.d.ts.map +1 -1
- package/dist/controllers/service.controller.js +2 -2
- package/dist/controllers/service.controller.js.map +1 -1
- package/dist/dtos/datasource-introspection-mapping.dto.d.ts +5 -0
- package/dist/dtos/datasource-introspection-mapping.dto.d.ts.map +1 -1
- package/dist/dtos/datasource-introspection-mapping.dto.js +24 -2
- package/dist/dtos/datasource-introspection-mapping.dto.js.map +1 -1
- package/dist/entities/{legacy-common-with-id.entity.d.ts → legacy-common-with-generated-id.entity.d.ts} +2 -2
- package/dist/entities/legacy-common-with-generated-id.entity.d.ts.map +1 -0
- package/dist/entities/{legacy-common-with-id.entity.js → legacy-common-with-generated-id.entity.js} +4 -4
- package/dist/entities/legacy-common-with-generated-id.entity.js.map +1 -0
- package/dist/entities/{legacy-common.entity.d.ts → legacy-common.entity-with-existing-id.entity.d.ts} +1 -1
- package/dist/entities/legacy-common.entity-with-existing-id.entity.d.ts.map +1 -0
- package/dist/entities/{legacy-common.entity.js → legacy-common.entity-with-existing-id.entity.js} +1 -1
- package/dist/entities/legacy-common.entity-with-existing-id.entity.js.map +1 -0
- package/dist/helpers/command.service.d.ts +2 -0
- package/dist/helpers/command.service.d.ts.map +1 -1
- package/dist/helpers/command.service.js +6 -2
- package/dist/helpers/command.service.js.map +1 -1
- package/dist/helpers/environment.helper.d.ts +1 -0
- package/dist/helpers/environment.helper.d.ts.map +1 -1
- package/dist/helpers/environment.helper.js +4 -0
- package/dist/helpers/environment.helper.js.map +1 -1
- package/dist/helpers/field-crud-managers/IntFieldCrudManager.d.ts.map +1 -1
- package/dist/helpers/field-crud-managers/IntFieldCrudManager.js +4 -1
- package/dist/helpers/field-crud-managers/IntFieldCrudManager.js.map +1 -1
- package/dist/helpers/model-metadata-helper.service.d.ts +2 -1
- package/dist/helpers/model-metadata-helper.service.d.ts.map +1 -1
- package/dist/helpers/model-metadata-helper.service.js +9 -5
- package/dist/helpers/model-metadata-helper.service.js.map +1 -1
- package/dist/helpers/module-metadata-helper.service.js +1 -1
- package/dist/helpers/module-metadata-helper.service.js.map +1 -1
- package/dist/helpers/nodemon-heartbeat.d.ts +5 -0
- package/dist/helpers/nodemon-heartbeat.d.ts.map +1 -0
- package/dist/helpers/nodemon-heartbeat.js +67 -0
- package/dist/helpers/nodemon-heartbeat.js.map +1 -0
- package/dist/helpers/solid-registry.d.ts +2 -1
- package/dist/helpers/solid-registry.d.ts.map +1 -1
- package/dist/helpers/solid-registry.js.map +1 -1
- package/dist/helpers/typeorm-db-helper.d.ts +1 -0
- package/dist/helpers/typeorm-db-helper.d.ts.map +1 -1
- package/dist/helpers/typeorm-db-helper.js +12 -0
- package/dist/helpers/typeorm-db-helper.js.map +1 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -2
- package/dist/index.js.map +1 -1
- package/dist/interfaces/datasource-introspection-provider.interface.d.ts +1 -0
- package/dist/interfaces/datasource-introspection-provider.interface.d.ts.map +1 -1
- package/dist/interfaces/datasource-introspection-provider.interface.js.map +1 -1
- package/dist/interfaces.d.ts +1 -0
- package/dist/interfaces.d.ts.map +1 -1
- package/dist/interfaces.js.map +1 -1
- package/dist/repository/scheduled-job.repository.d.ts +2 -2
- package/dist/repository/scheduled-job.repository.d.ts.map +1 -1
- package/dist/repository/scheduled-job.repository.js +6 -3
- package/dist/repository/scheduled-job.repository.js.map +1 -1
- package/dist/repository/security-rule.repository.d.ts +2 -2
- package/dist/repository/security-rule.repository.d.ts.map +1 -1
- package/dist/repository/security-rule.repository.js +6 -3
- package/dist/repository/security-rule.repository.js.map +1 -1
- package/dist/seeders/module-metadata-seeder.service.d.ts +11 -1
- package/dist/seeders/module-metadata-seeder.service.d.ts.map +1 -1
- package/dist/seeders/module-metadata-seeder.service.js +163 -3
- package/dist/seeders/module-metadata-seeder.service.js.map +1 -1
- package/dist/seeders/seed-data/solid-core-metadata.json +128 -34
- package/dist/services/api-key.service.d.ts.map +1 -1
- package/dist/services/api-key.service.js.map +1 -1
- package/dist/services/authentication.service.d.ts +2 -0
- package/dist/services/authentication.service.d.ts.map +1 -1
- package/dist/services/authentication.service.js +32 -11
- package/dist/services/authentication.service.js.map +1 -1
- package/dist/services/computed-fields/entity/alpha-num-external-id-computed-field-provider.d.ts.map +1 -1
- package/dist/services/computed-fields/entity/alpha-num-external-id-computed-field-provider.js +7 -5
- package/dist/services/computed-fields/entity/alpha-num-external-id-computed-field-provider.js.map +1 -1
- package/dist/services/computed-fields/entity/concat-entity-computed-field-provider.service.d.ts +4 -0
- package/dist/services/computed-fields/entity/concat-entity-computed-field-provider.service.d.ts.map +1 -1
- package/dist/services/computed-fields/entity/concat-entity-computed-field-provider.service.js +18 -1
- package/dist/services/computed-fields/entity/concat-entity-computed-field-provider.service.js.map +1 -1
- package/dist/services/crud-helper.service.d.ts.map +1 -1
- package/dist/services/crud-helper.service.js +25 -12
- package/dist/services/crud-helper.service.js.map +1 -1
- package/dist/services/crud.service.d.ts +1 -0
- package/dist/services/crud.service.d.ts.map +1 -1
- package/dist/services/crud.service.js +27 -0
- package/dist/services/crud.service.js.map +1 -1
- package/dist/services/datasource-introspection/mssql-datasource-introspection-provider.service.d.ts.map +1 -1
- package/dist/services/datasource-introspection/mssql-datasource-introspection-provider.service.js +12 -0
- package/dist/services/datasource-introspection/mssql-datasource-introspection-provider.service.js.map +1 -1
- package/dist/services/datasource-introspection/mysql-datasource-introspection-provider.service.d.ts.map +1 -1
- package/dist/services/datasource-introspection/mysql-datasource-introspection-provider.service.js +2 -0
- package/dist/services/datasource-introspection/mysql-datasource-introspection-provider.service.js.map +1 -1
- package/dist/services/datasource-introspection/postgres-datasource-introspection-provider.service.d.ts.map +1 -1
- package/dist/services/datasource-introspection/postgres-datasource-introspection-provider.service.js +28 -15
- package/dist/services/datasource-introspection/postgres-datasource-introspection-provider.service.js.map +1 -1
- package/dist/services/datasource-introspection.service.d.ts +63 -2
- package/dist/services/datasource-introspection.service.d.ts.map +1 -1
- package/dist/services/datasource-introspection.service.js +665 -188
- package/dist/services/datasource-introspection.service.js.map +1 -1
- package/dist/services/import-transaction.service.d.ts.map +1 -1
- package/dist/services/import-transaction.service.js +3 -0
- package/dist/services/import-transaction.service.js.map +1 -1
- package/dist/services/locale.service.d.ts +14 -0
- package/dist/services/locale.service.d.ts.map +1 -1
- package/dist/services/locale.service.js +83 -0
- package/dist/services/locale.service.js.map +1 -1
- package/dist/services/mediaStorageProviders/file-s3-storage-provider.js +10 -10
- package/dist/services/mediaStorageProviders/file-s3-storage-provider.js.map +1 -1
- package/dist/services/menu-item-metadata.service.d.ts.map +1 -1
- package/dist/services/menu-item-metadata.service.js +1 -0
- package/dist/services/menu-item-metadata.service.js.map +1 -1
- package/dist/services/model-metadata.service.d.ts +3 -0
- package/dist/services/model-metadata.service.d.ts.map +1 -1
- package/dist/services/model-metadata.service.js +78 -0
- package/dist/services/model-metadata.service.js.map +1 -1
- package/dist/services/module-metadata.service.d.ts.map +1 -1
- package/dist/services/module-metadata.service.js +28 -0
- package/dist/services/module-metadata.service.js.map +1 -1
- package/dist/services/removed-field-migration.service.d.ts +30 -0
- package/dist/services/removed-field-migration.service.d.ts.map +1 -0
- package/dist/services/removed-field-migration.service.js +319 -0
- package/dist/services/removed-field-migration.service.js.map +1 -0
- package/dist/services/role-metadata.service.d.ts +1 -0
- package/dist/services/role-metadata.service.d.ts.map +1 -1
- package/dist/services/role-metadata.service.js +23 -2
- package/dist/services/role-metadata.service.js.map +1 -1
- package/dist/services/settings/default-settings-provider.service.d.ts +244 -0
- package/dist/services/settings/default-settings-provider.service.d.ts.map +1 -1
- package/dist/services/settings/default-settings-provider.service.js +120 -0
- package/dist/services/settings/default-settings-provider.service.js.map +1 -1
- package/dist/services/solid-ts-morph.service.d.ts +6 -0
- package/dist/services/solid-ts-morph.service.d.ts.map +1 -1
- package/dist/services/solid-ts-morph.service.js +43 -0
- package/dist/services/solid-ts-morph.service.js.map +1 -1
- package/dist/services/view-metadata.service.d.ts.map +1 -1
- package/dist/services/view-metadata.service.js +4 -7
- package/dist/services/view-metadata.service.js.map +1 -1
- package/dist/solid-core.module.d.ts.map +1 -1
- package/dist/solid-core.module.js +4 -0
- package/dist/solid-core.module.js.map +1 -1
- package/dist/subscribers/audit.subscriber.d.ts +5 -1
- package/dist/subscribers/audit.subscriber.d.ts.map +1 -1
- package/dist/subscribers/audit.subscriber.js +76 -12
- package/dist/subscribers/audit.subscriber.js.map +1 -1
- package/dist/subscribers/computed-entity-field.subscriber.d.ts +9 -1
- package/dist/subscribers/computed-entity-field.subscriber.d.ts.map +1 -1
- package/dist/subscribers/computed-entity-field.subscriber.js +45 -11
- package/dist/subscribers/computed-entity-field.subscriber.js.map +1 -1
- package/dist/subscribers/created-by-updated-by.subscriber.d.ts.map +1 -1
- package/dist/subscribers/created-by-updated-by.subscriber.js.map +1 -1
- package/dist/subscribers/field-metadata.subscriber.d.ts.map +1 -1
- package/dist/subscribers/field-metadata.subscriber.js.map +1 -1
- package/dist/subscribers/scheduled-job.subscriber.d.ts.map +1 -1
- package/dist/subscribers/scheduled-job.subscriber.js +3 -2
- package/dist/subscribers/scheduled-job.subscriber.js.map +1 -1
- package/dist/subscribers/security-rule.subscriber.d.ts.map +1 -1
- package/dist/subscribers/security-rule.subscriber.js +5 -3
- package/dist/subscribers/security-rule.subscriber.js.map +1 -1
- package/package.json +1 -1
- package/src/commands/migrate-removed-fields.command.ts +81 -0
- package/src/controllers/datasource-introspection.controller.ts +27 -1
- package/src/controllers/service.controller.ts +3 -2
- package/src/dtos/datasource-introspection-mapping.dto.ts +15 -0
- package/src/entities/{legacy-common-with-id.entity.ts → legacy-common-with-generated-id.entity.ts} +1 -1
- package/src/helpers/command.service.ts +10 -2
- package/src/helpers/environment.helper.ts +11 -0
- package/src/helpers/field-crud-managers/IntFieldCrudManager.ts +4 -1
- package/src/helpers/model-metadata-helper.service.ts +8 -3
- package/src/helpers/module-metadata-helper.service.ts +1 -1
- package/src/helpers/nodemon-heartbeat.ts +59 -0
- package/src/helpers/solid-registry.ts +7 -1
- package/src/helpers/typeorm-db-helper.ts +15 -0
- package/src/index.ts +3 -2
- package/src/interfaces/datasource-introspection-provider.interface.ts +1 -0
- package/src/interfaces.ts +1 -0
- package/src/repository/scheduled-job.repository.ts +10 -4
- package/src/repository/security-rule.repository.ts +10 -4
- package/src/seeders/module-metadata-seeder.service.ts +192 -2
- package/src/seeders/seed-data/solid-core-metadata.json +128 -34
- package/src/services/api-key.service.ts +36 -36
- package/src/services/authentication.service.ts +46 -12
- package/src/services/computed-fields/entity/alpha-num-external-id-computed-field-provider.ts +11 -5
- package/src/services/computed-fields/entity/concat-entity-computed-field-provider.service.ts +15 -1
- package/src/services/crud-helper.service.ts +25 -12
- package/src/services/crud.service.ts +36 -11
- package/src/services/datasource-introspection/mssql-datasource-introspection-provider.service.ts +12 -0
- package/src/services/datasource-introspection/mysql-datasource-introspection-provider.service.ts +2 -0
- package/src/services/datasource-introspection/postgres-datasource-introspection-provider.service.ts +28 -15
- package/src/services/datasource-introspection.service.ts +827 -164
- package/src/services/import-transaction.service.ts +5 -0
- package/src/services/locale.service.ts +111 -3
- package/src/services/mediaStorageProviders/file-s3-storage-provider.ts +2 -2
- package/src/services/menu-item-metadata.service.ts +2 -1
- package/src/services/model-metadata.service.ts +109 -1
- package/src/services/module-metadata.service.ts +36 -0
- package/src/services/removed-field-migration.service.ts +334 -0
- package/src/services/role-metadata.service.ts +26 -2
- package/src/services/settings/default-settings-provider.service.ts +120 -0
- package/src/services/solid-ts-morph.service.ts +61 -1
- package/src/services/view-metadata.service.ts +4 -8
- package/src/solid-core.module.ts +4 -0
- package/src/subscribers/audit.subscriber.ts +101 -18
- package/src/subscribers/computed-entity-field.subscriber.ts +81 -28
- package/src/subscribers/created-by-updated-by.subscriber.ts +3 -0
- package/src/subscribers/field-metadata.subscriber.ts +3 -0
- package/src/subscribers/scheduled-job.subscriber.ts +3 -2
- package/src/subscribers/security-rule.subscriber.ts +8 -3
- package/dist/entities/legacy-common-with-id.entity.d.ts.map +0 -1
- package/dist/entities/legacy-common-with-id.entity.js.map +0 -1
- package/dist/entities/legacy-common.entity.d.ts.map +0 -1
- package/dist/entities/legacy-common.entity.js.map +0 -1
- /package/src/entities/{legacy-common.entity.ts → legacy-common.entity-with-existing-id.entity.ts} +0 -0
|
@@ -22,6 +22,10 @@ export class DatasourceIntrospectionMappingColumnDto {
|
|
|
22
22
|
@IsString()
|
|
23
23
|
fieldName: string;
|
|
24
24
|
|
|
25
|
+
@ApiProperty()
|
|
26
|
+
@IsString()
|
|
27
|
+
displayName: string;
|
|
28
|
+
|
|
25
29
|
@ApiProperty()
|
|
26
30
|
@IsString()
|
|
27
31
|
dataType: string;
|
|
@@ -91,6 +95,11 @@ export class DatasourceIntrospectionMappingColumnDto {
|
|
|
91
95
|
@ApiProperty()
|
|
92
96
|
@IsBoolean()
|
|
93
97
|
isPrimaryKey: boolean;
|
|
98
|
+
|
|
99
|
+
@ApiProperty({ required: false, type: "object", additionalProperties: true })
|
|
100
|
+
@IsOptional()
|
|
101
|
+
@IsObject()
|
|
102
|
+
fieldConfig?: Record<string, any>;
|
|
94
103
|
}
|
|
95
104
|
|
|
96
105
|
export class DatasourceIntrospectionMappingDto {
|
|
@@ -180,3 +189,9 @@ export class DatasourceIntrospectionMappingDto {
|
|
|
180
189
|
@IsObject()
|
|
181
190
|
reviewedModel?: Record<string, any>;
|
|
182
191
|
}
|
|
192
|
+
|
|
193
|
+
export class DatasourceIntrospectionRunMigrationDto {
|
|
194
|
+
@ApiProperty()
|
|
195
|
+
@IsString()
|
|
196
|
+
datasource: string;
|
|
197
|
+
}
|
package/src/entities/{legacy-common-with-id.entity.ts → legacy-common-with-generated-id.entity.ts}
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Exclude, Expose } from "class-transformer";
|
|
2
2
|
import { Column, Generated } from "typeorm";
|
|
3
|
-
import { LEGACY_TABLE_FIELDS_PREFIX, LegacyCommonEntityWithExistingId } from "./legacy-common.entity";
|
|
3
|
+
import { LEGACY_TABLE_FIELDS_PREFIX, LegacyCommonEntityWithExistingId } from "./legacy-common.entity-with-existing-id.entity";
|
|
4
4
|
|
|
5
5
|
@Exclude()
|
|
6
6
|
export abstract class LegacyCommonEntityWithGeneratedId extends LegacyCommonEntityWithExistingId {
|
|
@@ -10,6 +10,10 @@ export type CommandWithArgs = {
|
|
|
10
10
|
@Injectable()
|
|
11
11
|
export class CommandService {
|
|
12
12
|
private readonly logger = new Logger(CommandService.name);
|
|
13
|
+
private readonly ansiEscapePattern =
|
|
14
|
+
// Covers standard ANSI CSI, OSC, and related terminal escape sequences.
|
|
15
|
+
// eslint-disable-next-line no-control-regex
|
|
16
|
+
/[\u001B\u009B][[\]()#;?]*(?:(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><~]|(?:][^\u0007]*(?:\u0007|\u001B\\)))/g;
|
|
13
17
|
|
|
14
18
|
/**
|
|
15
19
|
* Escape an argument for Windows CMD shell
|
|
@@ -51,11 +55,11 @@ export class CommandService {
|
|
|
51
55
|
let stderr = '';
|
|
52
56
|
|
|
53
57
|
child.stdout.on('data', (data) => {
|
|
54
|
-
stdout += data.toString();
|
|
58
|
+
stdout += this.stripAnsi(data.toString());
|
|
55
59
|
});
|
|
56
60
|
|
|
57
61
|
child.stderr.on('data', (data) => {
|
|
58
|
-
stderr += data.toString();
|
|
62
|
+
stderr += this.stripAnsi(data.toString());
|
|
59
63
|
});
|
|
60
64
|
|
|
61
65
|
child.on('error', (error) => {
|
|
@@ -73,4 +77,8 @@ export class CommandService {
|
|
|
73
77
|
});
|
|
74
78
|
});
|
|
75
79
|
}
|
|
80
|
+
|
|
81
|
+
private stripAnsi(value: string): string {
|
|
82
|
+
return value.replace(this.ansiEscapePattern, '');
|
|
83
|
+
}
|
|
76
84
|
}
|
|
@@ -13,4 +13,15 @@ export function isRedisConfigured(configService: ConfigService): boolean {
|
|
|
13
13
|
const host = configService.get<string>('REDIS_HOST');
|
|
14
14
|
const port = configService.get<string>('REDIS_PORT');
|
|
15
15
|
return host && port && isNumber(parseInt(port));
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Returns true when the project is configured to use an embedded PGlite
|
|
20
|
+
* database with a single-connection pool. Several TypeORM subscriber and
|
|
21
|
+
* code-generation workarounds are only necessary (and only applied) under
|
|
22
|
+
* this constraint; on a regular multi-connection Postgres pool the original
|
|
23
|
+
* behaviour is preserved.
|
|
24
|
+
*/
|
|
25
|
+
export function isEmbeddedDb(): boolean {
|
|
26
|
+
return process.env.DEFAULT_DATABASE_DRIVER === 'pglite';
|
|
16
27
|
}
|
|
@@ -15,7 +15,10 @@ export class IntFieldCrudManager implements FieldCrudManager {
|
|
|
15
15
|
|
|
16
16
|
validate(createDto: any): ValidationError[] {
|
|
17
17
|
const fieldValue: any = createDto[this.options.fieldName];
|
|
18
|
-
|
|
18
|
+
if (typeof fieldValue === 'string' && /^\d+$/.test(fieldValue.trim())) {
|
|
19
|
+
createDto[this.options.fieldName] = Number.parseInt(fieldValue, 10);
|
|
20
|
+
}
|
|
21
|
+
return this.applyValidations(createDto[this.options.fieldName]);
|
|
19
22
|
}
|
|
20
23
|
|
|
21
24
|
private applyValidations(fieldValue: any): ValidationError[] {
|
|
@@ -2,10 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
import { forwardRef, Inject, Injectable, Logger } from "@nestjs/common";
|
|
4
4
|
import { _ } from "lodash";
|
|
5
|
-
import { LEGACY_TABLE_FIELDS_PREFIX } from "src/entities/legacy-common.entity";
|
|
5
|
+
import { LEGACY_TABLE_FIELDS_PREFIX } from "src/entities/legacy-common.entity-with-existing-id.entity";
|
|
6
6
|
import { LegacyTableType } from "src/enums/legacy-table-type.enum";
|
|
7
|
+
import { ModelMetadata } from "src/entities/model-metadata.entity";
|
|
7
8
|
import { ModelMetadataRepository } from "src/repository/model-metadata.repository";
|
|
8
9
|
import { SolidRegistry } from "./solid-registry";
|
|
10
|
+
import { EntityManager } from "typeorm";
|
|
9
11
|
|
|
10
12
|
@Injectable()
|
|
11
13
|
export class ModelMetadataHelperService {
|
|
@@ -143,8 +145,11 @@ export class ModelMetadataHelperService {
|
|
|
143
145
|
return systemFieldsMetadata;
|
|
144
146
|
}
|
|
145
147
|
|
|
146
|
-
async loadFieldHierarchy(modelName: any) {
|
|
147
|
-
const
|
|
148
|
+
async loadFieldHierarchy(modelName: any, manager?: EntityManager) {
|
|
149
|
+
const repo = manager
|
|
150
|
+
? manager.getRepository(ModelMetadata)
|
|
151
|
+
: this.modelMetadataRepo;
|
|
152
|
+
const model = await repo.findOne({
|
|
148
153
|
where: {
|
|
149
154
|
singularName: modelName,
|
|
150
155
|
},
|
|
@@ -34,7 +34,7 @@ export class ModuleMetadataHelperService {
|
|
|
34
34
|
if (!moduleName) {
|
|
35
35
|
return '';
|
|
36
36
|
}
|
|
37
|
-
return path.resolve(process.cwd(), 'src', moduleName);
|
|
37
|
+
return path.resolve(process.cwd(), 'src', kebabCase(moduleName));
|
|
38
38
|
}
|
|
39
39
|
|
|
40
40
|
async getSolidUiModulePath(moduleName: string): Promise<string> {
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import * as fsSync from 'fs';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* The heartbeat file is placed inside the nodemon-watched src/ directory so
|
|
6
|
+
* that touching it resets nodemon's restart delay. This prevents nodemon from
|
|
7
|
+
* restarting the API server while in-process code generation is writing files
|
|
8
|
+
* (which is especially important for embedded PGlite projects where the
|
|
9
|
+
* generate-code endpoint runs schematics in-process).
|
|
10
|
+
*
|
|
11
|
+
* The file uses a .json extension because nodemon is configured to watch
|
|
12
|
+
* ts,json files. It is placed at the src/ root (not inside any metadata
|
|
13
|
+
* directory) so it is not caught by the metadata JSON ignore rule.
|
|
14
|
+
*/
|
|
15
|
+
const HEARTBEAT_FILENAME = 'solidx-nodemon-heartbeat.json';
|
|
16
|
+
const HEARTBEAT_INTERVAL_MS = 1500;
|
|
17
|
+
|
|
18
|
+
export interface NodemonHeartbeatHandle {
|
|
19
|
+
stop: () => void;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Starts a heartbeat that touches a file in the nodemon-watched directory
|
|
24
|
+
* every ~1.5 s. Call `stop()` after the response has been flushed to allow
|
|
25
|
+
* nodemon to restart.
|
|
26
|
+
*
|
|
27
|
+
* @param srcDir Absolute path to the `src/` directory nodemon watches.
|
|
28
|
+
*/
|
|
29
|
+
export function startNodemonHeartbeat(srcDir: string): NodemonHeartbeatHandle {
|
|
30
|
+
const filePath = path.join(srcDir, HEARTBEAT_FILENAME);
|
|
31
|
+
let stopped = false;
|
|
32
|
+
|
|
33
|
+
const touch = () => {
|
|
34
|
+
try {
|
|
35
|
+
fsSync.writeFileSync(filePath, String(Date.now()));
|
|
36
|
+
} catch {
|
|
37
|
+
// best-effort — if the file can't be written, nodemon will simply
|
|
38
|
+
// restart as usual, which is the safe fallback.
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
// Write immediately so the delay resets before any schematic output lands.
|
|
43
|
+
touch();
|
|
44
|
+
|
|
45
|
+
const timer = setInterval(touch, HEARTBEAT_INTERVAL_MS);
|
|
46
|
+
|
|
47
|
+
return {
|
|
48
|
+
stop() {
|
|
49
|
+
if (stopped) return;
|
|
50
|
+
stopped = true;
|
|
51
|
+
clearInterval(timer);
|
|
52
|
+
try {
|
|
53
|
+
fsSync.unlinkSync(filePath);
|
|
54
|
+
} catch {
|
|
55
|
+
// file may not exist — ignore
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
};
|
|
59
|
+
}
|
|
@@ -8,7 +8,7 @@ import { Locale } from 'src/entities/locale.entity';
|
|
|
8
8
|
import { SecurityRule } from 'src/entities/security-rule.entity';
|
|
9
9
|
import { IScheduledJob } from 'src/services/scheduled-jobs/scheduled-job.interface';
|
|
10
10
|
import { IDashboardWidgetDataProvider, IErrorCodeProvider, ISecurityRuleConfigProvider, ISelectionProvider, ISelectionProviderContext, ISolidDatabaseModule } from "../interfaces";
|
|
11
|
-
import { ObjectLiteral } from 'typeorm';
|
|
11
|
+
import { EntityManager, ObjectLiteral } from 'typeorm';
|
|
12
12
|
|
|
13
13
|
type ControllerMetadata = {
|
|
14
14
|
name: string;
|
|
@@ -49,6 +49,12 @@ export interface TypeOrmEventContext {
|
|
|
49
49
|
metadataName?: string;
|
|
50
50
|
updatedColumns?: string[];
|
|
51
51
|
updatedRelations?: string[];
|
|
52
|
+
// Transaction-bound EntityManager from the originating TypeORM event. Providers that
|
|
53
|
+
// need to query the DB during a pre-compute hook must use this (not an injected
|
|
54
|
+
// DataSource/EntityManager) so the query runs on the active transaction's connection.
|
|
55
|
+
// On single-threaded embedded engines (PGlite), a second connection mid-transaction
|
|
56
|
+
// can deadlock.
|
|
57
|
+
manager?: EntityManager;
|
|
52
58
|
}
|
|
53
59
|
|
|
54
60
|
export interface ComputedFieldMetadata<TContext = any> {
|
|
@@ -45,6 +45,21 @@ const solidCoreDbType: DatasourceType =
|
|
|
45
45
|
? (process.env.SOLID_CORE_DB_TYPE as DatasourceType)
|
|
46
46
|
: DatasourceType.postgres;
|
|
47
47
|
|
|
48
|
+
// Must use ANSI CAST() syntax — not Postgres-specific `::text` — because TypeORM's
|
|
49
|
+
// replacePropertyNamesForTheWholeQuery regex treats ':' as part of the property name,
|
|
50
|
+
// so `alias.col::text` is parsed as property `col::text` (no match → no column substitution).
|
|
51
|
+
export function buildCastToText(driver: string, colExpr: string): string {
|
|
52
|
+
switch (driver) {
|
|
53
|
+
case DatasourceType.mssql:
|
|
54
|
+
return `CAST(${colExpr} AS NVARCHAR(MAX))`;
|
|
55
|
+
case DatasourceType.mysql:
|
|
56
|
+
case DatasourceType.mariadb:
|
|
57
|
+
return `CAST(${colExpr} AS CHAR)`;
|
|
58
|
+
default:
|
|
59
|
+
return `CAST(${colExpr} AS TEXT)`;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
48
63
|
export function getColumnType(solidType: string): ColumnOptions {
|
|
49
64
|
switch (solidType) {
|
|
50
65
|
case "longText":
|
package/src/index.ts
CHANGED
|
@@ -119,8 +119,8 @@ export * from './dtos/update-dashboard-user-layout.dto'
|
|
|
119
119
|
|
|
120
120
|
export * from './entities/action-metadata.entity'
|
|
121
121
|
export * from './entities/common.entity'
|
|
122
|
-
export * from './entities/legacy-common.entity'
|
|
123
|
-
export * from './entities/legacy-common-with-id.entity'
|
|
122
|
+
export * from './entities/legacy-common.entity-with-existing-id.entity'
|
|
123
|
+
export * from './entities/legacy-common-with-generated-id.entity'
|
|
124
124
|
export * from './entities/email-attachment.entity'
|
|
125
125
|
export * from './entities/email-template.entity'
|
|
126
126
|
export * from './entities/field-metadata.entity'
|
|
@@ -171,6 +171,7 @@ export * from './guards/permissions.guard'
|
|
|
171
171
|
export * from './guards/roles.guard'
|
|
172
172
|
|
|
173
173
|
export * from './helpers/command.service'
|
|
174
|
+
export * from './helpers/nodemon-heartbeat'
|
|
174
175
|
export * from './helpers/module.helper'
|
|
175
176
|
export * from './helpers/schematic.service'
|
|
176
177
|
export * from './helpers/solid-registry'
|
package/src/interfaces.ts
CHANGED
|
@@ -3,7 +3,7 @@ import { CreateScheduledJobDto } from "src/dtos/create-scheduled-job.dto";
|
|
|
3
3
|
import { ModuleMetadata } from "src/entities/module-metadata.entity";
|
|
4
4
|
import { ScheduledJob } from "src/entities/scheduled-job.entity";
|
|
5
5
|
import { RequestContextService } from "src/services/request-context.service";
|
|
6
|
-
import { DataSource } from "typeorm";
|
|
6
|
+
import { DataSource, EntityManager } from "typeorm";
|
|
7
7
|
import { SecurityRuleRepository } from "./security-rule.repository";
|
|
8
8
|
import { SolidBaseRepository } from "./solid-base.repository";
|
|
9
9
|
|
|
@@ -101,19 +101,25 @@ export class ScheduledJobRepository extends SolidBaseRepository<ScheduledJob> {
|
|
|
101
101
|
|
|
102
102
|
/**
|
|
103
103
|
* Converts an entity to a plain DTO object.
|
|
104
|
+
* When manager is provided (e.g. from a TypeORM subscriber event), the
|
|
105
|
+
* fallback relation load uses it so the query runs on the active
|
|
106
|
+
* transaction's connection instead of the default DataSource pool.
|
|
104
107
|
*/
|
|
105
|
-
async toDto(scheduledJob: ScheduledJob): Promise<CreateScheduledJobDto> {
|
|
108
|
+
async toDto(scheduledJob: ScheduledJob, manager?: EntityManager): Promise<CreateScheduledJobDto> {
|
|
106
109
|
let populatedScheduledJob: ScheduledJob = scheduledJob;
|
|
107
110
|
// If the scheduled job does not have the module relation loaded, load it
|
|
108
111
|
if (!scheduledJob.module) {
|
|
109
|
-
|
|
112
|
+
const repo = manager
|
|
113
|
+
? manager.getRepository(ScheduledJob)
|
|
114
|
+
: this;
|
|
115
|
+
populatedScheduledJob = await repo.findOne({
|
|
110
116
|
where: {
|
|
111
117
|
id: scheduledJob.id,
|
|
112
118
|
},
|
|
113
119
|
relations: {
|
|
114
120
|
module: true,
|
|
115
121
|
},
|
|
116
|
-
});
|
|
122
|
+
}) ?? scheduledJob;
|
|
117
123
|
}
|
|
118
124
|
|
|
119
125
|
if (!populatedScheduledJob.module) {
|
|
@@ -9,7 +9,7 @@ import { SecurityRule } from 'src/entities/security-rule.entity';
|
|
|
9
9
|
import { SolidRegistry } from 'src/helpers/solid-registry';
|
|
10
10
|
import { ActiveUserData } from 'src/interfaces/active-user-data.interface';
|
|
11
11
|
import { CrudHelperService } from 'src/services/crud-helper.service';
|
|
12
|
-
import { Brackets, DataSource, SelectQueryBuilder } from 'typeorm';
|
|
12
|
+
import { Brackets, DataSource, EntityManager, SelectQueryBuilder } from 'typeorm';
|
|
13
13
|
import { SolidBaseRepository } from './solid-base.repository';
|
|
14
14
|
|
|
15
15
|
@Injectable()
|
|
@@ -84,12 +84,18 @@ export class SecurityRuleRepository extends SolidBaseRepository<SecurityRule> {
|
|
|
84
84
|
return configString.replace('$activeUserId', activeUser.sub.toString());
|
|
85
85
|
}
|
|
86
86
|
|
|
87
|
-
async toDto(securityRule: SecurityRule): Promise<UpdateSecurityRuleDto> {
|
|
87
|
+
async toDto(securityRule: SecurityRule, manager?: EntityManager): Promise<UpdateSecurityRuleDto> {
|
|
88
88
|
// load the role and model relations for the security rule
|
|
89
89
|
let populatedSecurityRule: SecurityRule = securityRule;
|
|
90
90
|
// If the security rule does not have the role and model relations loaded, load them
|
|
91
91
|
if (!securityRule.role || !securityRule.modelMetadata) {
|
|
92
|
-
|
|
92
|
+
// When a manager is provided (e.g. from a TypeORM subscriber event),
|
|
93
|
+
// use it so the query runs on the active transaction's connection
|
|
94
|
+
// instead of trying to acquire a second connection from the pool.
|
|
95
|
+
const repo = manager
|
|
96
|
+
? manager.getRepository(SecurityRule)
|
|
97
|
+
: this;
|
|
98
|
+
populatedSecurityRule = await repo.findOne({
|
|
93
99
|
where: {
|
|
94
100
|
id: securityRule.id,
|
|
95
101
|
},
|
|
@@ -97,7 +103,7 @@ export class SecurityRuleRepository extends SolidBaseRepository<SecurityRule> {
|
|
|
97
103
|
role: true,
|
|
98
104
|
modelMetadata: true,
|
|
99
105
|
},
|
|
100
|
-
});
|
|
106
|
+
}) ?? securityRule;
|
|
101
107
|
}
|
|
102
108
|
|
|
103
109
|
return {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { forwardRef, Inject, Injectable, Logger } from '@nestjs/common';
|
|
2
2
|
import * as fs from 'fs';
|
|
3
|
+
import * as os from 'os';
|
|
3
4
|
import * as path from 'path';
|
|
4
5
|
import { v4 as uuidv4 } from 'uuid';
|
|
5
6
|
|
|
@@ -20,6 +21,7 @@ import { CreateModuleMetadataDto } from '../dtos/create-module-metadata.dto';
|
|
|
20
21
|
import { getDynamicModuleNamesBasedOnMetadata } from '../helpers/module.helper';
|
|
21
22
|
import { SolidRegistry } from '../helpers/solid-registry';
|
|
22
23
|
import { ActionMetadataService } from '../services/action-metadata.service';
|
|
24
|
+
import { ApiKeyService } from '../services/api-key.service';
|
|
23
25
|
import { FieldMetadataService } from '../services/field-metadata.service';
|
|
24
26
|
import { MediaStorageProviderMetadataService } from '../services/media-storage-provider-metadata.service';
|
|
25
27
|
import { ModelMetadataService } from '../services/model-metadata.service';
|
|
@@ -33,18 +35,22 @@ import { ActionMetadata } from '../entities/action-metadata.entity';
|
|
|
33
35
|
import { MenuItemMetadata } from '../entities/menu-item-metadata.entity';
|
|
34
36
|
import { ModuleMetadata } from '../entities/module-metadata.entity';
|
|
35
37
|
import { RoleMetadata } from '../entities/role-metadata.entity';
|
|
38
|
+
import { User } from '../entities/user.entity';
|
|
36
39
|
import { MENU_ROLE_JOIN_TABLE_NAME, MENU_ROLE_JOIN_TABLE_NAME_MENU_COL, MENU_ROLE_JOIN_TABLE_NAME_ROLE_COL } from '../dtos/create-menu-item-metadata.dto';
|
|
37
40
|
import { DEFAULT_SA_PASSWORD } from '../dtos/create-user.dto';
|
|
38
41
|
import { SignUpDto } from '../dtos/sign-up.dto';
|
|
39
42
|
import { ADMIN_ROLE_NAME, CreateRoleMetadataDto } from 'src/dtos/create-role-metadata.dto';
|
|
40
43
|
import { CreateSavedFiltersDto } from 'src/dtos/create-saved-filters.dto';
|
|
41
44
|
import { CreateScheduledJobDto } from 'src/dtos/create-scheduled-job.dto';
|
|
45
|
+
import { CreateLocaleDto } from 'src/dtos/create-locale.dto';
|
|
42
46
|
import { PermissionMetadataRepository } from 'src/repository/permission-metadata.repository';
|
|
43
47
|
import { SavedFiltersRepository } from 'src/repository/saved-filters.repository';
|
|
44
48
|
import { ScheduledJobRepository } from 'src/repository/scheduled-job.repository';
|
|
45
49
|
import { SettingRepository } from 'src/repository/setting.repository';
|
|
50
|
+
import { UserApiKeyRepository } from 'src/repository/user-api-key.repository';
|
|
46
51
|
import { CreateModelSequenceDto } from 'src/dtos/create-model-sequence.dto';
|
|
47
52
|
import { ModelSequenceRepository } from 'src/repository/model-sequence.repository';
|
|
53
|
+
import { LocaleRepository } from 'src/repository/locale.repository';
|
|
48
54
|
import { ModelSequence } from 'src/entities/model-sequence.entity';
|
|
49
55
|
import { SavedFilters } from 'src/entities/saved-filters.entity';
|
|
50
56
|
import { ScheduledJob } from 'src/entities/scheduled-job.entity';
|
|
@@ -97,11 +103,13 @@ export class ModuleMetadataSeederService {
|
|
|
97
103
|
private readonly roleService: RoleMetadataService,
|
|
98
104
|
private readonly userService: UserService,
|
|
99
105
|
private readonly authenticationService: AuthenticationService,
|
|
106
|
+
private readonly apiKeyService: ApiKeyService,
|
|
100
107
|
private readonly solidActionService: ActionMetadataService,
|
|
101
108
|
private readonly solidViewService: ViewMetadataService,
|
|
102
109
|
private readonly emailTemplateService: EmailTemplateService,
|
|
103
110
|
private readonly smsTemplateService: SmsTemplateService,
|
|
104
111
|
private readonly listOfValuesService: ListOfValuesService,
|
|
112
|
+
private readonly userApiKeyRepository: UserApiKeyRepository,
|
|
105
113
|
// @InjectRepository(PermissionMetadata)
|
|
106
114
|
// private readonly permissionRepo: Repository<PermissionMetadata>,
|
|
107
115
|
private readonly permissionRepo: PermissionMetadataRepository,
|
|
@@ -116,6 +124,7 @@ export class ModuleMetadataSeederService {
|
|
|
116
124
|
readonly savedFiltersRepo: SavedFiltersRepository,
|
|
117
125
|
readonly dataSource: DataSource,
|
|
118
126
|
readonly modelSequenceRepo: ModelSequenceRepository,
|
|
127
|
+
readonly localeRepo: LocaleRepository,
|
|
119
128
|
) { }
|
|
120
129
|
|
|
121
130
|
async seed(conf?: any) {
|
|
@@ -194,6 +203,11 @@ export class ModuleMetadataSeederService {
|
|
|
194
203
|
const moduleModelFieldCounts = await this.timeOperation('seed-module-model-fields', () => this.seedModuleModelFields(moduleMetadata), { moduleName: moduleMetadata.name, component: 'module-model-fields' });
|
|
195
204
|
console.log(`${this.formatSeedResult(moduleMetadata.name, 'Module/Model/Fields', moduleModelFieldCounts)}`);
|
|
196
205
|
|
|
206
|
+
currentStep = 'seedLocales';
|
|
207
|
+
this.logger.log(`Seeding Locales`);
|
|
208
|
+
const localeCounts = await this.seedLocales(overallMetadata);
|
|
209
|
+
console.log(`${this.formatSeedResult(moduleMetadata.name, 'Locales', localeCounts)}`);
|
|
210
|
+
|
|
197
211
|
currentStep = 'seedPermissions';
|
|
198
212
|
this.logger.log(`Seeding Permissions`);
|
|
199
213
|
const permissionCounts = await this.timeOperation('seed-permissions', () => this.seedPermissions(overallMetadata), { moduleName: moduleMetadata.name, component: 'permissions' });
|
|
@@ -466,6 +480,22 @@ export class ModuleMetadataSeederService {
|
|
|
466
480
|
return { pruned, upserted: listOfValues.length };
|
|
467
481
|
}
|
|
468
482
|
|
|
483
|
+
private async seedLocales(overallMetadata: any): Promise<{ pruned: number; upserted: number }> {
|
|
484
|
+
this.logger.debug(`[Start] Processing locales`);
|
|
485
|
+
const locales = this.getSeedArray<CreateLocaleDto>(overallMetadata?.locales);
|
|
486
|
+
const existingLocales = await this.localeRepo.find();
|
|
487
|
+
|
|
488
|
+
if (existingLocales.length > 0) {
|
|
489
|
+
this.solidRegistry.registerlocales(existingLocales);
|
|
490
|
+
this.logger.debug(`[End] Skipping locale seed because locales already exist`);
|
|
491
|
+
return { pruned: 0, upserted: 0 };
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
await this.handleSeedLocales(locales);
|
|
495
|
+
this.logger.debug(`[End] Processing locales`);
|
|
496
|
+
return { pruned: 0, upserted: locales.length };
|
|
497
|
+
}
|
|
498
|
+
|
|
469
499
|
private async setupDefaultRolesWithPermissions() {
|
|
470
500
|
this.logger.debug(`About to add all permissions to the Admin role`);
|
|
471
501
|
await this.timeOperation('role-add-all-permissions', () => this.roleService.addAllPermissionsToRole(ADMIN_ROLE_NAME), {
|
|
@@ -592,11 +622,15 @@ export class ModuleMetadataSeederService {
|
|
|
592
622
|
// OK
|
|
593
623
|
private async seedRoles(overallMetadata: any): Promise<{ pruned: number; upserted: number }> {
|
|
594
624
|
const roles = this.getSeedArray<CreateRoleMetadataDto>(overallMetadata?.roles);
|
|
595
|
-
//
|
|
625
|
+
// Preserve module linkage metadata so RoleMetadata.module is populated from the defining module.
|
|
596
626
|
await this.timeOperation('roles-create-if-not-exists', () => this.roleService.createRolesIfNotExists(
|
|
597
627
|
roles
|
|
598
628
|
.filter((role) => role?.name)
|
|
599
|
-
.map((role) => ({
|
|
629
|
+
.map((role) => ({
|
|
630
|
+
name: role.name,
|
|
631
|
+
moduleId: role.moduleId,
|
|
632
|
+
moduleUserKey: role.moduleUserKey ?? overallMetadata?.moduleMetadata?.name,
|
|
633
|
+
} as CreateRoleMetadataDto)),
|
|
600
634
|
), {
|
|
601
635
|
moduleName: overallMetadata?.moduleMetadata?.name,
|
|
602
636
|
component: 'roles',
|
|
@@ -1336,6 +1370,7 @@ export class ModuleMetadataSeederService {
|
|
|
1336
1370
|
|
|
1337
1371
|
for (let l = 0; l < users.length; l++) {
|
|
1338
1372
|
const user: SignUpDto = users[l];
|
|
1373
|
+
const isSystemAdminUser = user.username === 'sa';
|
|
1339
1374
|
let exisitingUser = await this.timeOperation('user-find-by-username', () => this.userService.findOneByUsername(user.username), {
|
|
1340
1375
|
component: 'users',
|
|
1341
1376
|
serviceCall: 'userService.findOneByUsername',
|
|
@@ -1345,14 +1380,32 @@ export class ModuleMetadataSeederService {
|
|
|
1345
1380
|
if (user.username === 'sa') {
|
|
1346
1381
|
user.password = DEFAULT_SA_PASSWORD;
|
|
1347
1382
|
}
|
|
1383
|
+
if (isSystemAdminUser) {
|
|
1384
|
+
(user as SignUpDto & { isAllowedToGenerateApiKeys?: boolean }).isAllowedToGenerateApiKeys = true;
|
|
1385
|
+
}
|
|
1348
1386
|
|
|
1349
1387
|
exisitingUser = await this.timeOperation('user-sign-up', () => this.authenticationService.signUp(user), {
|
|
1350
1388
|
component: 'users',
|
|
1351
1389
|
serviceCall: 'authenticationService.signUp',
|
|
1352
1390
|
details: `username=${user.username}`,
|
|
1353
1391
|
});
|
|
1392
|
+
if (isSystemAdminUser) {
|
|
1393
|
+
await this.timeOperation('seed-sa-api-key', async () => {
|
|
1394
|
+
const generatedApiKey = await this.apiKeyService.generate(exisitingUser.id, {
|
|
1395
|
+
name: 'Default SA API Key',
|
|
1396
|
+
});
|
|
1397
|
+
await this.writeSolidxAdminApiKeyConfig(generatedApiKey);
|
|
1398
|
+
}, {
|
|
1399
|
+
component: 'users',
|
|
1400
|
+
serviceCall: 'apiKeyService.generate',
|
|
1401
|
+
details: `username=${user.username}`,
|
|
1402
|
+
});
|
|
1403
|
+
}
|
|
1354
1404
|
this.logger.log(`Newly created user ${user.username}`);
|
|
1355
1405
|
}
|
|
1406
|
+
if (isSystemAdminUser) {
|
|
1407
|
+
await this.ensureSystemAdminApiKey(exisitingUser);
|
|
1408
|
+
}
|
|
1356
1409
|
//FIXME: Create the user roles assignment logic here.
|
|
1357
1410
|
// now add Roles to user.
|
|
1358
1411
|
// for (let m = 0; m < roles.length; m++) {
|
|
@@ -1362,6 +1415,101 @@ export class ModuleMetadataSeederService {
|
|
|
1362
1415
|
}
|
|
1363
1416
|
}
|
|
1364
1417
|
|
|
1418
|
+
private async ensureSystemAdminApiKey(existingUser: { id: number; username: string; isAllowedToGenerateApiKeys?: boolean }) {
|
|
1419
|
+
if (!existingUser?.id) {
|
|
1420
|
+
return;
|
|
1421
|
+
}
|
|
1422
|
+
|
|
1423
|
+
if (!existingUser.isAllowedToGenerateApiKeys) {
|
|
1424
|
+
await this.timeOperation('enable-sa-api-key-generation', () => this.userApiKeyRepository.manager.update(User, { id: existingUser.id }, {
|
|
1425
|
+
isAllowedToGenerateApiKeys: true,
|
|
1426
|
+
}), {
|
|
1427
|
+
component: 'users',
|
|
1428
|
+
serviceCall: 'userRepository.update',
|
|
1429
|
+
details: `username=${existingUser.username}`,
|
|
1430
|
+
});
|
|
1431
|
+
existingUser.isAllowedToGenerateApiKeys = true;
|
|
1432
|
+
}
|
|
1433
|
+
|
|
1434
|
+
const existingDefaultKey = await this.timeOperation('find-sa-default-api-key', () => this.userApiKeyRepository.findOne({
|
|
1435
|
+
where: {
|
|
1436
|
+
user: { id: existingUser.id },
|
|
1437
|
+
name: 'Default SA API Key',
|
|
1438
|
+
},
|
|
1439
|
+
relations: {
|
|
1440
|
+
user: true,
|
|
1441
|
+
},
|
|
1442
|
+
}), {
|
|
1443
|
+
component: 'users',
|
|
1444
|
+
serviceCall: 'userApiKeyRepository.findOne',
|
|
1445
|
+
details: `username=${existingUser.username}`,
|
|
1446
|
+
});
|
|
1447
|
+
|
|
1448
|
+
if (existingDefaultKey) {
|
|
1449
|
+
return;
|
|
1450
|
+
}
|
|
1451
|
+
|
|
1452
|
+
await this.timeOperation('seed-sa-api-key', async () => {
|
|
1453
|
+
const generatedApiKey = await this.apiKeyService.generate(existingUser.id, {
|
|
1454
|
+
name: 'Default SA API Key',
|
|
1455
|
+
});
|
|
1456
|
+
await this.writeSolidxAdminApiKeyConfig(generatedApiKey);
|
|
1457
|
+
}, {
|
|
1458
|
+
component: 'users',
|
|
1459
|
+
serviceCall: 'apiKeyService.generate',
|
|
1460
|
+
details: `username=${existingUser.username}`,
|
|
1461
|
+
});
|
|
1462
|
+
}
|
|
1463
|
+
|
|
1464
|
+
private async writeSolidxAdminApiKeyConfig(generatedApiKey: {
|
|
1465
|
+
apiKey: string;
|
|
1466
|
+
record: {
|
|
1467
|
+
id: number;
|
|
1468
|
+
name: string;
|
|
1469
|
+
maskedKey: string;
|
|
1470
|
+
isActive: boolean;
|
|
1471
|
+
expiresAt: Date | null;
|
|
1472
|
+
createdAt?: Date;
|
|
1473
|
+
updatedAt?: Date;
|
|
1474
|
+
};
|
|
1475
|
+
}) {
|
|
1476
|
+
const consumingProjectRoot = path.resolve(process.cwd(), '..');
|
|
1477
|
+
const consumingProjectName = path.basename(consumingProjectRoot);
|
|
1478
|
+
const solidxDir = path.join(os.homedir(), '.solidx', consumingProjectName);
|
|
1479
|
+
const configPath = path.join(solidxDir, 'mcp.json');
|
|
1480
|
+
await fs.promises.mkdir(solidxDir, { recursive: true });
|
|
1481
|
+
|
|
1482
|
+
let existingConfig: Record<string, any> = {};
|
|
1483
|
+
|
|
1484
|
+
if (fs.existsSync(configPath)) {
|
|
1485
|
+
const rawConfig = await fs.promises.readFile(configPath, 'utf8');
|
|
1486
|
+
if (rawConfig.trim()) {
|
|
1487
|
+
const parsedConfig = JSON.parse(rawConfig);
|
|
1488
|
+
if (parsedConfig && typeof parsedConfig === 'object' && !Array.isArray(parsedConfig)) {
|
|
1489
|
+
existingConfig = parsedConfig;
|
|
1490
|
+
} else {
|
|
1491
|
+
this.logger.warn(`Expected ${configPath} to contain a JSON object. Overwriting with a new object.`);
|
|
1492
|
+
}
|
|
1493
|
+
}
|
|
1494
|
+
}
|
|
1495
|
+
|
|
1496
|
+
const nextConfig = {
|
|
1497
|
+
...existingConfig,
|
|
1498
|
+
solidxAdminApiKey: {
|
|
1499
|
+
id: generatedApiKey.record.id,
|
|
1500
|
+
name: generatedApiKey.record.name,
|
|
1501
|
+
apiKey: generatedApiKey.apiKey,
|
|
1502
|
+
maskedKey: generatedApiKey.record.maskedKey,
|
|
1503
|
+
isActive: generatedApiKey.record.isActive,
|
|
1504
|
+
expiresAt: generatedApiKey.record.expiresAt,
|
|
1505
|
+
createdAt: generatedApiKey.record.createdAt,
|
|
1506
|
+
updatedAt: generatedApiKey.record.updatedAt,
|
|
1507
|
+
},
|
|
1508
|
+
};
|
|
1509
|
+
|
|
1510
|
+
await fs.promises.writeFile(configPath, `${JSON.stringify(nextConfig, null, 2)}\n`, 'utf8');
|
|
1511
|
+
}
|
|
1512
|
+
|
|
1365
1513
|
// OK
|
|
1366
1514
|
private async seedModuleModelFields(moduleMetadata: CreateModuleMetadataDto): Promise<{ pruned: number; upserted: number }> {
|
|
1367
1515
|
const fieldMetadataRepo = this.dataSource.getRepository(FieldMetadata);
|
|
@@ -1872,6 +2020,48 @@ export class ModuleMetadataSeederService {
|
|
|
1872
2020
|
}
|
|
1873
2021
|
}
|
|
1874
2022
|
|
|
2023
|
+
private async handleSeedLocales(localesDto: CreateLocaleDto[]) {
|
|
2024
|
+
if (!localesDto || localesDto.length === 0) {
|
|
2025
|
+
this.logger.debug(`No locales found to seed`);
|
|
2026
|
+
return;
|
|
2027
|
+
}
|
|
2028
|
+
|
|
2029
|
+
for (const dto of localesDto) {
|
|
2030
|
+
if (dto.isDefault) {
|
|
2031
|
+
const existingDefaultLocales = await this.localeRepo.find({
|
|
2032
|
+
where: {
|
|
2033
|
+
isDefault: true,
|
|
2034
|
+
} as any,
|
|
2035
|
+
});
|
|
2036
|
+
|
|
2037
|
+
for (const locale of existingDefaultLocales) {
|
|
2038
|
+
if (locale.locale !== dto.locale) {
|
|
2039
|
+
await this.localeRepo.save({
|
|
2040
|
+
...locale,
|
|
2041
|
+
isDefault: false,
|
|
2042
|
+
});
|
|
2043
|
+
}
|
|
2044
|
+
}
|
|
2045
|
+
}
|
|
2046
|
+
|
|
2047
|
+
const existingLocale = await this.localeRepo.findOne({
|
|
2048
|
+
where: {
|
|
2049
|
+
locale: dto.locale,
|
|
2050
|
+
} as any,
|
|
2051
|
+
});
|
|
2052
|
+
|
|
2053
|
+
await this.localeRepo.save(
|
|
2054
|
+
this.localeRepo.create({
|
|
2055
|
+
...existingLocale,
|
|
2056
|
+
...dto,
|
|
2057
|
+
})
|
|
2058
|
+
);
|
|
2059
|
+
}
|
|
2060
|
+
|
|
2061
|
+
const locales = await this.localeRepo.find();
|
|
2062
|
+
this.solidRegistry.registerlocales(locales);
|
|
2063
|
+
}
|
|
2064
|
+
|
|
1875
2065
|
private validateSavedFilterQueryJsonWrapper(dto: CreateSavedFiltersDto): void {
|
|
1876
2066
|
const filterName = dto?.name ?? '<unnamed>';
|
|
1877
2067
|
const filterQueryJson = dto?.filterQueryJson;
|