@flusys/nestjs-form-builder 8.0.2 → 8.1.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/README.md +47 -6
- package/config/message-keys.d.ts +9 -0
- package/controllers/form-email-auth.controller.d.ts +11 -0
- package/controllers/form-result.controller.d.ts +6 -3
- package/controllers/form.controller.d.ts +5 -2
- package/controllers/index.d.ts +1 -0
- package/dtos/form-email-auth.dto.d.ts +20 -0
- package/dtos/form-result.dto.d.ts +2 -0
- package/dtos/form.dto.d.ts +3 -0
- package/dtos/index.d.ts +1 -0
- package/entities/form-email-verification.entity.d.ts +11 -0
- package/entities/form-result.entity.d.ts +1 -0
- package/entities/index.d.ts +4 -2
- package/enums/form-access-type.enum.d.ts +2 -1
- package/fesm/{262.js → 432.js} +561 -37
- package/fesm/{98.js → 720.js} +364 -36
- package/fesm/config/index.js +11 -2
- package/fesm/controllers/index.js +6 -5
- package/fesm/dtos/index.js +170 -4
- package/fesm/entities/index.js +136 -6
- package/fesm/enums/index.js +1 -0
- package/fesm/index.js +66 -21
- package/fesm/interfaces/index.js +23 -2
- package/fesm/modules/index.js +22 -12
- package/fesm/services/index.js +8 -4
- package/interfaces/form-builder-email-provider.interface.d.ts +4 -0
- package/interfaces/form-builder-module.interface.d.ts +3 -1
- package/interfaces/form-result.interface.d.ts +1 -0
- package/interfaces/index.d.ts +1 -0
- package/package.json +3 -3
- package/services/form-email-auth.service.d.ts +29 -0
- package/services/form-result.service.d.ts +10 -1
- package/services/form.service.d.ts +2 -0
- package/services/index.d.ts +1 -0
package/fesm/{262.js → 432.js}
RENAMED
|
@@ -4,16 +4,17 @@ import * as __rspack_external__flusys_nestjs_shared_classes_6076a329 from "@flus
|
|
|
4
4
|
import * as __rspack_external__flusys_nestjs_shared_decorators_c129b601 from "@flusys/nestjs-shared/decorators";
|
|
5
5
|
import * as __rspack_external__flusys_nestjs_shared_dtos_c4585026 from "@flusys/nestjs-shared/dtos";
|
|
6
6
|
import * as __rspack_external__flusys_nestjs_shared_modules_24702bee from "@flusys/nestjs-shared/modules";
|
|
7
|
+
import * as __rspack_external__flusys_nestjs_shared_utils_9125c1f1 from "@flusys/nestjs-shared/utils";
|
|
7
8
|
import * as __rspack_external__nestjs_common_a04939da from "@nestjs/common";
|
|
8
9
|
import * as __rspack_external__nestjs_swagger_7cd450b5 from "@nestjs/swagger";
|
|
9
10
|
import * as __rspack_external_class_transformer_c5a479fb from "class-transformer";
|
|
10
11
|
import * as __rspack_external_class_validator_7b99ef4d from "class-validator";
|
|
11
12
|
import * as __rspack_external_typeorm from "typeorm";
|
|
12
|
-
import * as
|
|
13
|
-
import * as __rspack_external__flusys_nestjs_shared_utils_9125c1f1 from "@flusys/nestjs-shared/utils";
|
|
13
|
+
import * as __rspack_external_crypto from "crypto";
|
|
14
14
|
import * as __rspack_external__nestjs_core_d7b00a1a from "@nestjs/core";
|
|
15
|
-
|
|
16
|
-
export const
|
|
15
|
+
import * as __rspack_external__flusys_nestjs_core_ff4ca4e2 from "@flusys/nestjs-core";
|
|
16
|
+
export const __rspack_esm_id = 432;
|
|
17
|
+
export const __rspack_esm_ids = [432];
|
|
17
18
|
export const __webpack_modules__ = {
|
|
18
19
|
769(__unused_rspack_module, __webpack_exports__, __webpack_require__) {
|
|
19
20
|
/**
|
|
@@ -58,7 +59,15 @@ const FORM_MESSAGES = {
|
|
|
58
59
|
INVALID_ACCESS_TYPE: 'form.invalid.access.type',
|
|
59
60
|
PERMISSION_CHECK_FAILED: 'form.permission.check.failed',
|
|
60
61
|
GET_SUCCESS: 'form.get.success',
|
|
61
|
-
GET_ACCESS_INFO_SUCCESS: 'form.get.access.info.success'
|
|
62
|
+
GET_ACCESS_INFO_SUCCESS: 'form.get.access.info.success',
|
|
63
|
+
EMAIL_NOT_CONFIGURED: 'form.email.not.configured',
|
|
64
|
+
EMAIL_VERIFICATION_REQUIRED: 'form.email.verification.required',
|
|
65
|
+
OTP_SENT: 'form.email.otp.sent',
|
|
66
|
+
OTP_SEND_FAILED: 'form.email.otp.send.failed',
|
|
67
|
+
OTP_INVALID: 'form.email.otp.invalid',
|
|
68
|
+
OTP_EXPIRED: 'form.email.otp.expired',
|
|
69
|
+
OTP_TOO_MANY_ATTEMPTS: 'form.email.otp.too.many.attempts',
|
|
70
|
+
OTP_VERIFIED: 'form.email.otp.verified'
|
|
62
71
|
};
|
|
63
72
|
const FORM_RESULT_MESSAGES = {
|
|
64
73
|
GET_ALL_SUCCESS: 'form.result.get.all.success',
|
|
@@ -67,7 +76,8 @@ const FORM_RESULT_MESSAGES = {
|
|
|
67
76
|
HAS_NOT_SUBMITTED_SUCCESS: 'form.result.has.not.submitted.success',
|
|
68
77
|
SUBMIT_SUCCESS: 'form.result.submit.success',
|
|
69
78
|
DRAFT_GET_SUCCESS: 'form.result.draft.get.success',
|
|
70
|
-
DRAFT_UPDATE_SUCCESS: 'form.result.draft.update.success'
|
|
79
|
+
DRAFT_UPDATE_SUCCESS: 'form.result.draft.update.success',
|
|
80
|
+
ALREADY_SUBMITTED: 'form.result.already.submitted'
|
|
71
81
|
};
|
|
72
82
|
|
|
73
83
|
__webpack_require__.d(__webpack_exports__, {
|
|
@@ -109,6 +119,7 @@ class SubmitFormDto {
|
|
|
109
119
|
formId;
|
|
110
120
|
data;
|
|
111
121
|
isDraft;
|
|
122
|
+
accessToken;
|
|
112
123
|
}
|
|
113
124
|
_ts_decorate([
|
|
114
125
|
(0,_nestjs_swagger__rspack_import_1.ApiProperty)({
|
|
@@ -141,6 +152,15 @@ _ts_decorate([
|
|
|
141
152
|
(0,class_validator__rspack_import_3.IsOptional)(),
|
|
142
153
|
_ts_metadata("design:type", Boolean)
|
|
143
154
|
], SubmitFormDto.prototype, "isDraft", void 0);
|
|
155
|
+
_ts_decorate([
|
|
156
|
+
(0,_nestjs_swagger__rspack_import_1.ApiProperty)({
|
|
157
|
+
description: 'Access token from email verification (only used for EMAIL_VERIFIED forms submitted anonymously)',
|
|
158
|
+
required: false
|
|
159
|
+
}),
|
|
160
|
+
(0,class_validator__rspack_import_3.IsString)(),
|
|
161
|
+
(0,class_validator__rspack_import_3.IsOptional)(),
|
|
162
|
+
_ts_metadata("design:type", String)
|
|
163
|
+
], SubmitFormDto.prototype, "accessToken", void 0);
|
|
144
164
|
class CreateFormResultDto extends SubmitFormDto {
|
|
145
165
|
schemaVersionSnapshot;
|
|
146
166
|
schemaVersion;
|
|
@@ -250,6 +270,7 @@ class FormResultResponseDto extends _flusys_nestjs_shared_dtos__rspack_import_0.
|
|
|
250
270
|
schemaVersion;
|
|
251
271
|
data;
|
|
252
272
|
submittedById;
|
|
273
|
+
submitterEmail;
|
|
253
274
|
submittedAt;
|
|
254
275
|
isDraft;
|
|
255
276
|
}
|
|
@@ -278,6 +299,11 @@ _ts_decorate([
|
|
|
278
299
|
(0,_nestjs_swagger__rspack_import_1.ApiProperty)(),
|
|
279
300
|
_ts_metadata("design:type", Object)
|
|
280
301
|
], FormResultResponseDto.prototype, "submittedById", void 0);
|
|
302
|
+
_ts_decorate([
|
|
303
|
+
(0,class_transformer__rspack_import_2.Expose)(),
|
|
304
|
+
(0,_nestjs_swagger__rspack_import_1.ApiProperty)(),
|
|
305
|
+
_ts_metadata("design:type", Object)
|
|
306
|
+
], FormResultResponseDto.prototype, "submitterEmail", void 0);
|
|
281
307
|
_ts_decorate([
|
|
282
308
|
(0,class_transformer__rspack_import_2.Expose)(),
|
|
283
309
|
(0,_nestjs_swagger__rspack_import_1.ApiProperty)(),
|
|
@@ -301,7 +327,7 @@ __webpack_require__.d(__webpack_exports__, {
|
|
|
301
327
|
|
|
302
328
|
|
|
303
329
|
},
|
|
304
|
-
|
|
330
|
+
814(__unused_rspack_module, __webpack_exports__, __webpack_require__) {
|
|
305
331
|
|
|
306
332
|
// EXPORTS
|
|
307
333
|
__webpack_require__.d(__webpack_exports__, {
|
|
@@ -309,6 +335,7 @@ __webpack_require__.d(__webpack_exports__, {
|
|
|
309
335
|
C7: () => (/* reexport */ Form),
|
|
310
336
|
Xl: () => (/* binding */ FormCompanyEntities),
|
|
311
337
|
t$: () => (/* binding */ FormCoreEntities),
|
|
338
|
+
Kr: () => (/* reexport */ FormEmailVerification),
|
|
312
339
|
ez: () => (/* reexport */ FormResult),
|
|
313
340
|
Wl: () => (/* reexport */ FormWithCompany),
|
|
314
341
|
getFormBuilderEntitiesByConfig: () => (/* binding */ getFormBuilderEntitiesByConfig)
|
|
@@ -470,6 +497,7 @@ class FormResult extends Identity {
|
|
|
470
497
|
schemaVersion;
|
|
471
498
|
data;
|
|
472
499
|
submittedById;
|
|
500
|
+
submitterEmail;
|
|
473
501
|
submittedAt;
|
|
474
502
|
isDraft;
|
|
475
503
|
}
|
|
@@ -513,6 +541,15 @@ form_result_entity_ts_decorate([
|
|
|
513
541
|
}),
|
|
514
542
|
form_result_entity_ts_metadata("design:type", Object)
|
|
515
543
|
], FormResult.prototype, "submittedById", void 0);
|
|
544
|
+
form_result_entity_ts_decorate([
|
|
545
|
+
(0,external_typeorm_.Column)({
|
|
546
|
+
type: 'varchar',
|
|
547
|
+
length: 255,
|
|
548
|
+
nullable: true,
|
|
549
|
+
name: 'submitter_email'
|
|
550
|
+
}),
|
|
551
|
+
form_result_entity_ts_metadata("design:type", Object)
|
|
552
|
+
], FormResult.prototype, "submitterEmail", void 0);
|
|
516
553
|
form_result_entity_ts_decorate([
|
|
517
554
|
(0,external_typeorm_.Column)({
|
|
518
555
|
type: 'timestamp',
|
|
@@ -532,9 +569,122 @@ form_result_entity_ts_decorate([
|
|
|
532
569
|
FormResult = form_result_entity_ts_decorate([
|
|
533
570
|
(0,external_typeorm_.Entity)({
|
|
534
571
|
name: 'form_result'
|
|
535
|
-
})
|
|
572
|
+
}),
|
|
573
|
+
(0,external_typeorm_.Index)([
|
|
574
|
+
'formId',
|
|
575
|
+
'submitterEmail'
|
|
576
|
+
])
|
|
536
577
|
], FormResult);
|
|
537
578
|
|
|
579
|
+
;// CONCATENATED MODULE: ./projects/nestjs-form-builder/src/entities/form-email-verification.entity.ts
|
|
580
|
+
function form_email_verification_entity_ts_decorate(decorators, target, key, desc) {
|
|
581
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
582
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") {
|
|
583
|
+
r = Reflect.decorate(decorators, target, key, desc);
|
|
584
|
+
} else {
|
|
585
|
+
for(var i = decorators.length - 1; i >= 0; i--){
|
|
586
|
+
if (d = decorators[i]) {
|
|
587
|
+
r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
592
|
+
}
|
|
593
|
+
function form_email_verification_entity_ts_metadata(metadataKey, metadataValue) {
|
|
594
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") {
|
|
595
|
+
return Reflect.metadata(metadataKey, metadataValue);
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
|
|
600
|
+
class FormEmailVerification extends Identity {
|
|
601
|
+
formId;
|
|
602
|
+
email;
|
|
603
|
+
otpHash;
|
|
604
|
+
otpExpiresAt;
|
|
605
|
+
otpAttempts;
|
|
606
|
+
verifiedAt;
|
|
607
|
+
accessTokenHash;
|
|
608
|
+
accessTokenExpiresAt;
|
|
609
|
+
}
|
|
610
|
+
form_email_verification_entity_ts_decorate([
|
|
611
|
+
(0,external_typeorm_.Column)({
|
|
612
|
+
type: 'uuid',
|
|
613
|
+
nullable: false,
|
|
614
|
+
name: 'form_id'
|
|
615
|
+
}),
|
|
616
|
+
form_email_verification_entity_ts_metadata("design:type", String)
|
|
617
|
+
], FormEmailVerification.prototype, "formId", void 0);
|
|
618
|
+
form_email_verification_entity_ts_decorate([
|
|
619
|
+
(0,external_typeorm_.Column)({
|
|
620
|
+
type: 'varchar',
|
|
621
|
+
length: 255,
|
|
622
|
+
nullable: false
|
|
623
|
+
}),
|
|
624
|
+
form_email_verification_entity_ts_metadata("design:type", String)
|
|
625
|
+
], FormEmailVerification.prototype, "email", void 0);
|
|
626
|
+
form_email_verification_entity_ts_decorate([
|
|
627
|
+
(0,external_typeorm_.Column)({
|
|
628
|
+
type: 'varchar',
|
|
629
|
+
length: 64,
|
|
630
|
+
nullable: true,
|
|
631
|
+
name: 'otp_hash'
|
|
632
|
+
}),
|
|
633
|
+
form_email_verification_entity_ts_metadata("design:type", Object)
|
|
634
|
+
], FormEmailVerification.prototype, "otpHash", void 0);
|
|
635
|
+
form_email_verification_entity_ts_decorate([
|
|
636
|
+
(0,external_typeorm_.Column)({
|
|
637
|
+
type: 'timestamp',
|
|
638
|
+
nullable: true,
|
|
639
|
+
name: 'otp_expires_at'
|
|
640
|
+
}),
|
|
641
|
+
form_email_verification_entity_ts_metadata("design:type", Object)
|
|
642
|
+
], FormEmailVerification.prototype, "otpExpiresAt", void 0);
|
|
643
|
+
form_email_verification_entity_ts_decorate([
|
|
644
|
+
(0,external_typeorm_.Column)({
|
|
645
|
+
type: 'int',
|
|
646
|
+
nullable: false,
|
|
647
|
+
default: 0,
|
|
648
|
+
name: 'otp_attempts'
|
|
649
|
+
}),
|
|
650
|
+
form_email_verification_entity_ts_metadata("design:type", Number)
|
|
651
|
+
], FormEmailVerification.prototype, "otpAttempts", void 0);
|
|
652
|
+
form_email_verification_entity_ts_decorate([
|
|
653
|
+
(0,external_typeorm_.Column)({
|
|
654
|
+
type: 'timestamp',
|
|
655
|
+
nullable: true,
|
|
656
|
+
name: 'verified_at'
|
|
657
|
+
}),
|
|
658
|
+
form_email_verification_entity_ts_metadata("design:type", Object)
|
|
659
|
+
], FormEmailVerification.prototype, "verifiedAt", void 0);
|
|
660
|
+
form_email_verification_entity_ts_decorate([
|
|
661
|
+
(0,external_typeorm_.Index)(),
|
|
662
|
+
(0,external_typeorm_.Column)({
|
|
663
|
+
type: 'varchar',
|
|
664
|
+
length: 64,
|
|
665
|
+
nullable: true,
|
|
666
|
+
name: 'access_token_hash'
|
|
667
|
+
}),
|
|
668
|
+
form_email_verification_entity_ts_metadata("design:type", Object)
|
|
669
|
+
], FormEmailVerification.prototype, "accessTokenHash", void 0);
|
|
670
|
+
form_email_verification_entity_ts_decorate([
|
|
671
|
+
(0,external_typeorm_.Column)({
|
|
672
|
+
type: 'timestamp',
|
|
673
|
+
nullable: true,
|
|
674
|
+
name: 'access_token_expires_at'
|
|
675
|
+
}),
|
|
676
|
+
form_email_verification_entity_ts_metadata("design:type", Object)
|
|
677
|
+
], FormEmailVerification.prototype, "accessTokenExpiresAt", void 0);
|
|
678
|
+
FormEmailVerification = form_email_verification_entity_ts_decorate([
|
|
679
|
+
(0,external_typeorm_.Entity)({
|
|
680
|
+
name: 'form_email_verification'
|
|
681
|
+
}),
|
|
682
|
+
(0,external_typeorm_.Unique)([
|
|
683
|
+
'formId',
|
|
684
|
+
'email'
|
|
685
|
+
])
|
|
686
|
+
], FormEmailVerification);
|
|
687
|
+
|
|
538
688
|
;// CONCATENATED MODULE: ./projects/nestjs-form-builder/src/entities/form-with-company.entity.ts
|
|
539
689
|
function form_with_company_entity_ts_decorate(decorators, target, key, desc) {
|
|
540
690
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
@@ -590,20 +740,24 @@ FormWithCompany = form_with_company_entity_ts_decorate([
|
|
|
590
740
|
// Entities without company feature
|
|
591
741
|
|
|
592
742
|
|
|
743
|
+
|
|
593
744
|
// Entities with company feature
|
|
594
745
|
|
|
595
746
|
|
|
596
747
|
|
|
597
748
|
|
|
749
|
+
|
|
598
750
|
// Core entities (no company feature)
|
|
599
751
|
const FormCoreEntities = [
|
|
600
752
|
Form,
|
|
601
|
-
FormResult
|
|
753
|
+
FormResult,
|
|
754
|
+
FormEmailVerification
|
|
602
755
|
];
|
|
603
756
|
// Company-specific entities
|
|
604
757
|
const FormCompanyEntities = [
|
|
605
758
|
FormWithCompany,
|
|
606
|
-
FormResult
|
|
759
|
+
FormResult,
|
|
760
|
+
FormEmailVerification
|
|
607
761
|
];
|
|
608
762
|
function getFormBuilderEntitiesByConfig(enableCompanyFeature) {
|
|
609
763
|
return enableCompanyFeature ? FormCompanyEntities : FormCoreEntities;
|
|
@@ -621,6 +775,7 @@ function getFormBuilderEntitiesByConfig(enableCompanyFeature) {
|
|
|
621
775
|
/** No authentication required - anyone can submit */ FormAccessType["PUBLIC"] = "public";
|
|
622
776
|
/** Any authenticated user can submit */ FormAccessType["AUTHENTICATED"] = "authenticated";
|
|
623
777
|
/** Only users with specific action permissions can submit */ FormAccessType["ACTION_GROUP"] = "action_group";
|
|
778
|
+
/** Anonymous visitors verify a one-time email code before submitting (already-logged-in users are auto-identified by their account email) */ FormAccessType["EMAIL_VERIFIED"] = "email_verified";
|
|
624
779
|
return FormAccessType;
|
|
625
780
|
}({});
|
|
626
781
|
|
|
@@ -629,6 +784,20 @@ __webpack_require__.d(__webpack_exports__, {
|
|
|
629
784
|
});
|
|
630
785
|
|
|
631
786
|
|
|
787
|
+
},
|
|
788
|
+
341(__unused_rspack_module, __webpack_exports__, __webpack_require__) {
|
|
789
|
+
/**
|
|
790
|
+
* Provider Interface for sending form-submission verification emails.
|
|
791
|
+
* Implemented at the app level (composition root) using the email module,
|
|
792
|
+
* so nestjs-form-builder never depends on nestjs-email directly.
|
|
793
|
+
*/ const FORM_BUILDER_EMAIL_PROVIDER = 'FORM_BUILDER_EMAIL_PROVIDER';
|
|
794
|
+
|
|
795
|
+
__webpack_require__.d(__webpack_exports__, {
|
|
796
|
+
}, {
|
|
797
|
+
Q: FORM_BUILDER_EMAIL_PROVIDER
|
|
798
|
+
});
|
|
799
|
+
|
|
800
|
+
|
|
632
801
|
},
|
|
633
802
|
781(__unused_rspack_module, __webpack_exports__, __webpack_require__) {
|
|
634
803
|
/* import */ var _nestjs_common__rspack_import_0 = __webpack_require__(157);
|
|
@@ -758,7 +927,7 @@ class FormBuilderDataSourceProvider extends modules_.MultiTenantDataSourceServic
|
|
|
758
927
|
}
|
|
759
928
|
async getFormBuilderEntities(enableCompanyFeature) {
|
|
760
929
|
const enable = enableCompanyFeature ?? this.configService.isCompanyFeatureEnabled();
|
|
761
|
-
const { getFormBuilderEntitiesByConfig } = await Promise.resolve(/* import() */).then(__webpack_require__.bind(__webpack_require__,
|
|
930
|
+
const { getFormBuilderEntitiesByConfig } = await Promise.resolve(/* import() */).then(__webpack_require__.bind(__webpack_require__, 814));
|
|
762
931
|
return getFormBuilderEntitiesByConfig(enable);
|
|
763
932
|
}
|
|
764
933
|
async createDataSourceFromConfig(config) {
|
|
@@ -782,6 +951,219 @@ FormBuilderDataSourceProvider = _ts_decorate([
|
|
|
782
951
|
], FormBuilderDataSourceProvider);
|
|
783
952
|
|
|
784
953
|
|
|
954
|
+
},
|
|
955
|
+
474(__unused_rspack_module, __webpack_exports__, __webpack_require__) {
|
|
956
|
+
|
|
957
|
+
// EXPORTS
|
|
958
|
+
__webpack_require__.d(__webpack_exports__, {
|
|
959
|
+
w: () => (/* binding */ FormEmailAuthService)
|
|
960
|
+
});
|
|
961
|
+
|
|
962
|
+
// EXTERNAL MODULE: external "@flusys/nestjs-shared/utils"
|
|
963
|
+
var utils_ = __webpack_require__(216);
|
|
964
|
+
// EXTERNAL MODULE: external "@nestjs/common"
|
|
965
|
+
var common_ = __webpack_require__(157);
|
|
966
|
+
;// CONCATENATED MODULE: external "crypto"
|
|
967
|
+
|
|
968
|
+
// EXTERNAL MODULE: ./projects/nestjs-form-builder/src/config/message-keys.ts
|
|
969
|
+
var message_keys = __webpack_require__(137);
|
|
970
|
+
// EXTERNAL MODULE: ./projects/nestjs-form-builder/src/entities/index.ts + 6 modules
|
|
971
|
+
var entities = __webpack_require__(814);
|
|
972
|
+
// EXTERNAL MODULE: ./projects/nestjs-form-builder/src/interfaces/form-builder-email-provider.interface.ts
|
|
973
|
+
var form_builder_email_provider_interface = __webpack_require__(341);
|
|
974
|
+
// EXTERNAL MODULE: ./projects/nestjs-form-builder/src/services/form-builder-datasource.provider.ts + 1 modules
|
|
975
|
+
var form_builder_datasource_provider = __webpack_require__(593);
|
|
976
|
+
;// CONCATENATED MODULE: ./projects/nestjs-form-builder/src/services/form-email-auth.service.ts
|
|
977
|
+
function _ts_decorate(decorators, target, key, desc) {
|
|
978
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
979
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") {
|
|
980
|
+
r = Reflect.decorate(decorators, target, key, desc);
|
|
981
|
+
} else {
|
|
982
|
+
for(var i = decorators.length - 1; i >= 0; i--){
|
|
983
|
+
if (d = decorators[i]) {
|
|
984
|
+
r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
985
|
+
}
|
|
986
|
+
}
|
|
987
|
+
}
|
|
988
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
989
|
+
}
|
|
990
|
+
function _ts_metadata(metadataKey, metadataValue) {
|
|
991
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") {
|
|
992
|
+
return Reflect.metadata(metadataKey, metadataValue);
|
|
993
|
+
}
|
|
994
|
+
}
|
|
995
|
+
function _ts_param(paramIndex, decorator) {
|
|
996
|
+
return function(target, key) {
|
|
997
|
+
decorator(target, key, paramIndex);
|
|
998
|
+
};
|
|
999
|
+
}
|
|
1000
|
+
|
|
1001
|
+
|
|
1002
|
+
|
|
1003
|
+
|
|
1004
|
+
|
|
1005
|
+
|
|
1006
|
+
|
|
1007
|
+
class FormEmailAuthService {
|
|
1008
|
+
dataSourceProvider;
|
|
1009
|
+
emailProvider;
|
|
1010
|
+
OTP_EXPIRY_MS = 10 * 60 * 1000;
|
|
1011
|
+
ACCESS_TOKEN_EXPIRY_MS = 30 * 24 * 60 * 60 * 1000;
|
|
1012
|
+
MAX_OTP_ATTEMPTS = 5;
|
|
1013
|
+
repository = null;
|
|
1014
|
+
constructor(dataSourceProvider, emailProvider){
|
|
1015
|
+
this.dataSourceProvider = dataSourceProvider;
|
|
1016
|
+
this.emailProvider = emailProvider;
|
|
1017
|
+
}
|
|
1018
|
+
async ensureRepository() {
|
|
1019
|
+
if (!this.repository) {
|
|
1020
|
+
this.repository = await this.dataSourceProvider.getRepository(entities/* .FormEmailVerification */.Kr);
|
|
1021
|
+
}
|
|
1022
|
+
return this.repository;
|
|
1023
|
+
}
|
|
1024
|
+
generateOtp() {
|
|
1025
|
+
return __rspack_external_crypto.randomInt(100000, 1000000).toString();
|
|
1026
|
+
}
|
|
1027
|
+
generateAccessToken() {
|
|
1028
|
+
return (0,utils_.generateSecureToken)();
|
|
1029
|
+
}
|
|
1030
|
+
hashValue(value) {
|
|
1031
|
+
return (0,utils_.hashToken)(value);
|
|
1032
|
+
}
|
|
1033
|
+
normalizeEmail(email) {
|
|
1034
|
+
return email.trim().toLowerCase();
|
|
1035
|
+
}
|
|
1036
|
+
assertEmailEnabled() {
|
|
1037
|
+
if (!this.emailProvider) {
|
|
1038
|
+
throw new common_.BadRequestException({
|
|
1039
|
+
message: 'Email service is not configured for this form',
|
|
1040
|
+
messageKey: message_keys/* .FORM_MESSAGES.EMAIL_NOT_CONFIGURED */.Q.EMAIL_NOT_CONFIGURED
|
|
1041
|
+
});
|
|
1042
|
+
}
|
|
1043
|
+
}
|
|
1044
|
+
async requestOtp(formId, email, formTitle) {
|
|
1045
|
+
this.assertEmailEnabled();
|
|
1046
|
+
const repository = await this.ensureRepository();
|
|
1047
|
+
email = this.normalizeEmail(email);
|
|
1048
|
+
const otp = this.generateOtp();
|
|
1049
|
+
const record = await repository.findOne({
|
|
1050
|
+
where: {
|
|
1051
|
+
formId,
|
|
1052
|
+
email
|
|
1053
|
+
}
|
|
1054
|
+
}) ?? repository.create({
|
|
1055
|
+
formId,
|
|
1056
|
+
email
|
|
1057
|
+
});
|
|
1058
|
+
Object.assign(record, {
|
|
1059
|
+
otpHash: this.hashValue(otp),
|
|
1060
|
+
otpExpiresAt: new Date(Date.now() + this.OTP_EXPIRY_MS),
|
|
1061
|
+
otpAttempts: 0
|
|
1062
|
+
});
|
|
1063
|
+
await repository.save(record);
|
|
1064
|
+
try {
|
|
1065
|
+
await this.emailProvider.sendOtpEmail(email, otp, formTitle, this.OTP_EXPIRY_MS / 60000);
|
|
1066
|
+
} catch {
|
|
1067
|
+
throw new common_.BadRequestException({
|
|
1068
|
+
message: 'Failed to send verification code. Please try again.',
|
|
1069
|
+
messageKey: message_keys/* .FORM_MESSAGES.OTP_SEND_FAILED */.Q.OTP_SEND_FAILED
|
|
1070
|
+
});
|
|
1071
|
+
}
|
|
1072
|
+
return {
|
|
1073
|
+
success: true,
|
|
1074
|
+
message: 'Verification code sent to your email',
|
|
1075
|
+
messageKey: message_keys/* .FORM_MESSAGES.OTP_SENT */.Q.OTP_SENT
|
|
1076
|
+
};
|
|
1077
|
+
}
|
|
1078
|
+
async verifyOtp(formId, email, code) {
|
|
1079
|
+
const repository = await this.ensureRepository();
|
|
1080
|
+
email = this.normalizeEmail(email);
|
|
1081
|
+
const record = await repository.findOne({
|
|
1082
|
+
where: {
|
|
1083
|
+
formId,
|
|
1084
|
+
email
|
|
1085
|
+
}
|
|
1086
|
+
});
|
|
1087
|
+
if (!record?.otpHash || !record.otpExpiresAt) {
|
|
1088
|
+
throw new common_.BadRequestException({
|
|
1089
|
+
message: 'Invalid or expired verification code',
|
|
1090
|
+
messageKey: message_keys/* .FORM_MESSAGES.OTP_INVALID */.Q.OTP_INVALID
|
|
1091
|
+
});
|
|
1092
|
+
}
|
|
1093
|
+
if (record.otpAttempts >= this.MAX_OTP_ATTEMPTS) {
|
|
1094
|
+
throw new common_.BadRequestException({
|
|
1095
|
+
message: 'Too many incorrect attempts. Please request a new code.',
|
|
1096
|
+
messageKey: message_keys/* .FORM_MESSAGES.OTP_TOO_MANY_ATTEMPTS */.Q.OTP_TOO_MANY_ATTEMPTS
|
|
1097
|
+
});
|
|
1098
|
+
}
|
|
1099
|
+
if ((0,utils_.isExpired)(record.otpExpiresAt)) {
|
|
1100
|
+
throw new common_.BadRequestException({
|
|
1101
|
+
message: 'Verification code has expired. Please request a new one.',
|
|
1102
|
+
messageKey: message_keys/* .FORM_MESSAGES.OTP_EXPIRED */.Q.OTP_EXPIRED
|
|
1103
|
+
});
|
|
1104
|
+
}
|
|
1105
|
+
if (record.otpHash !== this.hashValue(code)) {
|
|
1106
|
+
record.otpAttempts += 1;
|
|
1107
|
+
await repository.save(record);
|
|
1108
|
+
throw new common_.BadRequestException({
|
|
1109
|
+
message: 'Invalid verification code',
|
|
1110
|
+
messageKey: message_keys/* .FORM_MESSAGES.OTP_INVALID */.Q.OTP_INVALID
|
|
1111
|
+
});
|
|
1112
|
+
}
|
|
1113
|
+
const accessToken = this.generateAccessToken();
|
|
1114
|
+
const accessTokenExpiresAt = new Date(Date.now() + this.ACCESS_TOKEN_EXPIRY_MS);
|
|
1115
|
+
Object.assign(record, {
|
|
1116
|
+
verifiedAt: record.verifiedAt ?? new Date(),
|
|
1117
|
+
accessTokenHash: this.hashValue(accessToken),
|
|
1118
|
+
accessTokenExpiresAt,
|
|
1119
|
+
otpHash: null,
|
|
1120
|
+
otpExpiresAt: null,
|
|
1121
|
+
otpAttempts: 0
|
|
1122
|
+
});
|
|
1123
|
+
await repository.save(record);
|
|
1124
|
+
return {
|
|
1125
|
+
accessToken,
|
|
1126
|
+
expiresAt: accessTokenExpiresAt
|
|
1127
|
+
};
|
|
1128
|
+
}
|
|
1129
|
+
// Slides the token's expiry forward on every use so an active repeat-filler
|
|
1130
|
+
// on a 'multiple' response-mode form never needs to re-verify.
|
|
1131
|
+
async resolveAccessToken(formId, accessToken) {
|
|
1132
|
+
if (!accessToken) return null;
|
|
1133
|
+
const repository = await this.ensureRepository();
|
|
1134
|
+
const record = await repository.findOne({
|
|
1135
|
+
where: {
|
|
1136
|
+
formId,
|
|
1137
|
+
accessTokenHash: this.hashValue(accessToken)
|
|
1138
|
+
}
|
|
1139
|
+
});
|
|
1140
|
+
if (!record?.accessTokenExpiresAt || (0,utils_.isExpired)(record.accessTokenExpiresAt)) {
|
|
1141
|
+
return null;
|
|
1142
|
+
}
|
|
1143
|
+
record.accessTokenExpiresAt = new Date(Date.now() + this.ACCESS_TOKEN_EXPIRY_MS);
|
|
1144
|
+
await repository.save(record);
|
|
1145
|
+
return record.email;
|
|
1146
|
+
}
|
|
1147
|
+
async resolveSubmitterEmail(formId, user, accessToken) {
|
|
1148
|
+
if (user?.email) return this.normalizeEmail(user.email);
|
|
1149
|
+
return this.resolveAccessToken(formId, accessToken);
|
|
1150
|
+
}
|
|
1151
|
+
}
|
|
1152
|
+
FormEmailAuthService = _ts_decorate([
|
|
1153
|
+
(0,common_.Injectable)({
|
|
1154
|
+
scope: common_.Scope.REQUEST
|
|
1155
|
+
}),
|
|
1156
|
+
_ts_param(0, (0,common_.Inject)(form_builder_datasource_provider/* .FormBuilderDataSourceProvider */._)),
|
|
1157
|
+
_ts_param(1, (0,common_.Optional)()),
|
|
1158
|
+
_ts_param(1, (0,common_.Inject)(form_builder_email_provider_interface/* .FORM_BUILDER_EMAIL_PROVIDER */.Q)),
|
|
1159
|
+
_ts_metadata("design:type", Function),
|
|
1160
|
+
_ts_metadata("design:paramtypes", [
|
|
1161
|
+
typeof form_builder_datasource_provider/* .FormBuilderDataSourceProvider */._ === "undefined" ? Object : form_builder_datasource_provider/* .FormBuilderDataSourceProvider */._,
|
|
1162
|
+
typeof IFormBuilderEmailProvider === "undefined" ? Object : IFormBuilderEmailProvider
|
|
1163
|
+
])
|
|
1164
|
+
], FormEmailAuthService);
|
|
1165
|
+
|
|
1166
|
+
|
|
785
1167
|
},
|
|
786
1168
|
160(__unused_rspack_module, __webpack_exports__, __webpack_require__) {
|
|
787
1169
|
/* import */ var _flusys_nestjs_shared_classes__rspack_import_0 = __webpack_require__(937);
|
|
@@ -789,15 +1171,16 @@ FormBuilderDataSourceProvider = _ts_decorate([
|
|
|
789
1171
|
/* import */ var _flusys_nestjs_shared_modules__rspack_import_2 = __webpack_require__(236);
|
|
790
1172
|
/* import */ var _nestjs_common__rspack_import_3 = __webpack_require__(157);
|
|
791
1173
|
/* import */ var typeorm__rspack_import_4 = __webpack_require__(158);
|
|
792
|
-
/* import */ var
|
|
793
|
-
/* import */ var
|
|
1174
|
+
/* import */ var _config_index_js__rspack_import_11 = __webpack_require__(769);
|
|
1175
|
+
/* import */ var _config_index_js__rspack_import_12 = __webpack_require__(137);
|
|
794
1176
|
/* import */ var _dtos_form_result_dto_js__rspack_import_5 = __webpack_require__(788);
|
|
795
|
-
/* import */ var _entities_index_js__rspack_import_6 = __webpack_require__(
|
|
796
|
-
/* import */ var
|
|
797
|
-
/* import */ var
|
|
1177
|
+
/* import */ var _entities_index_js__rspack_import_6 = __webpack_require__(814);
|
|
1178
|
+
/* import */ var _enums_form_access_type_enum_js__rspack_import_13 = __webpack_require__(376);
|
|
1179
|
+
/* import */ var _utils_computed_field_utils_js__rspack_import_14 = __webpack_require__(801);
|
|
798
1180
|
/* import */ var _utils_permission_utils_js__rspack_import_7 = __webpack_require__(988);
|
|
799
1181
|
/* import */ var _form_builder_config_service_js__rspack_import_8 = __webpack_require__(781);
|
|
800
1182
|
/* import */ var _form_builder_datasource_provider_js__rspack_import_9 = __webpack_require__(593);
|
|
1183
|
+
/* import */ var _form_email_auth_service_js__rspack_import_10 = __webpack_require__(474);
|
|
801
1184
|
function _ts_decorate(decorators, target, key, desc) {
|
|
802
1185
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
803
1186
|
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") {
|
|
@@ -834,14 +1217,22 @@ function _ts_param(paramIndex, decorator) {
|
|
|
834
1217
|
|
|
835
1218
|
|
|
836
1219
|
|
|
1220
|
+
|
|
837
1221
|
class FormResultService extends _flusys_nestjs_shared_classes__rspack_import_0.ApiService {
|
|
838
1222
|
cacheManager;
|
|
839
1223
|
utilsService;
|
|
840
1224
|
formBuilderConfig;
|
|
841
1225
|
dataSourceProvider;
|
|
1226
|
+
formEmailAuthService;
|
|
842
1227
|
formRepository = null;
|
|
843
|
-
|
|
844
|
-
|
|
1228
|
+
// Serializes the single-response check-then-insert per (formId, email) within
|
|
1229
|
+
// this process, so a double-click/two-tab submission can't both pass the check
|
|
1230
|
+
// before either has saved. Static because the service is REQUEST-scoped - a
|
|
1231
|
+
// per-instance lock would never be shared between the two racing requests.
|
|
1232
|
+
// Does not span multiple app instances; see withSubmissionLock().
|
|
1233
|
+
static submissionLocks = new Map();
|
|
1234
|
+
constructor(cacheManager, utilsService, formBuilderConfig, dataSourceProvider, formEmailAuthService){
|
|
1235
|
+
super(_config_index_js__rspack_import_11/* .FORM_BUILDER_EVENT_ENTITIES.FORM_RESULT */.eF.FORM_RESULT, cacheManager, utilsService, FormResultService.name, true, _config_index_js__rspack_import_11/* .FORM_BUILDER_EVENT_MODULE */.T$, dataSourceProvider, _entities_index_js__rspack_import_6/* .FormResult */.ez), this.cacheManager = cacheManager, this.utilsService = utilsService, this.formBuilderConfig = formBuilderConfig, this.dataSourceProvider = dataSourceProvider, this.formEmailAuthService = formEmailAuthService;
|
|
845
1236
|
}
|
|
846
1237
|
async ensureFormRepositoryInitialized() {
|
|
847
1238
|
if (!this.formRepository) {
|
|
@@ -850,6 +1241,23 @@ class FormResultService extends _flusys_nestjs_shared_classes__rspack_import_0.A
|
|
|
850
1241
|
this.formRepository = await this.dataSourceProvider.getRepository(formEntity);
|
|
851
1242
|
}
|
|
852
1243
|
}
|
|
1244
|
+
async withSubmissionLock(key, fn) {
|
|
1245
|
+
const prior = FormResultService.submissionLocks.get(key) ?? Promise.resolve();
|
|
1246
|
+
let release;
|
|
1247
|
+
const current = new Promise((resolve)=>{
|
|
1248
|
+
release = resolve;
|
|
1249
|
+
});
|
|
1250
|
+
FormResultService.submissionLocks.set(key, current);
|
|
1251
|
+
await prior;
|
|
1252
|
+
try {
|
|
1253
|
+
return await fn();
|
|
1254
|
+
} finally{
|
|
1255
|
+
release();
|
|
1256
|
+
if (FormResultService.submissionLocks.get(key) === current) {
|
|
1257
|
+
FormResultService.submissionLocks.delete(key);
|
|
1258
|
+
}
|
|
1259
|
+
}
|
|
1260
|
+
}
|
|
853
1261
|
async getActiveForm(formId) {
|
|
854
1262
|
await this.ensureFormRepositoryInitialized();
|
|
855
1263
|
const form = await this.formRepository.findOne({
|
|
@@ -862,7 +1270,7 @@ class FormResultService extends _flusys_nestjs_shared_classes__rspack_import_0.A
|
|
|
862
1270
|
if (!form) {
|
|
863
1271
|
throw new _nestjs_common__rspack_import_3.NotFoundException({
|
|
864
1272
|
message: 'Form not found or inactive',
|
|
865
|
-
messageKey:
|
|
1273
|
+
messageKey: _config_index_js__rspack_import_12/* .FORM_MESSAGES.NOT_FOUND */.Q.NOT_FOUND
|
|
866
1274
|
});
|
|
867
1275
|
}
|
|
868
1276
|
return form;
|
|
@@ -874,35 +1282,85 @@ class FormResultService extends _flusys_nestjs_shared_classes__rspack_import_0.A
|
|
|
874
1282
|
});
|
|
875
1283
|
}
|
|
876
1284
|
}
|
|
1285
|
+
static PUBLICLY_SUBMITTABLE_ACCESS_TYPES = new Set([
|
|
1286
|
+
_enums_form_access_type_enum_js__rspack_import_13/* .FormAccessType.PUBLIC */.h.PUBLIC,
|
|
1287
|
+
_enums_form_access_type_enum_js__rspack_import_13/* .FormAccessType.EMAIL_VERIFIED */.h.EMAIL_VERIFIED
|
|
1288
|
+
]);
|
|
877
1289
|
async validateSubmissionAccess(form, user, isPublic) {
|
|
878
1290
|
if (isPublic) {
|
|
879
|
-
if (form.accessType
|
|
1291
|
+
if (!FormResultService.PUBLICLY_SUBMITTABLE_ACCESS_TYPES.has(form.accessType)) {
|
|
880
1292
|
throw new _nestjs_common__rspack_import_3.UnauthorizedException({
|
|
881
1293
|
message: 'This form is not available for public submission',
|
|
882
|
-
messageKey:
|
|
1294
|
+
messageKey: _config_index_js__rspack_import_12/* .FORM_MESSAGES.NOT_PUBLIC */.Q.NOT_PUBLIC
|
|
883
1295
|
});
|
|
884
1296
|
}
|
|
885
1297
|
return;
|
|
886
1298
|
}
|
|
887
|
-
|
|
1299
|
+
switch(form.accessType){
|
|
1300
|
+
case _enums_form_access_type_enum_js__rspack_import_13/* .FormAccessType.PUBLIC */.h.PUBLIC:
|
|
1301
|
+
return;
|
|
1302
|
+
case _enums_form_access_type_enum_js__rspack_import_13/* .FormAccessType.EMAIL_VERIFIED */.h.EMAIL_VERIFIED:
|
|
1303
|
+
throw new _nestjs_common__rspack_import_3.BadRequestException({
|
|
1304
|
+
message: 'Email-verified forms must be submitted through the public endpoint',
|
|
1305
|
+
messageKey: _config_index_js__rspack_import_12/* .FORM_MESSAGES.INVALID_ACCESS_TYPE */.Q.INVALID_ACCESS_TYPE
|
|
1306
|
+
});
|
|
1307
|
+
case _enums_form_access_type_enum_js__rspack_import_13/* .FormAccessType.ACTION_GROUP */.h.ACTION_GROUP:
|
|
1308
|
+
case _enums_form_access_type_enum_js__rspack_import_13/* .FormAccessType.AUTHENTICATED */.h.AUTHENTICATED:
|
|
1309
|
+
break;
|
|
1310
|
+
default:
|
|
1311
|
+
throw new _nestjs_common__rspack_import_3.BadRequestException({
|
|
1312
|
+
message: 'Invalid form access type',
|
|
1313
|
+
messageKey: _config_index_js__rspack_import_12/* .FORM_MESSAGES.INVALID_ACCESS_TYPE */.Q.INVALID_ACCESS_TYPE
|
|
1314
|
+
});
|
|
1315
|
+
}
|
|
888
1316
|
if (!user) {
|
|
889
1317
|
throw new _nestjs_common__rspack_import_3.UnauthorizedException({
|
|
890
1318
|
message: 'Authentication required to submit this form',
|
|
891
|
-
messageKey:
|
|
1319
|
+
messageKey: _config_index_js__rspack_import_12/* .FORM_MESSAGES.AUTH_REQUIRED */.Q.AUTH_REQUIRED
|
|
892
1320
|
});
|
|
893
1321
|
}
|
|
894
|
-
if (form.accessType ===
|
|
1322
|
+
if (form.accessType === _enums_form_access_type_enum_js__rspack_import_13/* .FormAccessType.ACTION_GROUP */.h.ACTION_GROUP && form.actionGroups?.length) {
|
|
895
1323
|
const hasPermission = await (0,_utils_permission_utils_js__rspack_import_7/* .validateUserPermissions */.i)(user, form.actionGroups, this.cacheManager, this.formBuilderConfig.isCompanyFeatureEnabled());
|
|
896
1324
|
if (!hasPermission) {
|
|
897
1325
|
throw new _nestjs_common__rspack_import_3.ForbiddenException({
|
|
898
1326
|
message: 'You do not have permission to submit this form',
|
|
899
|
-
messageKey:
|
|
1327
|
+
messageKey: _config_index_js__rspack_import_12/* .FORM_MESSAGES.ACCESS_DENIED */.Q.ACCESS_DENIED
|
|
900
1328
|
});
|
|
901
1329
|
}
|
|
902
|
-
} else if (form.accessType !==
|
|
1330
|
+
} else if (form.accessType !== _enums_form_access_type_enum_js__rspack_import_13/* .FormAccessType.AUTHENTICATED */.h.AUTHENTICATED) {
|
|
903
1331
|
throw new _nestjs_common__rspack_import_3.BadRequestException({
|
|
904
1332
|
message: 'Invalid form access type',
|
|
905
|
-
messageKey:
|
|
1333
|
+
messageKey: _config_index_js__rspack_import_12/* .FORM_MESSAGES.INVALID_ACCESS_TYPE */.Q.INVALID_ACCESS_TYPE
|
|
1334
|
+
});
|
|
1335
|
+
}
|
|
1336
|
+
}
|
|
1337
|
+
async resolveEmailVerifiedSubmitter(form, user, accessToken) {
|
|
1338
|
+
if (form.accessType !== _enums_form_access_type_enum_js__rspack_import_13/* .FormAccessType.EMAIL_VERIFIED */.h.EMAIL_VERIFIED) return null;
|
|
1339
|
+
const email = await this.formEmailAuthService.resolveSubmitterEmail(form.id, user ?? undefined, accessToken);
|
|
1340
|
+
if (!email) {
|
|
1341
|
+
throw new _nestjs_common__rspack_import_3.UnauthorizedException({
|
|
1342
|
+
message: 'Email verification required to submit this form',
|
|
1343
|
+
messageKey: _config_index_js__rspack_import_12/* .FORM_MESSAGES.EMAIL_VERIFICATION_REQUIRED */.Q.EMAIL_VERIFICATION_REQUIRED
|
|
1344
|
+
});
|
|
1345
|
+
}
|
|
1346
|
+
return email;
|
|
1347
|
+
}
|
|
1348
|
+
async assertSingleResponseNotExceeded(form, submitterEmail) {
|
|
1349
|
+
if (!submitterEmail) return;
|
|
1350
|
+
const settings = form.schema?.settings;
|
|
1351
|
+
if (settings?.responseMode !== 'single') return;
|
|
1352
|
+
const count = await this.repository.count({
|
|
1353
|
+
where: {
|
|
1354
|
+
formId: form.id,
|
|
1355
|
+
submitterEmail,
|
|
1356
|
+
isDraft: false,
|
|
1357
|
+
deletedAt: (0,typeorm__rspack_import_4.IsNull)()
|
|
1358
|
+
}
|
|
1359
|
+
});
|
|
1360
|
+
if (count > 0) {
|
|
1361
|
+
throw new _nestjs_common__rspack_import_3.ConflictException({
|
|
1362
|
+
message: 'You have already submitted a response to this form',
|
|
1363
|
+
messageKey: _config_index_js__rspack_import_12/* .FORM_RESULT_MESSAGES.ALREADY_SUBMITTED */.y.ALREADY_SUBMITTED
|
|
906
1364
|
});
|
|
907
1365
|
}
|
|
908
1366
|
}
|
|
@@ -924,6 +1382,7 @@ class FormResultService extends _flusys_nestjs_shared_classes__rspack_import_0.A
|
|
|
924
1382
|
'schemaVersion',
|
|
925
1383
|
'data',
|
|
926
1384
|
'submittedById',
|
|
1385
|
+
'submitterEmail',
|
|
927
1386
|
'submittedAt',
|
|
928
1387
|
'isDraft',
|
|
929
1388
|
'createdAt',
|
|
@@ -952,7 +1411,7 @@ class FormResultService extends _flusys_nestjs_shared_classes__rspack_import_0.A
|
|
|
952
1411
|
if (!computedFields || computedFields.length === 0) {
|
|
953
1412
|
return data;
|
|
954
1413
|
}
|
|
955
|
-
const computedValues = (0,
|
|
1414
|
+
const computedValues = (0,_utils_computed_field_utils_js__rspack_import_14/* .calculateComputedFields */.X)(data, computedFields);
|
|
956
1415
|
return {
|
|
957
1416
|
...data,
|
|
958
1417
|
_computed: computedValues
|
|
@@ -966,6 +1425,7 @@ class FormResultService extends _flusys_nestjs_shared_classes__rspack_import_0.A
|
|
|
966
1425
|
schemaVersion: entity.schemaVersion,
|
|
967
1426
|
data: entity.data,
|
|
968
1427
|
submittedById: entity.submittedById,
|
|
1428
|
+
submitterEmail: entity.submitterEmail,
|
|
969
1429
|
submittedAt: entity.submittedAt,
|
|
970
1430
|
isDraft: entity.isDraft,
|
|
971
1431
|
createdAt: entity.createdAt,
|
|
@@ -981,7 +1441,21 @@ class FormResultService extends _flusys_nestjs_shared_classes__rspack_import_0.A
|
|
|
981
1441
|
await this.ensureDataSourceRepository();
|
|
982
1442
|
const form = await this.getActiveForm(dto.formId);
|
|
983
1443
|
await this.validateSubmissionAccess(form, user, isPublic);
|
|
1444
|
+
const submitterEmail = await this.resolveEmailVerifiedSubmitter(form, user, dto.accessToken);
|
|
984
1445
|
const isDraft = dto.isDraft ?? false;
|
|
1446
|
+
const persist = ()=>this.persistSubmission(form, dto, user, submitterEmail, isDraft);
|
|
1447
|
+
// Only a non-draft submission is subject to the single-response check, so
|
|
1448
|
+
// only that path needs serializing against a concurrent duplicate for the
|
|
1449
|
+
// same form + email (see withSubmissionLock()).
|
|
1450
|
+
if (!isDraft && submitterEmail) {
|
|
1451
|
+
return this.withSubmissionLock(`${form.id}:${submitterEmail}`, persist);
|
|
1452
|
+
}
|
|
1453
|
+
return persist();
|
|
1454
|
+
}
|
|
1455
|
+
async persistSubmission(form, dto, user, submitterEmail, isDraft) {
|
|
1456
|
+
if (!isDraft) {
|
|
1457
|
+
await this.assertSingleResponseNotExceeded(form, submitterEmail);
|
|
1458
|
+
}
|
|
985
1459
|
const staleResults = [];
|
|
986
1460
|
// Handle existing draft for authenticated users
|
|
987
1461
|
if (user?.id) {
|
|
@@ -1013,6 +1487,7 @@ class FormResultService extends _flusys_nestjs_shared_classes__rspack_import_0.A
|
|
|
1013
1487
|
schemaVersion: form.schemaVersion,
|
|
1014
1488
|
data: this.applyComputedFields(dto.data, form, isDraft),
|
|
1015
1489
|
submittedById: user?.id ?? null,
|
|
1490
|
+
submitterEmail,
|
|
1016
1491
|
submittedAt: new Date(),
|
|
1017
1492
|
isDraft
|
|
1018
1493
|
});
|
|
@@ -1045,7 +1520,7 @@ class FormResultService extends _flusys_nestjs_shared_classes__rspack_import_0.A
|
|
|
1045
1520
|
if (!draft) {
|
|
1046
1521
|
throw new _nestjs_common__rspack_import_3.NotFoundException({
|
|
1047
1522
|
message: 'Draft not found',
|
|
1048
|
-
messageKey:
|
|
1523
|
+
messageKey: _config_index_js__rspack_import_12/* .FORM_RESULT_MESSAGES.NOT_FOUND */.y.NOT_FOUND
|
|
1049
1524
|
});
|
|
1050
1525
|
}
|
|
1051
1526
|
const form = await this.getActiveForm(dto.formId);
|
|
@@ -1065,7 +1540,7 @@ class FormResultService extends _flusys_nestjs_shared_classes__rspack_import_0.A
|
|
|
1065
1540
|
return this.convertEntityToResponseDto(saved, false);
|
|
1066
1541
|
}
|
|
1067
1542
|
async publishSubmissionEvent(result, formId, isDraft, user) {
|
|
1068
|
-
await this.publishDomainAction(isDraft ?
|
|
1543
|
+
await this.publishDomainAction(isDraft ? _config_index_js__rspack_import_11/* .FORM_BUILDER_EVENT_ACTIONS.DRAFT_SAVED */.ov.DRAFT_SAVED : _config_index_js__rspack_import_11/* .FORM_BUILDER_EVENT_ACTIONS.SUBMITTED */.ov.SUBMITTED, {
|
|
1069
1544
|
ids: [
|
|
1070
1545
|
result.id
|
|
1071
1546
|
],
|
|
@@ -1111,6 +1586,20 @@ class FormResultService extends _flusys_nestjs_shared_classes__rspack_import_0.A
|
|
|
1111
1586
|
});
|
|
1112
1587
|
return count > 0;
|
|
1113
1588
|
}
|
|
1589
|
+
// Anonymous-path counterpart to hasUserSubmitted, keyed by a resolved
|
|
1590
|
+
// EMAIL_VERIFIED submitter email instead of a user id.
|
|
1591
|
+
async hasSubmittedByEmail(formId, email) {
|
|
1592
|
+
await this.ensureDataSourceRepository();
|
|
1593
|
+
const count = await this.repository.count({
|
|
1594
|
+
where: {
|
|
1595
|
+
formId,
|
|
1596
|
+
submitterEmail: email,
|
|
1597
|
+
isDraft: false,
|
|
1598
|
+
deletedAt: (0,typeorm__rspack_import_4.IsNull)()
|
|
1599
|
+
}
|
|
1600
|
+
});
|
|
1601
|
+
return count > 0;
|
|
1602
|
+
}
|
|
1114
1603
|
}
|
|
1115
1604
|
_ts_decorate([
|
|
1116
1605
|
(0,_flusys_nestjs_shared_decorators__rspack_import_1.LogAction)({
|
|
@@ -1146,12 +1635,14 @@ FormResultService = _ts_decorate([
|
|
|
1146
1635
|
_ts_param(1, (0,_nestjs_common__rspack_import_3.Inject)(_flusys_nestjs_shared_modules__rspack_import_2.UtilsService)),
|
|
1147
1636
|
_ts_param(2, (0,_nestjs_common__rspack_import_3.Inject)(_form_builder_config_service_js__rspack_import_8/* .FormBuilderConfigService */.j)),
|
|
1148
1637
|
_ts_param(3, (0,_nestjs_common__rspack_import_3.Inject)(_form_builder_datasource_provider_js__rspack_import_9/* .FormBuilderDataSourceProvider */._)),
|
|
1638
|
+
_ts_param(4, (0,_nestjs_common__rspack_import_3.Inject)(_form_email_auth_service_js__rspack_import_10/* .FormEmailAuthService */.w)),
|
|
1149
1639
|
_ts_metadata("design:type", Function),
|
|
1150
1640
|
_ts_metadata("design:paramtypes", [
|
|
1151
1641
|
typeof _flusys_nestjs_shared_classes__rspack_import_0.HybridCache === "undefined" ? Object : _flusys_nestjs_shared_classes__rspack_import_0.HybridCache,
|
|
1152
1642
|
typeof _flusys_nestjs_shared_modules__rspack_import_2.UtilsService === "undefined" ? Object : _flusys_nestjs_shared_modules__rspack_import_2.UtilsService,
|
|
1153
1643
|
typeof _form_builder_config_service_js__rspack_import_8/* .FormBuilderConfigService */.j === "undefined" ? Object : _form_builder_config_service_js__rspack_import_8/* .FormBuilderConfigService */.j,
|
|
1154
|
-
typeof _form_builder_datasource_provider_js__rspack_import_9/* .FormBuilderDataSourceProvider */._ === "undefined" ? Object : _form_builder_datasource_provider_js__rspack_import_9/* .FormBuilderDataSourceProvider */._
|
|
1644
|
+
typeof _form_builder_datasource_provider_js__rspack_import_9/* .FormBuilderDataSourceProvider */._ === "undefined" ? Object : _form_builder_datasource_provider_js__rspack_import_9/* .FormBuilderDataSourceProvider */._,
|
|
1645
|
+
typeof _form_email_auth_service_js__rspack_import_10/* .FormEmailAuthService */.w === "undefined" ? Object : _form_email_auth_service_js__rspack_import_10/* .FormEmailAuthService */.w
|
|
1155
1646
|
])
|
|
1156
1647
|
], FormResultService);
|
|
1157
1648
|
|
|
@@ -1161,7 +1652,7 @@ __webpack_require__.d(__webpack_exports__, {
|
|
|
1161
1652
|
|
|
1162
1653
|
|
|
1163
1654
|
},
|
|
1164
|
-
|
|
1655
|
+
134(__unused_rspack_module, __webpack_exports__, __webpack_require__) {
|
|
1165
1656
|
|
|
1166
1657
|
// EXPORTS
|
|
1167
1658
|
__webpack_require__.d(__webpack_exports__, {
|
|
@@ -1174,8 +1665,8 @@ __webpack_require__.d(__webpack_exports__, {
|
|
|
1174
1665
|
var classes_ = __webpack_require__(937);
|
|
1175
1666
|
// EXTERNAL MODULE: external "@flusys/nestjs-shared/modules"
|
|
1176
1667
|
var modules_ = __webpack_require__(236);
|
|
1177
|
-
|
|
1178
|
-
|
|
1668
|
+
// EXTERNAL MODULE: external "@flusys/nestjs-shared/utils"
|
|
1669
|
+
var utils_ = __webpack_require__(216);
|
|
1179
1670
|
// EXTERNAL MODULE: external "@nestjs/common"
|
|
1180
1671
|
var common_ = __webpack_require__(157);
|
|
1181
1672
|
// EXTERNAL MODULE: external "typeorm"
|
|
@@ -1184,8 +1675,8 @@ var external_typeorm_ = __webpack_require__(158);
|
|
|
1184
1675
|
var event_actions = __webpack_require__(769);
|
|
1185
1676
|
// EXTERNAL MODULE: ./projects/nestjs-form-builder/src/config/message-keys.ts
|
|
1186
1677
|
var message_keys = __webpack_require__(137);
|
|
1187
|
-
// EXTERNAL MODULE: ./projects/nestjs-form-builder/src/entities/index.ts +
|
|
1188
|
-
var entities = __webpack_require__(
|
|
1678
|
+
// EXTERNAL MODULE: ./projects/nestjs-form-builder/src/entities/index.ts + 6 modules
|
|
1679
|
+
var entities = __webpack_require__(814);
|
|
1189
1680
|
// EXTERNAL MODULE: ./projects/nestjs-form-builder/src/enums/form-access-type.enum.ts
|
|
1190
1681
|
var form_access_type_enum = __webpack_require__(376);
|
|
1191
1682
|
// EXTERNAL MODULE: ./projects/nestjs-form-builder/src/utils/permission.utils.ts
|
|
@@ -1348,7 +1839,7 @@ class FormService extends classes_.ApiService {
|
|
|
1348
1839
|
async getExtraManipulateQuery(query, filterDto, user) {
|
|
1349
1840
|
const result = await super.getExtraManipulateQuery(query, filterDto, user);
|
|
1350
1841
|
// Apply company filter using shared utility
|
|
1351
|
-
(0,
|
|
1842
|
+
(0,utils_.applyCompanyFilter)(query, {
|
|
1352
1843
|
isCompanyFeatureEnabled: this.formBuilderConfig.isCompanyFeatureEnabled(),
|
|
1353
1844
|
entityAlias: 'form'
|
|
1354
1845
|
}, user);
|
|
@@ -1494,6 +1985,33 @@ class FormService extends classes_.ApiService {
|
|
|
1494
1985
|
isActive: form.isActive
|
|
1495
1986
|
};
|
|
1496
1987
|
}
|
|
1988
|
+
async getEmailVerifiedFormEntity(formId) {
|
|
1989
|
+
await this.ensureDataSourceRepository();
|
|
1990
|
+
const form = await this.repository.findOne({
|
|
1991
|
+
where: {
|
|
1992
|
+
id: formId,
|
|
1993
|
+
isActive: true,
|
|
1994
|
+
deletedAt: (0,external_typeorm_.IsNull)()
|
|
1995
|
+
}
|
|
1996
|
+
});
|
|
1997
|
+
if (!form) {
|
|
1998
|
+
throw new common_.NotFoundException({
|
|
1999
|
+
message: 'Form not found or inactive',
|
|
2000
|
+
messageKey: message_keys/* .FORM_MESSAGES.NOT_FOUND */.Q.NOT_FOUND
|
|
2001
|
+
});
|
|
2002
|
+
}
|
|
2003
|
+
if (form.accessType !== form_access_type_enum/* .FormAccessType.EMAIL_VERIFIED */.h.EMAIL_VERIFIED) {
|
|
2004
|
+
throw new common_.BadRequestException({
|
|
2005
|
+
message: 'This form does not use email verification',
|
|
2006
|
+
messageKey: message_keys/* .FORM_MESSAGES.INVALID_ACCESS_TYPE */.Q.INVALID_ACCESS_TYPE
|
|
2007
|
+
});
|
|
2008
|
+
}
|
|
2009
|
+
return form;
|
|
2010
|
+
}
|
|
2011
|
+
async getEmailVerifiedForm(formId) {
|
|
2012
|
+
const form = await this.getEmailVerifiedFormEntity(formId);
|
|
2013
|
+
return this.toPublicForm(form);
|
|
2014
|
+
}
|
|
1497
2015
|
async getAuthenticatedForm(formId, user) {
|
|
1498
2016
|
const form = await this.getFormForSubmission(formId, user);
|
|
1499
2017
|
// For action_group access, check permissions from cache
|
|
@@ -1806,6 +2324,12 @@ module.exports = __rspack_external__flusys_nestjs_shared_dtos_c4585026;
|
|
|
1806
2324
|
module.exports = __rspack_external__flusys_nestjs_shared_modules_24702bee;
|
|
1807
2325
|
|
|
1808
2326
|
|
|
2327
|
+
},
|
|
2328
|
+
216(module) {
|
|
2329
|
+
|
|
2330
|
+
module.exports = __rspack_external__flusys_nestjs_shared_utils_9125c1f1;
|
|
2331
|
+
|
|
2332
|
+
|
|
1809
2333
|
},
|
|
1810
2334
|
157(module) {
|
|
1811
2335
|
|