@solidxai/core 0.1.13-beta.9 → 0.1.14-beta.0
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/CHANGELOG.md +9244 -0
- package/README.md +19 -0
- package/dist/commands/run-tests.command.d.ts +2 -0
- package/dist/commands/run-tests.command.d.ts.map +1 -1
- package/dist/commands/run-tests.command.js +20 -3
- package/dist/commands/run-tests.command.js.map +1 -1
- package/dist/config/cache.options.d.ts.map +1 -1
- package/dist/config/cache.options.js +2 -2
- package/dist/config/cache.options.js.map +1 -1
- package/dist/constants/error-messages.d.ts +7 -0
- package/dist/constants/error-messages.d.ts.map +1 -1
- package/dist/constants/error-messages.js +7 -0
- package/dist/constants/error-messages.js.map +1 -1
- package/dist/constants/media-file-types.d.ts +17 -0
- package/dist/constants/media-file-types.d.ts.map +1 -1
- package/dist/constants/media-file-types.js +129 -22
- package/dist/constants/media-file-types.js.map +1 -1
- package/dist/constants/success-messages.d.ts +3 -0
- package/dist/constants/success-messages.d.ts.map +1 -1
- package/dist/constants/success-messages.js +3 -0
- package/dist/constants/success-messages.js.map +1 -1
- package/dist/controllers/authentication.controller.d.ts +1 -1
- package/dist/controllers/authentication.controller.d.ts.map +1 -1
- package/dist/controllers/authentication.controller.js +4 -3
- package/dist/controllers/authentication.controller.js.map +1 -1
- package/dist/controllers/field-metadata.controller.d.ts +6 -2
- package/dist/controllers/field-metadata.controller.d.ts.map +1 -1
- package/dist/controllers/mpin-authentication.controller.d.ts +44 -0
- package/dist/controllers/mpin-authentication.controller.d.ts.map +1 -0
- package/dist/controllers/mpin-authentication.controller.js +102 -0
- package/dist/controllers/mpin-authentication.controller.js.map +1 -0
- package/dist/controllers/view-metadata.controller.d.ts +4 -0
- package/dist/controllers/view-metadata.controller.d.ts.map +1 -1
- package/dist/dtos/change-mpin.dto.d.ts +6 -0
- package/dist/dtos/change-mpin.dto.d.ts.map +1 -0
- package/dist/dtos/change-mpin.dto.js +39 -0
- package/dist/dtos/change-mpin.dto.js.map +1 -0
- package/dist/dtos/create-field-metadata.dto.d.ts +3 -6
- package/dist/dtos/create-field-metadata.dto.d.ts.map +1 -1
- package/dist/dtos/create-field-metadata.dto.js +15 -10
- package/dist/dtos/create-field-metadata.dto.js.map +1 -1
- package/dist/dtos/create-model-sequence.dto.d.ts +1 -1
- package/dist/dtos/create-model-sequence.dto.d.ts.map +1 -1
- package/dist/dtos/create-model-sequence.dto.js +1 -1
- package/dist/dtos/create-model-sequence.dto.js.map +1 -1
- package/dist/dtos/mpin-login.dto.d.ts +5 -0
- package/dist/dtos/mpin-login.dto.d.ts.map +1 -0
- package/dist/dtos/mpin-login.dto.js +33 -0
- package/dist/dtos/mpin-login.dto.js.map +1 -0
- package/dist/dtos/setup-mpin.dto.d.ts +7 -0
- package/dist/dtos/setup-mpin.dto.d.ts.map +1 -0
- package/dist/dtos/setup-mpin.dto.js +47 -0
- package/dist/dtos/setup-mpin.dto.js.map +1 -0
- package/dist/entities/dashboard-user-layout.entity.js +2 -2
- package/dist/entities/dashboard-user-layout.entity.js.map +1 -1
- package/dist/entities/field-metadata.entity.d.ts +1 -0
- package/dist/entities/field-metadata.entity.d.ts.map +1 -1
- package/dist/entities/field-metadata.entity.js +5 -1
- package/dist/entities/field-metadata.entity.js.map +1 -1
- package/dist/entities/user-device-credential.entity.d.ts +18 -0
- package/dist/entities/user-device-credential.entity.d.ts.map +1 -0
- package/dist/entities/user-device-credential.entity.js +91 -0
- package/dist/entities/user-device-credential.entity.js.map +1 -0
- package/dist/entities/user.entity.js +2 -2
- package/dist/entities/user.entity.js.map +1 -1
- package/dist/entities/view-metadata.entity.js.map +1 -1
- package/dist/entities/workflow-execution-artifact.entity.d.ts.map +1 -1
- package/dist/entities/workflow-execution-artifact.entity.js +4 -3
- package/dist/entities/workflow-execution-artifact.entity.js.map +1 -1
- package/dist/entities/workflow-execution-log.entity.d.ts.map +1 -1
- package/dist/entities/workflow-execution-log.entity.js +4 -3
- package/dist/entities/workflow-execution-log.entity.js.map +1 -1
- package/dist/entities/workflow-execution.entity.d.ts.map +1 -1
- package/dist/entities/workflow-execution.entity.js +3 -2
- package/dist/entities/workflow-execution.entity.js.map +1 -1
- package/dist/entities/workflow-secret.entity.d.ts.map +1 -1
- package/dist/entities/workflow-secret.entity.js +3 -2
- package/dist/entities/workflow-secret.entity.js.map +1 -1
- package/dist/entities/workflow-step-execution.entity.d.ts.map +1 -1
- package/dist/entities/workflow-step-execution.entity.js +3 -2
- package/dist/entities/workflow-step-execution.entity.js.map +1 -1
- package/dist/entities/workflow-trigger-execution.entity.d.ts.map +1 -1
- package/dist/entities/workflow-trigger-execution.entity.js +6 -5
- package/dist/entities/workflow-trigger-execution.entity.js.map +1 -1
- package/dist/filters/http-exception.filter.d.ts.map +1 -1
- package/dist/filters/http-exception.filter.js +5 -1
- package/dist/filters/http-exception.filter.js.map +1 -1
- package/dist/helpers/bootstrap.helper.d.ts +5 -2
- package/dist/helpers/bootstrap.helper.d.ts.map +1 -1
- package/dist/helpers/bootstrap.helper.js +7 -3
- package/dist/helpers/bootstrap.helper.js.map +1 -1
- package/dist/helpers/field-crud-managers/LongTextFieldCrudManager.d.ts.map +1 -1
- package/dist/helpers/field-crud-managers/LongTextFieldCrudManager.js +3 -2
- package/dist/helpers/field-crud-managers/LongTextFieldCrudManager.js.map +1 -1
- package/dist/helpers/field-crud-managers/MediaFieldCrudManager.d.ts +8 -0
- package/dist/helpers/field-crud-managers/MediaFieldCrudManager.d.ts.map +1 -1
- package/dist/helpers/field-crud-managers/MediaFieldCrudManager.js +61 -76
- package/dist/helpers/field-crud-managers/MediaFieldCrudManager.js.map +1 -1
- package/dist/helpers/field-crud-managers/ShortTextFieldCrudManager.d.ts.map +1 -1
- package/dist/helpers/field-crud-managers/ShortTextFieldCrudManager.js +3 -2
- package/dist/helpers/field-crud-managers/ShortTextFieldCrudManager.js.map +1 -1
- package/dist/helpers/field-crud-managers/textFieldCrudManagerUtils.d.ts +3 -0
- package/dist/helpers/field-crud-managers/textFieldCrudManagerUtils.d.ts.map +1 -0
- package/dist/helpers/field-crud-managers/textFieldCrudManagerUtils.js +17 -0
- package/dist/helpers/field-crud-managers/textFieldCrudManagerUtils.js.map +1 -0
- package/dist/helpers/file-download.helper.d.ts.map +1 -1
- package/dist/helpers/file-download.helper.js +6 -1
- package/dist/helpers/file-download.helper.js.map +1 -1
- package/dist/helpers/security.helper.d.ts +5 -1
- package/dist/helpers/security.helper.d.ts.map +1 -1
- package/dist/helpers/security.helper.js +15 -4
- package/dist/helpers/security.helper.js.map +1 -1
- package/dist/helpers/solid-core-error-codes-provider.service.d.ts.map +1 -1
- package/dist/helpers/solid-core-error-codes-provider.service.js +45 -0
- package/dist/helpers/solid-core-error-codes-provider.service.js.map +1 -1
- package/dist/helpers/solid-microservice-adapter.service.d.ts.map +1 -1
- package/dist/helpers/solid-microservice-adapter.service.js +1 -1
- package/dist/helpers/solid-microservice-adapter.service.js.map +1 -1
- package/dist/helpers/typeorm-db-helper.d.ts.map +1 -1
- package/dist/helpers/typeorm-db-helper.js +9 -0
- package/dist/helpers/typeorm-db-helper.js.map +1 -1
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -1
- package/dist/interfaces/active-user-data.interface.d.ts +1 -0
- package/dist/interfaces/active-user-data.interface.d.ts.map +1 -1
- package/dist/interfaces/active-user-data.interface.js.map +1 -1
- package/dist/repository/user-device-credential.repository.d.ts +12 -0
- package/dist/repository/user-device-credential.repository.d.ts.map +1 -0
- package/dist/repository/user-device-credential.repository.js +34 -0
- package/dist/repository/user-device-credential.repository.js.map +1 -0
- package/dist/seeders/module-metadata-seeder.service.d.ts.map +1 -1
- package/dist/seeders/module-metadata-seeder.service.js +7 -3
- package/dist/seeders/module-metadata-seeder.service.js.map +1 -1
- package/dist/seeders/seed-data/solid-core-metadata.json +168 -2
- package/dist/services/api-key.service.d.ts.map +1 -1
- package/dist/services/api-key.service.js +1 -1
- package/dist/services/api-key.service.js.map +1 -1
- package/dist/services/authentication.service.d.ts +5 -4
- package/dist/services/authentication.service.d.ts.map +1 -1
- package/dist/services/authentication.service.js +50 -19
- package/dist/services/authentication.service.js.map +1 -1
- package/dist/services/chatter-message.service.d.ts +5 -1
- package/dist/services/chatter-message.service.d.ts.map +1 -1
- package/dist/services/chatter-message.service.js +48 -13
- package/dist/services/chatter-message.service.js.map +1 -1
- package/dist/services/crud-helper.service.d.ts +1 -2
- package/dist/services/crud-helper.service.d.ts.map +1 -1
- package/dist/services/crud-helper.service.js +2 -16
- package/dist/services/crud-helper.service.js.map +1 -1
- package/dist/services/crud.service.d.ts.map +1 -1
- package/dist/services/crud.service.js +4 -2
- package/dist/services/crud.service.js.map +1 -1
- package/dist/services/draft-publish-helper.service.d.ts +1 -0
- package/dist/services/draft-publish-helper.service.d.ts.map +1 -1
- package/dist/services/draft-publish-helper.service.js +38 -4
- package/dist/services/draft-publish-helper.service.js.map +1 -1
- package/dist/services/field-metadata.service.d.ts +6 -2
- package/dist/services/field-metadata.service.d.ts.map +1 -1
- package/dist/services/field-metadata.service.js +5 -1
- package/dist/services/field-metadata.service.js.map +1 -1
- package/dist/services/media-storage.utils.d.ts +1 -0
- package/dist/services/media-storage.utils.d.ts.map +1 -1
- package/dist/services/media-storage.utils.js +12 -1
- package/dist/services/media-storage.utils.js.map +1 -1
- package/dist/services/media.service.d.ts.map +1 -1
- package/dist/services/media.service.js +1 -0
- package/dist/services/media.service.js.map +1 -1
- package/dist/services/module-metadata.service.d.ts.map +1 -1
- package/dist/services/module-metadata.service.js +9 -1
- package/dist/services/module-metadata.service.js.map +1 -1
- package/dist/services/mpin.service.d.ts +67 -0
- package/dist/services/mpin.service.d.ts.map +1 -0
- package/dist/services/mpin.service.js +282 -0
- package/dist/services/mpin.service.js.map +1 -0
- package/dist/services/refresh-token-ids-storage.service.d.ts +25 -6
- package/dist/services/refresh-token-ids-storage.service.d.ts.map +1 -1
- package/dist/services/refresh-token-ids-storage.service.js +80 -42
- package/dist/services/refresh-token-ids-storage.service.js.map +1 -1
- package/dist/services/setting.service.d.ts.map +1 -1
- package/dist/services/setting.service.js +6 -1
- package/dist/services/setting.service.js.map +1 -1
- package/dist/services/settings/default-settings-provider.service.d.ts +202 -6
- package/dist/services/settings/default-settings-provider.service.d.ts.map +1 -1
- package/dist/services/settings/default-settings-provider.service.js +114 -3
- package/dist/services/settings/default-settings-provider.service.js.map +1 -1
- package/dist/services/view-metadata.service.d.ts +4 -0
- package/dist/services/view-metadata.service.d.ts.map +1 -1
- package/dist/services/view-metadata.service.js +2 -0
- package/dist/services/view-metadata.service.js.map +1 -1
- package/dist/solid-core.module.d.ts +1 -3
- package/dist/solid-core.module.d.ts.map +1 -1
- package/dist/solid-core.module.js +14 -39
- package/dist/solid-core.module.js.map +1 -1
- package/dist/testing/reporter/console-reporter.d.ts +3 -0
- package/dist/testing/reporter/console-reporter.d.ts.map +1 -1
- package/dist/testing/reporter/console-reporter.js +2 -0
- package/dist/testing/reporter/console-reporter.js.map +1 -1
- package/dist/testing/reporter/progress-reporter.d.ts +64 -0
- package/dist/testing/reporter/progress-reporter.d.ts.map +1 -0
- package/dist/testing/reporter/progress-reporter.js +224 -0
- package/dist/testing/reporter/progress-reporter.js.map +1 -0
- package/dist/testing/reporter/reporter.types.d.ts +3 -0
- package/dist/testing/reporter/reporter.types.d.ts.map +1 -1
- package/dist/testing/reporter/reporter.types.js.map +1 -1
- package/dist/testing/runner/run-from-metadata.d.ts.map +1 -1
- package/dist/testing/runner/run-from-metadata.js +1 -0
- package/dist/testing/runner/run-from-metadata.js.map +1 -1
- package/dist/theme/theme-registry.d.ts +14 -0
- package/dist/theme/theme-registry.d.ts.map +1 -0
- package/dist/theme/theme-registry.js +147 -0
- package/dist/theme/theme-registry.js.map +1 -0
- package/package.json +2 -2
- package/src/commands/run-tests.command.ts +17 -3
- package/src/config/cache.options.ts +9 -1
- package/src/constants/error-messages.ts +14 -0
- package/src/constants/media-file-types.ts +182 -19
- package/src/constants/success-messages.ts +5 -0
- package/src/controllers/authentication.controller.ts +5 -2
- package/src/controllers/mpin-authentication.controller.ts +67 -0
- package/src/dtos/change-mpin.dto.ts +18 -0
- package/src/dtos/create-field-metadata.dto.ts +17 -9
- package/src/dtos/create-model-sequence.dto.ts +1 -1
- package/src/dtos/mpin-login.dto.ts +24 -0
- package/src/dtos/setup-mpin.dto.ts +51 -0
- package/src/entities/dashboard-user-layout.entity.ts +2 -2
- package/src/entities/field-metadata.entity.ts +3 -0
- package/src/entities/user-device-credential.entity.ts +130 -0
- package/src/entities/user.entity.ts +2 -2
- package/src/entities/view-metadata.entity.ts +2 -2
- package/src/entities/workflow-execution-artifact.entity.ts +4 -3
- package/src/entities/workflow-execution-log.entity.ts +4 -3
- package/src/entities/workflow-execution.entity.ts +3 -2
- package/src/entities/workflow-secret.entity.ts +3 -2
- package/src/entities/workflow-step-execution.entity.ts +3 -2
- package/src/entities/workflow-trigger-execution.entity.ts +6 -5
- package/src/filters/http-exception.filter.ts +11 -1
- package/src/helpers/bootstrap.helper.ts +43 -6
- package/src/helpers/field-crud-managers/LongTextFieldCrudManager.ts +3 -2
- package/src/helpers/field-crud-managers/MediaFieldCrudManager.ts +83 -96
- package/src/helpers/field-crud-managers/ShortTextFieldCrudManager.ts +3 -2
- package/src/helpers/field-crud-managers/textFieldCrudManagerUtils.ts +14 -0
- package/src/helpers/file-download.helper.ts +15 -1
- package/src/helpers/security.helper.ts +35 -8
- package/src/helpers/solid-core-error-codes-provider.service.ts +53 -0
- package/src/helpers/solid-microservice-adapter.service.ts +6 -1
- package/src/helpers/typeorm-db-helper.ts +12 -1
- package/src/index.ts +6 -0
- package/src/interfaces/active-user-data.interface.ts +9 -0
- package/src/repository/user-device-credential.repository.ts +17 -0
- package/src/seeders/module-metadata-seeder.service.ts +11 -3
- package/src/seeders/seed-data/solid-core-metadata.json +168 -2
- package/src/services/api-key.service.ts +13 -1
- package/src/services/authentication.service.ts +127 -36
- package/src/services/chatter-message.service.ts +78 -15
- package/src/services/crud-helper.service.ts +19 -16
- package/src/services/crud.service.ts +5 -2
- package/src/services/draft-publish-helper.service.ts +56 -4
- package/src/services/field-metadata.service.ts +6 -2
- package/src/services/media-storage.utils.ts +26 -1
- package/src/services/media.service.ts +1 -0
- package/src/services/module-metadata.service.ts +10 -1
- package/src/services/mpin.service.ts +447 -0
- package/src/services/refresh-token-ids-storage.service.ts +229 -90
- package/src/services/setting.service.ts +8 -1
- package/src/services/settings/default-settings-provider.service.ts +119 -3
- package/src/services/view-metadata.service.ts +4 -0
- package/src/solid-core.module.ts +26 -12
- package/src/testing/README.md +2 -1
- package/src/testing/reporter/console-reporter.ts +4 -0
- package/src/testing/reporter/progress-reporter.ts +251 -0
- package/src/testing/reporter/reporter.types.ts +3 -0
- package/src/testing/runner/run-from-metadata.ts +1 -0
- package/src/theme/theme-registry.ts +181 -0
- package/CLAUDE.md +0 -26
|
@@ -36,12 +36,12 @@ export class User extends CommonEntity {
|
|
|
36
36
|
@Expose()
|
|
37
37
|
username: string;
|
|
38
38
|
|
|
39
|
-
@Index(
|
|
39
|
+
@Index()
|
|
40
40
|
@Column({ type: "varchar", nullable: true })
|
|
41
41
|
@Expose()
|
|
42
42
|
email: string;
|
|
43
43
|
|
|
44
|
-
@Index(
|
|
44
|
+
@Index()
|
|
45
45
|
@Column({ type: "varchar", nullable: true })
|
|
46
46
|
@Expose()
|
|
47
47
|
mobile: string;
|
|
@@ -19,11 +19,11 @@ export class ViewMetadata extends CommonEntity {
|
|
|
19
19
|
@Column({ name: "type", type: "varchar" })
|
|
20
20
|
type: string;
|
|
21
21
|
|
|
22
|
-
//TODO: To make this truly cross db compatible, we should avoid setting a db type
|
|
22
|
+
// TODO: To make this truly cross db compatible, we should avoid setting a db type
|
|
23
23
|
@Column({ name: "context", type: "text" })
|
|
24
24
|
context: any = "{}";
|
|
25
25
|
|
|
26
|
-
//TODO: To make this truly cross db compatible, we should avoid setting a db type
|
|
26
|
+
// TODO: To make this truly cross db compatible, we should avoid setting a db type
|
|
27
27
|
@Column({ name: "layout", type: "text" })
|
|
28
28
|
layout: any;
|
|
29
29
|
|
|
@@ -2,6 +2,7 @@ import { CommonEntity } from 'src/entities/common.entity';
|
|
|
2
2
|
import { Entity, Column, Index, JoinColumn, ManyToOne } from 'typeorm';
|
|
3
3
|
import { WorkflowExecution } from './workflow-execution.entity';
|
|
4
4
|
import { WorkflowStepExecution } from './workflow-step-execution.entity'
|
|
5
|
+
import { getColumnType } from 'src/helpers/typeorm-db-helper';
|
|
5
6
|
|
|
6
7
|
@Entity('ss_workflow_execution_artifact')
|
|
7
8
|
export class WorkflowExecutionArtifact extends CommonEntity {
|
|
@@ -10,12 +11,12 @@ export class WorkflowExecutionArtifact extends CommonEntity {
|
|
|
10
11
|
artifactKey: string;
|
|
11
12
|
|
|
12
13
|
@Index()
|
|
13
|
-
@ManyToOne(() => WorkflowExecution, {
|
|
14
|
+
@ManyToOne(() => WorkflowExecution, { nullable: false })
|
|
14
15
|
@JoinColumn()
|
|
15
16
|
workflowExecution: WorkflowExecution;
|
|
16
17
|
|
|
17
18
|
@Index()
|
|
18
|
-
@ManyToOne(() => WorkflowStepExecution, {
|
|
19
|
+
@ManyToOne(() => WorkflowStepExecution, { nullable: true })
|
|
19
20
|
@JoinColumn()
|
|
20
21
|
workflowStepExecution: WorkflowStepExecution;
|
|
21
22
|
|
|
@@ -61,7 +62,7 @@ export class WorkflowExecutionArtifact extends CommonEntity {
|
|
|
61
62
|
checksum: string;
|
|
62
63
|
|
|
63
64
|
@Index()
|
|
64
|
-
@Column({
|
|
65
|
+
@Column({ nullable: true, ...getColumnType('datetime') })
|
|
65
66
|
producedAt: Date;
|
|
66
67
|
|
|
67
68
|
@Column({ type: "simple-json", nullable: true })
|
|
@@ -2,6 +2,7 @@ import { CommonEntity } from 'src/entities/common.entity';
|
|
|
2
2
|
import { Entity, Column, Index, JoinColumn, ManyToOne } from 'typeorm';
|
|
3
3
|
import { WorkflowExecution } from './workflow-execution.entity';
|
|
4
4
|
import { WorkflowStepExecution } from './workflow-step-execution.entity'
|
|
5
|
+
import { getColumnType } from 'src/helpers/typeorm-db-helper';
|
|
5
6
|
|
|
6
7
|
@Entity('ss_workflow_execution_log')
|
|
7
8
|
export class WorkflowExecutionLog extends CommonEntity {
|
|
@@ -10,12 +11,12 @@ export class WorkflowExecutionLog extends CommonEntity {
|
|
|
10
11
|
logKey: string;
|
|
11
12
|
|
|
12
13
|
@Index()
|
|
13
|
-
@ManyToOne(() => WorkflowExecution, {
|
|
14
|
+
@ManyToOne(() => WorkflowExecution, { nullable: false })
|
|
14
15
|
@JoinColumn()
|
|
15
16
|
workflowExecution: WorkflowExecution;
|
|
16
17
|
|
|
17
18
|
@Index()
|
|
18
|
-
@ManyToOne(() => WorkflowStepExecution, {
|
|
19
|
+
@ManyToOne(() => WorkflowStepExecution, { nullable: true })
|
|
19
20
|
@JoinColumn()
|
|
20
21
|
workflowStepExecution: WorkflowStepExecution;
|
|
21
22
|
|
|
@@ -47,7 +48,7 @@ export class WorkflowExecutionLog extends CommonEntity {
|
|
|
47
48
|
sequenceNumber: number;
|
|
48
49
|
|
|
49
50
|
@Index()
|
|
50
|
-
@Column({
|
|
51
|
+
@Column({ ...getColumnType('datetime') })
|
|
51
52
|
occurredAt: Date;
|
|
52
53
|
|
|
53
54
|
@Column({ type: "simple-json", nullable: true })
|
|
@@ -4,6 +4,7 @@ import { WorkflowDefinition } from './workflow-definition.entity'
|
|
|
4
4
|
import { WorkflowStepExecution } from './workflow-step-execution.entity';
|
|
5
5
|
import { WorkflowExecutionLog } from './workflow-execution-log.entity';
|
|
6
6
|
import { WorkflowExecutionArtifact } from './workflow-execution-artifact.entity';
|
|
7
|
+
import { getColumnType } from 'src/helpers/typeorm-db-helper';
|
|
7
8
|
|
|
8
9
|
@Entity('ss_workflow_execution')
|
|
9
10
|
export class WorkflowExecution extends CommonEntity {
|
|
@@ -32,11 +33,11 @@ export class WorkflowExecution extends CommonEntity {
|
|
|
32
33
|
triggerType: string = "manual";
|
|
33
34
|
|
|
34
35
|
@Index()
|
|
35
|
-
@Column({
|
|
36
|
+
@Column({ nullable: true, ...getColumnType('datetime') })
|
|
36
37
|
startedAt: Date;
|
|
37
38
|
|
|
38
39
|
@Index()
|
|
39
|
-
@Column({
|
|
40
|
+
@Column({ nullable: true, ...getColumnType('datetime') })
|
|
40
41
|
finishedAt: Date;
|
|
41
42
|
|
|
42
43
|
@Column({ type: "bigint", nullable: true })
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { CommonEntity } from 'src/entities/common.entity';
|
|
2
2
|
import { Entity, Column, Index } from 'typeorm';
|
|
3
|
+
import { getColumnType } from 'src/helpers/typeorm-db-helper';
|
|
3
4
|
|
|
4
5
|
@Entity('ss_workflow_secret')
|
|
5
6
|
export class WorkflowSecret extends CommonEntity {
|
|
@@ -25,9 +26,9 @@ export class WorkflowSecret extends CommonEntity {
|
|
|
25
26
|
@Column({ type: "varchar", default: "active" })
|
|
26
27
|
status: string = "active";
|
|
27
28
|
|
|
28
|
-
@Column({
|
|
29
|
+
@Column({ nullable: true, ...getColumnType('datetime') })
|
|
29
30
|
lastRotatedAt: Date;
|
|
30
31
|
|
|
31
|
-
@Column({
|
|
32
|
+
@Column({ nullable: true, ...getColumnType('datetime') })
|
|
32
33
|
lastAccessedAt: Date;
|
|
33
34
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { CommonEntity } from 'src/entities/common.entity';
|
|
2
2
|
import { Entity, Column, Index, JoinColumn, ManyToOne } from 'typeorm';
|
|
3
3
|
import { WorkflowExecution } from './workflow-execution.entity'
|
|
4
|
+
import { getColumnType } from 'src/helpers/typeorm-db-helper';
|
|
4
5
|
|
|
5
6
|
@Entity('ss_workflow_step_execution')
|
|
6
7
|
export class WorkflowStepExecution extends CommonEntity {
|
|
@@ -55,11 +56,11 @@ export class WorkflowStepExecution extends CommonEntity {
|
|
|
55
56
|
sequenceNumber: number;
|
|
56
57
|
|
|
57
58
|
@Index()
|
|
58
|
-
@Column({
|
|
59
|
+
@Column({ nullable: true, ...getColumnType('datetime') })
|
|
59
60
|
startedAt: Date;
|
|
60
61
|
|
|
61
62
|
@Index()
|
|
62
|
-
@Column({
|
|
63
|
+
@Column({ nullable: true, ...getColumnType('datetime') })
|
|
63
64
|
finishedAt: Date;
|
|
64
65
|
|
|
65
66
|
@Column({ type: "bigint", nullable: true })
|
|
@@ -2,6 +2,7 @@ import { CommonEntity } from 'src/entities/common.entity';
|
|
|
2
2
|
import { Entity, Column, Index, JoinColumn, ManyToOne } from 'typeorm';
|
|
3
3
|
import { WorkflowDefinition } from './workflow-definition.entity';
|
|
4
4
|
import { WorkflowExecution } from './workflow-execution.entity'
|
|
5
|
+
import { getColumnType } from 'src/helpers/typeorm-db-helper';
|
|
5
6
|
|
|
6
7
|
@Entity('ss_workflow_trigger_execution')
|
|
7
8
|
export class WorkflowTriggerExecution extends CommonEntity {
|
|
@@ -10,12 +11,12 @@ export class WorkflowTriggerExecution extends CommonEntity {
|
|
|
10
11
|
triggerExecutionKey: string;
|
|
11
12
|
|
|
12
13
|
@Index()
|
|
13
|
-
@ManyToOne(() => WorkflowDefinition, {
|
|
14
|
+
@ManyToOne(() => WorkflowDefinition, { nullable: false })
|
|
14
15
|
@JoinColumn()
|
|
15
16
|
workflowDefinition: WorkflowDefinition;
|
|
16
17
|
|
|
17
18
|
@Index()
|
|
18
|
-
@ManyToOne(() => WorkflowExecution, {
|
|
19
|
+
@ManyToOne(() => WorkflowExecution, { nullable: true })
|
|
19
20
|
@JoinColumn()
|
|
20
21
|
workflowExecution: WorkflowExecution;
|
|
21
22
|
|
|
@@ -39,15 +40,15 @@ export class WorkflowTriggerExecution extends CommonEntity {
|
|
|
39
40
|
matched: boolean = false;
|
|
40
41
|
|
|
41
42
|
@Index()
|
|
42
|
-
@Column({
|
|
43
|
+
@Column({ ...getColumnType('datetime') })
|
|
43
44
|
firedAt: Date;
|
|
44
45
|
|
|
45
46
|
@Index()
|
|
46
|
-
@Column({
|
|
47
|
+
@Column({ nullable: true, ...getColumnType('datetime') })
|
|
47
48
|
startedAt: Date;
|
|
48
49
|
|
|
49
50
|
@Index()
|
|
50
|
-
@Column({
|
|
51
|
+
@Column({ nullable: true, ...getColumnType('datetime') })
|
|
51
52
|
finishedAt: Date;
|
|
52
53
|
|
|
53
54
|
@Column({ type: "bigint", nullable: true })
|
|
@@ -30,12 +30,22 @@ export class HttpExceptionFilter implements ExceptionFilter {
|
|
|
30
30
|
const isHttp = exception instanceof HttpException;
|
|
31
31
|
const explicitStatus = isHttp ? exception.getStatus() : undefined;
|
|
32
32
|
|
|
33
|
+
// Errors from http-errors-style libraries (e.g. body-parser's PayloadTooLargeError)
|
|
34
|
+
// are plain Errors carrying their own status. Honour it rather than collapsing to 500.
|
|
35
|
+
const rawStatus = exception?.status ?? exception?.statusCode;
|
|
36
|
+
const derivedStatus =
|
|
37
|
+
typeof rawStatus === 'number' && rawStatus >= 400 && rawStatus < 600
|
|
38
|
+
? rawStatus
|
|
39
|
+
: undefined;
|
|
40
|
+
|
|
33
41
|
// Canonical code + static message
|
|
34
42
|
const code: ErrorCode = this.errorMapper.mapException(exception);
|
|
35
43
|
const defaultStatus = this.errorMapper.getHttpStatus(code);
|
|
36
44
|
const message = code === 'solidx-unknown-error' ? `${exception?.message}` : this.errorMapper.getMessage(code);
|
|
37
45
|
|
|
38
|
-
|
|
46
|
+
// derivedStatus must precede defaultStatus: getHttpStatus() returns a hard 500
|
|
47
|
+
// for unmatched codes, so it is never undefined and would always win otherwise.
|
|
48
|
+
const status = explicitStatus ?? derivedStatus ?? defaultStatus ?? 500;
|
|
39
49
|
|
|
40
50
|
// Logging
|
|
41
51
|
this.logger.error(
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { ClassSerializerInterceptor, ValidationPipe } from '@nestjs/common';
|
|
2
2
|
import { NestFactory, Reflector } from '@nestjs/core';
|
|
3
|
+
import { NestExpressApplication } from '@nestjs/platform-express';
|
|
3
4
|
import { WsAdapter } from '@nestjs/platform-ws';
|
|
4
5
|
import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger';
|
|
5
6
|
import { NextFunction, Request, Response } from 'express';
|
|
@@ -12,7 +13,12 @@ import { CommandFactory } from 'nest-commander';
|
|
|
12
13
|
import { createWinstonLoggerConfig } from '../winston.logger';
|
|
13
14
|
import { WrapResponseInterceptor } from '../interceptors/wrap-response.interceptor';
|
|
14
15
|
import { buildDefaultCorsOptions } from './cors.helper';
|
|
15
|
-
import {
|
|
16
|
+
import {
|
|
17
|
+
buildDefaultSecurityHeaderOptions,
|
|
18
|
+
buildPermissionsPolicyHeader,
|
|
19
|
+
PermissionsPolicyConfig,
|
|
20
|
+
SolidSecurityOptions,
|
|
21
|
+
} from './security.helper';
|
|
16
22
|
import { parseBooleanEnv } from './environment.helper';
|
|
17
23
|
|
|
18
24
|
// ---- Shared process handlers ----
|
|
@@ -50,8 +56,20 @@ export interface SolidBootstrapOptions {
|
|
|
50
56
|
swagger?: SolidSwaggerOptions | false;
|
|
51
57
|
/** Permissions-Policy header overrides (merged with defaults). */
|
|
52
58
|
permissionsPolicyOverrides?: Partial<PermissionsPolicyConfig>;
|
|
59
|
+
/** Security header overrides, including iframe frame-ancestor allowlists. */
|
|
60
|
+
security?: SolidSecurityOptions;
|
|
53
61
|
/** Show full NestJS init logs during bootstrap (route mapping, module deps, pollers). Defaults to false. */
|
|
54
62
|
verboseBootstrap?: boolean;
|
|
63
|
+
/**
|
|
64
|
+
* Max request body size for JSON and urlencoded payloads, as a `bytes`-compatible
|
|
65
|
+
* string (e.g. '10mb', '512kb'). Defaults to '10mb'.
|
|
66
|
+
*
|
|
67
|
+
* Does NOT apply to multipart/form-data uploads: body-parser matches on Content-Type,
|
|
68
|
+
* so multipart requests skip these parsers entirely and are handled by Multer. Raise
|
|
69
|
+
* this only for large structured JSON (e.g. /bulk arrays), bearing in mind the parsed
|
|
70
|
+
* object graph can retain several times the raw byte size.
|
|
71
|
+
*/
|
|
72
|
+
bodyLimit?: string;
|
|
55
73
|
}
|
|
56
74
|
|
|
57
75
|
/**
|
|
@@ -64,19 +82,30 @@ export interface SolidBootstrapOptions {
|
|
|
64
82
|
* // main.ts
|
|
65
83
|
* bootstrapSolidApp(() => AppModule.forRoot(), {
|
|
66
84
|
* swagger: { title: 'My API', description: 'My API description' },
|
|
85
|
+
* security: { frameAncestors: ["'self'", 'https://portal.example.com'] },
|
|
67
86
|
* });
|
|
68
87
|
*/
|
|
69
88
|
export async function bootstrapSolidApp(
|
|
70
89
|
appModuleFactory: () => Promise<any>,
|
|
71
90
|
options: SolidBootstrapOptions = {},
|
|
72
|
-
): Promise<
|
|
91
|
+
): Promise<NestExpressApplication> {
|
|
73
92
|
registerGlobalProcessHandlers();
|
|
74
93
|
|
|
75
|
-
const {
|
|
94
|
+
const {
|
|
95
|
+
globalPrefix = 'api',
|
|
96
|
+
swagger = {},
|
|
97
|
+
permissionsPolicyOverrides = {},
|
|
98
|
+
security = {},
|
|
99
|
+
verboseBootstrap = false,
|
|
100
|
+
bodyLimit = '10mb',
|
|
101
|
+
} = options;
|
|
76
102
|
|
|
77
103
|
const startTime = Date.now();
|
|
78
104
|
const appModule = await appModuleFactory();
|
|
79
|
-
const app = await NestFactory.create(appModule, {
|
|
105
|
+
const app = await NestFactory.create<NestExpressApplication>(appModule, {
|
|
106
|
+
// Nest's default parsers are registered during init() with a 100kb limit, ahead of
|
|
107
|
+
// any module middleware. Disable them so the parsers below own the limit outright.
|
|
108
|
+
bodyParser: false,
|
|
80
109
|
logger: WinstonModule.createLogger({ ...createWinstonLoggerConfig(), level: verboseBootstrap ? 'debug' : 'error' }),
|
|
81
110
|
});
|
|
82
111
|
|
|
@@ -87,7 +116,7 @@ export async function bootstrapSolidApp(
|
|
|
87
116
|
app
|
|
88
117
|
.get(WINSTON_MODULE_NEST_PROVIDER)
|
|
89
118
|
.log('API server disabled via API_ENABLED=false. Skipping HTTP listen.', 'Bootstrap');
|
|
90
|
-
return;
|
|
119
|
+
return app;
|
|
91
120
|
}
|
|
92
121
|
|
|
93
122
|
// Health check at root path
|
|
@@ -95,7 +124,7 @@ export async function bootstrapSolidApp(
|
|
|
95
124
|
server.get('/', (_req, res) => res.status(200).send('SOLID OK'));
|
|
96
125
|
|
|
97
126
|
// Security headers
|
|
98
|
-
app.use(helmet(buildDefaultSecurityHeaderOptions()));
|
|
127
|
+
app.use(helmet(buildDefaultSecurityHeaderOptions(security)));
|
|
99
128
|
|
|
100
129
|
// Nest's Swagger UI HTML injects inline styles; keep CSP strict elsewhere.
|
|
101
130
|
const isSwaggerPath = (path: string) =>
|
|
@@ -136,6 +165,12 @@ export async function bootstrapSolidApp(
|
|
|
136
165
|
next();
|
|
137
166
|
});
|
|
138
167
|
|
|
168
|
+
// Body parsers — replaces Nest's default 100kb parsers (disabled via bodyParser: false).
|
|
169
|
+
// Registered here so the middleware order Nest applied by default is preserved:
|
|
170
|
+
// helmet -> CSP -> Permissions-Policy -> qs -> body parsers -> routes.
|
|
171
|
+
app.useBodyParser('json', { limit: bodyLimit });
|
|
172
|
+
app.useBodyParser('urlencoded', { limit: bodyLimit, extended: true });
|
|
173
|
+
|
|
139
174
|
// Global ValidationPipe
|
|
140
175
|
app.useGlobalPipes(
|
|
141
176
|
new ValidationPipe({
|
|
@@ -195,6 +230,8 @@ export async function bootstrapSolidApp(
|
|
|
195
230
|
|
|
196
231
|
const elapsed = ((Date.now() - startTime) / 1000).toFixed(2);
|
|
197
232
|
process.stdout.write(`\x1b[32mServer started on port ${port} in ${elapsed}s\x1b[0m\n`);
|
|
233
|
+
|
|
234
|
+
return app;
|
|
198
235
|
}
|
|
199
236
|
|
|
200
237
|
// ---- CLI bootstrap ----
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { isEmpty, isNotEmpty, isString, matches } from "class-validator";
|
|
2
2
|
import { FieldCrudManager, ValidationError } from "src/interfaces";
|
|
3
|
+
import { normalizeTextFieldInDto, normalizeTextFieldValue } from "./textFieldCrudManagerUtils";
|
|
3
4
|
|
|
4
5
|
export interface LongTextFieldOptions {
|
|
5
6
|
regexPattern: string | undefined | null;
|
|
@@ -15,7 +16,7 @@ export class LongTextFieldCrudManager implements FieldCrudManager {
|
|
|
15
16
|
}
|
|
16
17
|
|
|
17
18
|
validate(dto: any): ValidationError[] {
|
|
18
|
-
const fieldValue: any = dto[this.options.fieldName];
|
|
19
|
+
const fieldValue: any = normalizeTextFieldValue(dto[this.options.fieldName]);
|
|
19
20
|
return this.applyValidations(fieldValue);
|
|
20
21
|
}
|
|
21
22
|
|
|
@@ -36,7 +37,7 @@ export class LongTextFieldCrudManager implements FieldCrudManager {
|
|
|
36
37
|
}
|
|
37
38
|
|
|
38
39
|
transformForCreate(dto: any): any {
|
|
39
|
-
return dto;
|
|
40
|
+
return normalizeTextFieldInDto(dto, this.options.fieldName);
|
|
40
41
|
}
|
|
41
42
|
|
|
42
43
|
// Validation to be applied
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { FieldCrudManager, ValidationError } from "src/interfaces";
|
|
2
|
-
import { EXT_TO_MEDIA_TYPE, getLowercaseFileExtension, getUploadedFileName, isDangerousMediaFile, MediaCategory } from "src/constants/media-file-types";
|
|
2
|
+
import { EXT_TO_MEDIA_TYPE, getLowercaseFileExtension, getUploadedFileName, isDangerousMediaFile, MediaCategory, MIME_TO_MEDIA_TYPE } from "src/constants/media-file-types";
|
|
3
3
|
|
|
4
4
|
export enum SolidMediaType {
|
|
5
5
|
mediaSingle = 'mediaSingle',
|
|
@@ -10,75 +10,24 @@ export interface MediaFieldOptions {
|
|
|
10
10
|
type: SolidMediaType;
|
|
11
11
|
required: boolean | undefined | null;
|
|
12
12
|
fieldName: string | undefined | null;
|
|
13
|
+
fieldLabel?: string | undefined | null;
|
|
13
14
|
mediaMaxSizeKb: number | undefined | null;
|
|
14
15
|
mediaTypes: string[];
|
|
16
|
+
mediaAllowedExtensions?: string[] | null;
|
|
15
17
|
isUpdate: boolean | undefined | null;
|
|
16
18
|
}
|
|
17
19
|
|
|
18
20
|
type MediaType = MediaCategory;
|
|
19
21
|
|
|
20
|
-
const MIME_TO_MEDIA_TYPE: Record<string, MediaType> = {
|
|
21
|
-
// Images
|
|
22
|
-
'image/png': 'image',
|
|
23
|
-
'image/jpeg': 'image',
|
|
24
|
-
'image/jpg': 'image',
|
|
25
|
-
'image/webp': 'image',
|
|
26
|
-
'image/gif': 'image',
|
|
27
|
-
'image/bmp': 'image',
|
|
28
|
-
'image/tiff': 'image',
|
|
29
|
-
'image/heic': 'image',
|
|
30
|
-
'image/heif': 'image',
|
|
31
|
-
|
|
32
|
-
// Audio
|
|
33
|
-
'audio/mpeg': 'audio', // mp3
|
|
34
|
-
'audio/mp3': 'audio',
|
|
35
|
-
'audio/wav': 'audio',
|
|
36
|
-
'audio/x-wav': 'audio',
|
|
37
|
-
'audio/webm': 'audio',
|
|
38
|
-
'audio/ogg': 'audio',
|
|
39
|
-
'audio/aac': 'audio',
|
|
40
|
-
'audio/mp4': 'audio', // m4a often shows as audio/mp4
|
|
41
|
-
'audio/x-m4a': 'audio',
|
|
42
|
-
'audio/flac': 'audio',
|
|
43
|
-
|
|
44
|
-
// Video
|
|
45
|
-
'video/mp4': 'video',
|
|
46
|
-
'video/mpeg': 'video',
|
|
47
|
-
'video/webm': 'video',
|
|
48
|
-
'video/ogg': 'video',
|
|
49
|
-
'video/quicktime': 'video', // mov
|
|
50
|
-
'video/x-msvideo': 'video', // avi
|
|
51
|
-
'video/x-matroska': 'video',// mkv
|
|
52
|
-
'video/3gpp': 'video',
|
|
53
|
-
'video/3gpp2': 'video',
|
|
54
|
-
|
|
55
|
-
// Documents / files (treat as "file")
|
|
56
|
-
'application/pdf': 'pdf',
|
|
57
|
-
'text/plain': 'file',
|
|
58
|
-
'text/markdown': 'file',
|
|
59
|
-
'application/json': 'file',
|
|
60
|
-
'text/csv': 'file',
|
|
61
|
-
|
|
62
|
-
// Office
|
|
63
|
-
'application/msword': 'file', // doc
|
|
64
|
-
'application/vnd.openxmlformats-officedocument.wordprocessingml.document': 'file', // docx
|
|
65
|
-
'application/vnd.ms-excel': 'file', // xls
|
|
66
|
-
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet': 'file', // xlsx
|
|
67
|
-
'application/vnd.ms-powerpoint': 'file', // ppt
|
|
68
|
-
'application/vnd.openxmlformats-officedocument.presentationml.presentation': 'file', // pptx,
|
|
69
|
-
|
|
70
|
-
// Archives (optional)
|
|
71
|
-
'application/zip': 'file',
|
|
72
|
-
'application/x-zip-compressed': 'file',
|
|
73
|
-
'application/x-rar-compressed': 'file',
|
|
74
|
-
'application/x-7z-compressed': 'file',
|
|
75
|
-
};
|
|
76
|
-
|
|
77
22
|
export class MediaFieldCrudManager implements FieldCrudManager {
|
|
78
23
|
|
|
79
24
|
constructor(private readonly options: MediaFieldOptions) {
|
|
80
25
|
}
|
|
81
26
|
|
|
27
|
+
private getFieldLabel(): string {
|
|
28
|
+
return this.options.fieldLabel || this.options.fieldName || 'File';
|
|
29
|
+
}
|
|
30
|
+
|
|
82
31
|
private resolveMediaType(file: Express.Multer.File): MediaType | null {
|
|
83
32
|
// Belt-and-braces: dangerous files are already rejected outright by
|
|
84
33
|
// validateDangerousFiles, but never let one resolve to a usable category.
|
|
@@ -86,24 +35,28 @@ export class MediaFieldCrudManager implements FieldCrudManager {
|
|
|
86
35
|
return null;
|
|
87
36
|
}
|
|
88
37
|
|
|
38
|
+
// Extension is authoritative: a declared mimetype is fully attacker-controlled and
|
|
39
|
+
// proved insufficient on its own (a .msi uploaded as image/png previously resolved to
|
|
40
|
+
// 'image', since the mimetype match short-circuited before the extension was ever
|
|
41
|
+
// checked). The extension must independently resolve to a known-safe category before
|
|
42
|
+
// anything is accepted - not merely "not on the denylist".
|
|
89
43
|
const ext = getLowercaseFileExtension(getUploadedFileName(file));
|
|
90
|
-
const
|
|
91
|
-
if (
|
|
92
|
-
return
|
|
44
|
+
const extType = ext ? EXT_TO_MEDIA_TYPE[ext] : undefined;
|
|
45
|
+
if (!extType) {
|
|
46
|
+
return null;
|
|
93
47
|
}
|
|
94
48
|
|
|
95
|
-
//
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
return EXT_TO_MEDIA_TYPE[ext];
|
|
49
|
+
// The mimetype may only confirm or veto what the extension already established, never
|
|
50
|
+
// grant a category on its own. A mimetype that doesn't resolve to anything (e.g.
|
|
51
|
+
// application/octet-stream) has no opinion and is treated as agreeing.
|
|
52
|
+
const mt = (file.mimetype || '').toLowerCase().trim();
|
|
53
|
+
const mimeType = MIME_TO_MEDIA_TYPE[mt]
|
|
54
|
+
?? (mt.startsWith('image/') ? 'image' : mt.startsWith('audio/') ? 'audio' : mt.startsWith('video/') ? 'video' : undefined);
|
|
55
|
+
if (mimeType && mimeType !== extType) {
|
|
56
|
+
return null;
|
|
104
57
|
}
|
|
105
58
|
|
|
106
|
-
return
|
|
59
|
+
return extType;
|
|
107
60
|
}
|
|
108
61
|
|
|
109
62
|
validate(dto: any, files: Array<Express.Multer.File>): ValidationError[] {
|
|
@@ -112,18 +65,17 @@ export class MediaFieldCrudManager implements FieldCrudManager {
|
|
|
112
65
|
}
|
|
113
66
|
|
|
114
67
|
private applyValidations(fieldFiles: Array<Express.Multer.File>): ValidationError[] {
|
|
115
|
-
//
|
|
116
|
-
//
|
|
117
|
-
|
|
118
|
-
const errors = this.validateDangerousFiles(fieldFiles);
|
|
68
|
+
// A dangerous upload is never acceptable, regardless of admin config, so this is the
|
|
69
|
+
// only check that sits outside validateCommon() and can't be skipped either way below.
|
|
70
|
+
const dangerousFileErrors = this.validateDangerousFiles(fieldFiles);
|
|
119
71
|
|
|
120
72
|
switch (this.options.type) {
|
|
121
73
|
case SolidMediaType.mediaSingle:
|
|
122
|
-
return [...
|
|
74
|
+
return [...dangerousFileErrors, ...this.validateMediaSingle(fieldFiles)];
|
|
123
75
|
case SolidMediaType.mediaMultiple:
|
|
124
|
-
return [...
|
|
76
|
+
return [...dangerousFileErrors, ...this.validateMediaMultiple(fieldFiles)];
|
|
125
77
|
default:
|
|
126
|
-
return
|
|
78
|
+
return dangerousFileErrors;
|
|
127
79
|
}
|
|
128
80
|
}
|
|
129
81
|
|
|
@@ -132,26 +84,35 @@ export class MediaFieldCrudManager implements FieldCrudManager {
|
|
|
132
84
|
.filter(fieldFile => isDangerousMediaFile(fieldFile))
|
|
133
85
|
.map(fieldFile => ({
|
|
134
86
|
field: this.options.fieldName,
|
|
135
|
-
error: `${this.
|
|
87
|
+
error: `${this.getFieldLabel()} file type not allowed. ` +
|
|
136
88
|
`${getUploadedFileName(fieldFile) || 'File'} is a potentially dangerous file type.`
|
|
137
89
|
}));
|
|
138
90
|
}
|
|
139
91
|
|
|
140
|
-
|
|
92
|
+
// Every optional, admin-configured, per-field check - no-ops when its setting is unset -
|
|
93
|
+
// shared between mediaSingle and mediaMultiple so neither can drift out of sync with it.
|
|
94
|
+
private validateCommon(fieldFiles: Array<Express.Multer.File>): ValidationError[] {
|
|
95
|
+
return [
|
|
96
|
+
...this.validateRequired(fieldFiles),
|
|
97
|
+
...this.validateSize(fieldFiles),
|
|
98
|
+
...this.validateTypes(fieldFiles),
|
|
99
|
+
...this.validateAllowedExtensions(fieldFiles),
|
|
100
|
+
];
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
private validateRequired(fieldFiles: Array<Express.Multer.File>): ValidationError[] {
|
|
141
104
|
const errors: ValidationError[] = [];
|
|
142
105
|
if (!this.options.isUpdate && this.options.required && fieldFiles.length === 0) {
|
|
143
106
|
errors.push({
|
|
144
107
|
field: this.options.fieldName,
|
|
145
|
-
error: `${this.
|
|
108
|
+
error: `${this.getFieldLabel()} is required`
|
|
146
109
|
});
|
|
147
110
|
}
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
}
|
|
154
|
-
// validate size
|
|
111
|
+
return errors;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
private validateSize(fieldFiles: Array<Express.Multer.File>): ValidationError[] {
|
|
115
|
+
const errors: ValidationError[] = [];
|
|
155
116
|
if (this.options.mediaMaxSizeKb) {
|
|
156
117
|
for (let i = 0; i < fieldFiles.length; i++) {
|
|
157
118
|
const fieldFile = fieldFiles[i];
|
|
@@ -159,12 +120,16 @@ export class MediaFieldCrudManager implements FieldCrudManager {
|
|
|
159
120
|
if (fieldFileSizeInBytes > this.options.mediaMaxSizeKb) {
|
|
160
121
|
errors.push({
|
|
161
122
|
field: this.options.fieldName,
|
|
162
|
-
error: `${this.
|
|
123
|
+
error: `${this.getFieldLabel()} with size ${fieldFileSizeInBytes} KB exceeds max size limit of ${this.options.mediaMaxSizeKb} KB`
|
|
163
124
|
});
|
|
164
125
|
}
|
|
165
126
|
}
|
|
166
127
|
}
|
|
167
|
-
|
|
128
|
+
return errors;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
private validateTypes(fieldFiles: Array<Express.Multer.File>): ValidationError[] {
|
|
132
|
+
const errors: ValidationError[] = [];
|
|
168
133
|
if (this.options.mediaTypes && this.options.mediaTypes.length > 0) {
|
|
169
134
|
const allowedFileTypes = this.options.mediaTypes as MediaType[];
|
|
170
135
|
|
|
@@ -175,28 +140,50 @@ export class MediaFieldCrudManager implements FieldCrudManager {
|
|
|
175
140
|
if (!resolvedType || !allowedFileTypes.includes(resolvedType)) {
|
|
176
141
|
errors.push({
|
|
177
142
|
field: this.options.fieldName,
|
|
178
|
-
error: `${this.
|
|
143
|
+
error: `${this.getFieldLabel()} file type not allowed. ` +
|
|
179
144
|
`Allowed: ${allowedFileTypes.join(', ')}. ` +
|
|
180
145
|
`Received mimetype: ${fieldFile.mimetype}${resolvedType ? ` (mapped to ${resolvedType})` : ''}`
|
|
181
146
|
});
|
|
182
147
|
}
|
|
183
148
|
}
|
|
184
149
|
}
|
|
185
|
-
|
|
186
150
|
return errors;
|
|
187
151
|
}
|
|
188
152
|
|
|
189
|
-
private
|
|
190
|
-
const
|
|
191
|
-
if (!
|
|
153
|
+
private validateAllowedExtensions(fieldFiles: Array<Express.Multer.File>): ValidationError[] {
|
|
154
|
+
const allowed = this.options.mediaAllowedExtensions;
|
|
155
|
+
if (!allowed || allowed.length === 0) {
|
|
156
|
+
return [];
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
const normalizedAllowed = new Set(allowed.map(ext => ext.toLowerCase().replace(/^\./, '')));
|
|
160
|
+
return fieldFiles
|
|
161
|
+
.filter(fieldFile => {
|
|
162
|
+
const ext = getLowercaseFileExtension(getUploadedFileName(fieldFile));
|
|
163
|
+
return !ext || !normalizedAllowed.has(ext);
|
|
164
|
+
})
|
|
165
|
+
.map(fieldFile => ({
|
|
166
|
+
field: this.options.fieldName,
|
|
167
|
+
error: `${this.getFieldLabel()} file type not allowed. ` +
|
|
168
|
+
`Allowed extensions: ${[...normalizedAllowed].join(', ')}.`
|
|
169
|
+
}));
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
private validateMediaSingle(fieldFiles: Array<Express.Multer.File>): ValidationError[] {
|
|
173
|
+
const errors = this.validateCommon(fieldFiles);
|
|
174
|
+
if (fieldFiles.length > 1) {
|
|
192
175
|
errors.push({
|
|
193
176
|
field: this.options.fieldName,
|
|
194
|
-
error: `${this.
|
|
177
|
+
error: `${this.getFieldLabel()} must be a single file`
|
|
195
178
|
});
|
|
196
179
|
}
|
|
197
180
|
return errors;
|
|
198
181
|
}
|
|
199
182
|
|
|
183
|
+
private validateMediaMultiple(fieldFiles: Array<Express.Multer.File>): ValidationError[] {
|
|
184
|
+
return this.validateCommon(fieldFiles);
|
|
185
|
+
}
|
|
186
|
+
|
|
200
187
|
transformForCreate(dto: any): any {
|
|
201
188
|
return dto;
|
|
202
189
|
}
|