@saasicat/nest 0.2.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/LICENSE +202 -0
- package/README.md +53 -0
- package/dist/admin/index.cjs +1283 -0
- package/dist/admin/index.d.cts +225 -0
- package/dist/admin/index.d.ts +225 -0
- package/dist/admin/index.js +58 -0
- package/dist/admin-audit.service-9IqXMlZm.d.cts +30 -0
- package/dist/admin-audit.service-9IqXMlZm.d.ts +30 -0
- package/dist/admin-manifest.config-DyrQNT7M.d.cts +10 -0
- package/dist/admin-manifest.config-DyrQNT7M.d.ts +10 -0
- package/dist/aggregation-CJ3qQf92.d.cts +288 -0
- package/dist/aggregation-Dvz9e8X2.d.ts +288 -0
- package/dist/billing/index.cjs +5205 -0
- package/dist/billing/index.d.cts +1326 -0
- package/dist/billing/index.d.ts +1326 -0
- package/dist/billing/index.js +197 -0
- package/dist/catalog/index.cjs +6449 -0
- package/dist/catalog/index.d.cts +1073 -0
- package/dist/catalog/index.d.ts +1073 -0
- package/dist/catalog/index.js +158 -0
- package/dist/checkout-offer/index.cjs +807 -0
- package/dist/checkout-offer/index.d.cts +120 -0
- package/dist/checkout-offer/index.d.ts +120 -0
- package/dist/checkout-offer/index.js +20 -0
- package/dist/chunk-2FR6ZL7R.js +0 -0
- package/dist/chunk-2PNX2QL2.js +347 -0
- package/dist/chunk-2VNZYRDV.js +326 -0
- package/dist/chunk-7MLGHWKO.js +12 -0
- package/dist/chunk-ABB2CRAS.js +31 -0
- package/dist/chunk-AJ3EPELS.js +157 -0
- package/dist/chunk-E56W4U2P.js +90 -0
- package/dist/chunk-HRU3CP57.js +71 -0
- package/dist/chunk-L4SUWH5B.js +14 -0
- package/dist/chunk-M6CZQYPY.js +314 -0
- package/dist/chunk-MDIZUVIK.js +87 -0
- package/dist/chunk-MV6LW5UB.js +217 -0
- package/dist/chunk-NYLON2VC.js +6 -0
- package/dist/chunk-P6MYZMXQ.js +951 -0
- package/dist/chunk-Q53N43LQ.js +13 -0
- package/dist/chunk-QJVPRD3R.js +573 -0
- package/dist/chunk-RIGLQ4BV.js +3367 -0
- package/dist/chunk-SABTXESR.js +24 -0
- package/dist/chunk-SHUYVCID.js +6 -0
- package/dist/chunk-SYEVGPFC.js +686 -0
- package/dist/chunk-V7HUZ6UQ.js +5923 -0
- package/dist/chunk-WPUVJRIN.js +29 -0
- package/dist/chunk-XCZ3TTFK.js +22 -0
- package/dist/chunk-XSGU2Y2O.js +1611 -0
- package/dist/chunk-YNEW3L63.js +190 -0
- package/dist/chunk-YXGHPILG.js +614 -0
- package/dist/chunk-ZL4LLE2Z.js +465 -0
- package/dist/di-CcNeq9v-.d.cts +16 -0
- package/dist/di-CcNeq9v-.d.ts +16 -0
- package/dist/discovery/index.cjs +588 -0
- package/dist/discovery/index.d.cts +299 -0
- package/dist/discovery/index.d.ts +299 -0
- package/dist/discovery/index.js +49 -0
- package/dist/discovery.scanner-CUYLKlYT.d.cts +59 -0
- package/dist/discovery.scanner-CUYLKlYT.d.ts +59 -0
- package/dist/entitlement/index.cjs +805 -0
- package/dist/entitlement/index.d.cts +60 -0
- package/dist/entitlement/index.d.ts +60 -0
- package/dist/entitlement/index.js +57 -0
- package/dist/index.cjs +15918 -0
- package/dist/index.d.cts +85 -0
- package/dist/index.d.ts +85 -0
- package/dist/index.js +956 -0
- package/dist/plan-resolution-CFCoUkrE.d.cts +41 -0
- package/dist/plan-resolution-CFCoUkrE.d.ts +41 -0
- package/dist/platform/index.cjs +2772 -0
- package/dist/platform/index.d.cts +118 -0
- package/dist/platform/index.d.ts +118 -0
- package/dist/platform/index.js +37 -0
- package/dist/promo/index.cjs +995 -0
- package/dist/promo/index.d.cts +121 -0
- package/dist/promo/index.d.ts +121 -0
- package/dist/promo/index.js +58 -0
- package/dist/registration/index.cjs +1687 -0
- package/dist/registration/index.d.cts +444 -0
- package/dist/registration/index.d.ts +444 -0
- package/dist/registration/index.js +82 -0
- package/dist/saas-platform.module-CgZ2omgg.d.cts +180 -0
- package/dist/saas-platform.module-DT9TnCzk.d.ts +180 -0
- package/dist/service-DX8KbGXl.d.cts +109 -0
- package/dist/service-DX8KbGXl.d.ts +109 -0
- package/dist/subscription-contract/index.cjs +471 -0
- package/dist/subscription-contract/index.d.cts +17 -0
- package/dist/subscription-contract/index.d.ts +17 -0
- package/dist/subscription-contract/index.js +21 -0
- package/dist/subscription-contract.service--cm47ZJJ.d.cts +39 -0
- package/dist/subscription-contract.service--cm47ZJJ.d.ts +39 -0
- package/dist/testing/index.cjs +3942 -0
- package/dist/testing/index.d.cts +279 -0
- package/dist/testing/index.d.ts +279 -0
- package/dist/testing/index.js +1195 -0
- package/package.json +185 -0
|
@@ -0,0 +1,1687 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
7
|
+
var __export = (target, all) => {
|
|
8
|
+
for (var name in all)
|
|
9
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
10
|
+
};
|
|
11
|
+
var __copyProps = (to, from, except, desc) => {
|
|
12
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
13
|
+
for (let key of __getOwnPropNames(from))
|
|
14
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
15
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
16
|
+
}
|
|
17
|
+
return to;
|
|
18
|
+
};
|
|
19
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
20
|
+
|
|
21
|
+
// src/registration/index.ts
|
|
22
|
+
var registration_exports = {};
|
|
23
|
+
__export(registration_exports, {
|
|
24
|
+
ACTIVATION_ORCHESTRATOR_TOKEN: () => ACTIVATION_ORCHESTRATOR_TOKEN,
|
|
25
|
+
BaseIpRateLimitGuard: () => BaseIpRateLimitGuard,
|
|
26
|
+
ContinueRegistrationDto: () => ContinueRegistrationDto,
|
|
27
|
+
CronExpression: () => import_schedule.CronExpression,
|
|
28
|
+
PASSWORD_HASHER_TOKEN: () => PASSWORD_HASHER_TOKEN,
|
|
29
|
+
PAYMENT_EVENT_LOG_TOKEN: () => PAYMENT_EVENT_LOG_TOKEN,
|
|
30
|
+
PAYMENT_PROVIDER_TOKEN: () => PAYMENT_PROVIDER_TOKEN,
|
|
31
|
+
PENDING_REGISTRATION_REPOSITORY_TOKEN: () => PENDING_REGISTRATION_REPOSITORY_TOKEN,
|
|
32
|
+
PLAN_CATALOG_LOOKUP_TOKEN: () => PLAN_CATALOG_LOOKUP_TOKEN,
|
|
33
|
+
PaymentWebhookDto: () => PaymentWebhookDto,
|
|
34
|
+
PendingRegistrationService: () => PendingRegistrationService,
|
|
35
|
+
PreviewRegistrationPromoDto: () => PreviewRegistrationPromoDto,
|
|
36
|
+
REGISTRATION_AUDIT_LOGGER_TOKEN: () => REGISTRATION_AUDIT_LOGGER_TOKEN,
|
|
37
|
+
REGISTRATION_BUSINESS_TYPE_LOOKUP_TOKEN: () => REGISTRATION_BUSINESS_TYPE_LOOKUP_TOKEN,
|
|
38
|
+
REGISTRATION_CONFIGURATOR_LOOKUP_TOKEN: () => REGISTRATION_CONFIGURATOR_LOOKUP_TOKEN,
|
|
39
|
+
REGISTRATION_CONFIG_TOKEN: () => REGISTRATION_CONFIG_TOKEN,
|
|
40
|
+
REGISTRATION_OTP_DELIVERY_TOKEN: () => REGISTRATION_OTP_DELIVERY_TOKEN,
|
|
41
|
+
REGISTRATION_PROMO_PREVIEW_TOKEN: () => REGISTRATION_PROMO_PREVIEW_TOKEN,
|
|
42
|
+
REGISTRATION_RESUME_BASE_URL_TOKEN: () => REGISTRATION_RESUME_BASE_URL_TOKEN,
|
|
43
|
+
REGISTRATION_RESUME_DELIVERY_TOKEN: () => REGISTRATION_RESUME_DELIVERY_TOKEN,
|
|
44
|
+
REGISTRATION_RESUME_TOKEN_SIGNER_TOKEN: () => REGISTRATION_RESUME_TOKEN_SIGNER_TOKEN,
|
|
45
|
+
RegisterStartDto: () => RegisterStartDto,
|
|
46
|
+
RegistrationCleanupCron: () => RegistrationCleanupCron,
|
|
47
|
+
RegistrationModule: () => RegistrationModule,
|
|
48
|
+
ResendRegistrationOtpDto: () => ResendRegistrationOtpDto,
|
|
49
|
+
SLUG_AVAILABILITY_CHECK_TOKEN: () => SLUG_AVAILABILITY_CHECK_TOKEN,
|
|
50
|
+
SaveRegistrationConfigDto: () => SaveRegistrationConfigDto,
|
|
51
|
+
SaveRegistrationConfigSelectionDto: () => SaveRegistrationConfigSelectionDto,
|
|
52
|
+
SelectRegistrationPlanDto: () => SelectRegistrationPlanDto,
|
|
53
|
+
StartRegistrationCheckoutDto: () => StartRegistrationCheckoutDto,
|
|
54
|
+
USER_ACCOUNT_LOOKUP_TOKEN: () => USER_ACCOUNT_LOOKUP_TOKEN,
|
|
55
|
+
VerifyRegistrationOtpDto: () => VerifyRegistrationOtpDto,
|
|
56
|
+
computeBreakdown: () => computeBreakdown,
|
|
57
|
+
generateOtpCode: () => generateOtpCode,
|
|
58
|
+
hashOtpCode: () => hashOtpCode,
|
|
59
|
+
resolveModel: () => resolveModel,
|
|
60
|
+
slugify: () => slugify,
|
|
61
|
+
verifyOtpCode: () => verifyOtpCode
|
|
62
|
+
});
|
|
63
|
+
module.exports = __toCommonJS(registration_exports);
|
|
64
|
+
|
|
65
|
+
// src/registration/helpers.ts
|
|
66
|
+
var import_node_crypto = require("crypto");
|
|
67
|
+
function generateOtpCode() {
|
|
68
|
+
return String((0, import_node_crypto.randomInt)(1e5, 1e6));
|
|
69
|
+
}
|
|
70
|
+
__name(generateOtpCode, "generateOtpCode");
|
|
71
|
+
function hashOtpCode(code) {
|
|
72
|
+
return (0, import_node_crypto.createHash)("sha256").update(code).digest("hex");
|
|
73
|
+
}
|
|
74
|
+
__name(hashOtpCode, "hashOtpCode");
|
|
75
|
+
function verifyOtpCode(expectedHash, code) {
|
|
76
|
+
const actual = hashOtpCode(code);
|
|
77
|
+
if (actual.length !== expectedHash.length) {
|
|
78
|
+
return false;
|
|
79
|
+
}
|
|
80
|
+
try {
|
|
81
|
+
return (0, import_node_crypto.timingSafeEqual)(Buffer.from(actual, "hex"), Buffer.from(expectedHash, "hex"));
|
|
82
|
+
} catch {
|
|
83
|
+
return false;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
__name(verifyOtpCode, "verifyOtpCode");
|
|
87
|
+
function slugify(name) {
|
|
88
|
+
const transliterated = name.toLowerCase().replace(/ß/g, "ss").replace(/ä/g, "ae").replace(/ö/g, "oe").replace(/ü/g, "ue").normalize("NFKD").replace(/[̀-ͯ]/g, "");
|
|
89
|
+
const slug = transliterated.replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
90
|
+
return slug || "verein";
|
|
91
|
+
}
|
|
92
|
+
__name(slugify, "slugify");
|
|
93
|
+
|
|
94
|
+
// src/registration/tokens.ts
|
|
95
|
+
var PENDING_REGISTRATION_REPOSITORY_TOKEN = /* @__PURE__ */ Symbol("PENDING_REGISTRATION_REPOSITORY");
|
|
96
|
+
var REGISTRATION_OTP_DELIVERY_TOKEN = /* @__PURE__ */ Symbol("REGISTRATION_OTP_DELIVERY");
|
|
97
|
+
var USER_ACCOUNT_LOOKUP_TOKEN = /* @__PURE__ */ Symbol("USER_ACCOUNT_LOOKUP");
|
|
98
|
+
var SLUG_AVAILABILITY_CHECK_TOKEN = /* @__PURE__ */ Symbol("SLUG_AVAILABILITY_CHECK");
|
|
99
|
+
var PASSWORD_HASHER_TOKEN = /* @__PURE__ */ Symbol("PASSWORD_HASHER");
|
|
100
|
+
var PLAN_CATALOG_LOOKUP_TOKEN = /* @__PURE__ */ Symbol("PLAN_CATALOG_LOOKUP");
|
|
101
|
+
var PAYMENT_PROVIDER_TOKEN = /* @__PURE__ */ Symbol("PAYMENT_PROVIDER");
|
|
102
|
+
var PAYMENT_EVENT_LOG_TOKEN = /* @__PURE__ */ Symbol("PAYMENT_EVENT_LOG");
|
|
103
|
+
var ACTIVATION_ORCHESTRATOR_TOKEN = /* @__PURE__ */ Symbol("ACTIVATION_ORCHESTRATOR");
|
|
104
|
+
var REGISTRATION_AUDIT_LOGGER_TOKEN = /* @__PURE__ */ Symbol("REGISTRATION_AUDIT_LOGGER");
|
|
105
|
+
var REGISTRATION_RESUME_TOKEN_SIGNER_TOKEN = /* @__PURE__ */ Symbol("REGISTRATION_RESUME_TOKEN_SIGNER");
|
|
106
|
+
var REGISTRATION_RESUME_DELIVERY_TOKEN = /* @__PURE__ */ Symbol("REGISTRATION_RESUME_DELIVERY");
|
|
107
|
+
var REGISTRATION_RESUME_BASE_URL_TOKEN = /* @__PURE__ */ Symbol("REGISTRATION_RESUME_BASE_URL");
|
|
108
|
+
var REGISTRATION_CONFIGURATOR_LOOKUP_TOKEN = /* @__PURE__ */ Symbol("REGISTRATION_CONFIGURATOR_LOOKUP");
|
|
109
|
+
var REGISTRATION_PROMO_PREVIEW_TOKEN = /* @__PURE__ */ Symbol("REGISTRATION_PROMO_PREVIEW");
|
|
110
|
+
var REGISTRATION_BUSINESS_TYPE_LOOKUP_TOKEN = /* @__PURE__ */ Symbol("REGISTRATION_BUSINESS_TYPE_LOOKUP");
|
|
111
|
+
var REGISTRATION_CONFIG_TOKEN = /* @__PURE__ */ Symbol("REGISTRATION_CONFIG");
|
|
112
|
+
|
|
113
|
+
// src/registration/pending-registration.service.ts
|
|
114
|
+
var import_common = require("@nestjs/common");
|
|
115
|
+
var import_types = require("@saasicat/types");
|
|
116
|
+
var import_types2 = require("@saasicat/types");
|
|
117
|
+
|
|
118
|
+
// src/registration/pricing.ts
|
|
119
|
+
function round2(value) {
|
|
120
|
+
return Math.round(value * 100) / 100;
|
|
121
|
+
}
|
|
122
|
+
__name(round2, "round2");
|
|
123
|
+
function computeBreakdown(catalog, selection, promo) {
|
|
124
|
+
const model = catalog.models.find((m) => m.id === selection.modelId);
|
|
125
|
+
if (!model) {
|
|
126
|
+
throw new Error(`Unknown model: ${selection.modelId}`);
|
|
127
|
+
}
|
|
128
|
+
const modelMonthlyNet = round2(model.monthlyNet);
|
|
129
|
+
const effectiveQuotas = {
|
|
130
|
+
...model.quotaBase
|
|
131
|
+
};
|
|
132
|
+
const subtotalMonthlyNet = modelMonthlyNet;
|
|
133
|
+
const factor = selection.billingCycle === "YEARLY" ? catalog.cycleDiscount : 1;
|
|
134
|
+
const subtotalNet = round2(subtotalMonthlyNet * factor);
|
|
135
|
+
const discountAmount = promo ? Math.min(round2(promo.discountAmount), subtotalNet) : 0;
|
|
136
|
+
const totalNet = Math.max(0, round2(subtotalNet - discountAmount));
|
|
137
|
+
const totalGross = round2(totalNet * (1 + catalog.vatRate / 100));
|
|
138
|
+
const yearlySavings = selection.billingCycle === "YEARLY" ? round2(subtotalMonthlyNet * 12 - subtotalMonthlyNet * catalog.cycleDiscount) : 0;
|
|
139
|
+
return {
|
|
140
|
+
cycle: selection.billingCycle,
|
|
141
|
+
effectiveQuotas,
|
|
142
|
+
modelMonthlyNet,
|
|
143
|
+
subtotalMonthlyNet,
|
|
144
|
+
subtotalNet,
|
|
145
|
+
discountAmount,
|
|
146
|
+
totalNet,
|
|
147
|
+
vatRate: catalog.vatRate,
|
|
148
|
+
totalGross,
|
|
149
|
+
yearlySavings,
|
|
150
|
+
appliedPromo: promo ? {
|
|
151
|
+
code: "",
|
|
152
|
+
label: promo.label,
|
|
153
|
+
percent: promo.percent
|
|
154
|
+
} : void 0
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
__name(computeBreakdown, "computeBreakdown");
|
|
158
|
+
function resolveModel(catalog, modelId) {
|
|
159
|
+
const model = catalog.models.find((m) => m.id === modelId);
|
|
160
|
+
if (!model) throw new Error(`Unknown model: ${modelId}`);
|
|
161
|
+
return {
|
|
162
|
+
planId: model.planId,
|
|
163
|
+
monthlyNet: model.monthlyNet,
|
|
164
|
+
yearlyNet: model.yearlyNet
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
__name(resolveModel, "resolveModel");
|
|
168
|
+
|
|
169
|
+
// src/registration/pending-registration.service.ts
|
|
170
|
+
function _ts_decorate(decorators, target, key, desc) {
|
|
171
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
172
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
173
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
174
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
175
|
+
}
|
|
176
|
+
__name(_ts_decorate, "_ts_decorate");
|
|
177
|
+
function _ts_metadata(k, v) {
|
|
178
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
179
|
+
}
|
|
180
|
+
__name(_ts_metadata, "_ts_metadata");
|
|
181
|
+
function _ts_param(paramIndex, decorator) {
|
|
182
|
+
return function(target, key) {
|
|
183
|
+
decorator(target, key, paramIndex);
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
__name(_ts_param, "_ts_param");
|
|
187
|
+
var PendingRegistrationService = class _PendingRegistrationService {
|
|
188
|
+
static {
|
|
189
|
+
__name(this, "PendingRegistrationService");
|
|
190
|
+
}
|
|
191
|
+
repo;
|
|
192
|
+
otpDelivery;
|
|
193
|
+
userLookup;
|
|
194
|
+
slugCheck;
|
|
195
|
+
passwordHasher;
|
|
196
|
+
planCatalog;
|
|
197
|
+
paymentProvider;
|
|
198
|
+
paymentEventLog;
|
|
199
|
+
activationOrchestrator;
|
|
200
|
+
audit;
|
|
201
|
+
resumeTokenSigner;
|
|
202
|
+
resumeDelivery;
|
|
203
|
+
resumeBaseUrl;
|
|
204
|
+
configuratorLookup;
|
|
205
|
+
promoPreview;
|
|
206
|
+
businessTypeLookup;
|
|
207
|
+
logger = new import_common.Logger(_PendingRegistrationService.name);
|
|
208
|
+
constructor(repo, otpDelivery, userLookup, slugCheck, passwordHasher, planCatalog, paymentProvider, paymentEventLog, activationOrchestrator, audit, resumeTokenSigner, resumeDelivery, resumeBaseUrl, configuratorLookup, promoPreview, businessTypeLookup) {
|
|
209
|
+
this.repo = repo;
|
|
210
|
+
this.otpDelivery = otpDelivery;
|
|
211
|
+
this.userLookup = userLookup;
|
|
212
|
+
this.slugCheck = slugCheck;
|
|
213
|
+
this.passwordHasher = passwordHasher;
|
|
214
|
+
this.planCatalog = planCatalog;
|
|
215
|
+
this.paymentProvider = paymentProvider;
|
|
216
|
+
this.paymentEventLog = paymentEventLog;
|
|
217
|
+
this.activationOrchestrator = activationOrchestrator;
|
|
218
|
+
this.audit = audit;
|
|
219
|
+
this.resumeTokenSigner = resumeTokenSigner;
|
|
220
|
+
this.resumeDelivery = resumeDelivery;
|
|
221
|
+
this.resumeBaseUrl = resumeBaseUrl;
|
|
222
|
+
this.configuratorLookup = configuratorLookup;
|
|
223
|
+
this.promoPreview = promoPreview;
|
|
224
|
+
this.businessTypeLookup = businessTypeLookup;
|
|
225
|
+
}
|
|
226
|
+
async record(eventType, pendingRegistrationId, context, metadata) {
|
|
227
|
+
try {
|
|
228
|
+
await this.audit.log({
|
|
229
|
+
eventType,
|
|
230
|
+
pendingRegistrationId,
|
|
231
|
+
context,
|
|
232
|
+
metadata
|
|
233
|
+
});
|
|
234
|
+
} catch (error) {
|
|
235
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
236
|
+
this.logger.warn(`Audit-Log fehlgeschlagen (${eventType}): ${message}`);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
/** Public signup plan list for step 3 (frontend `usePublicPlans()`). */
|
|
240
|
+
async listPublicPlans() {
|
|
241
|
+
return this.planCatalog.listPublicSignupPlans();
|
|
242
|
+
}
|
|
243
|
+
async selectPlan(input, context) {
|
|
244
|
+
const pending = await this.repo.findById(input.pendingRegistrationId);
|
|
245
|
+
if (!pending) {
|
|
246
|
+
throw new import_common.NotFoundException({
|
|
247
|
+
code: "PENDING_REGISTRATION_NOT_FOUND"
|
|
248
|
+
});
|
|
249
|
+
}
|
|
250
|
+
if (pending.expiresAt.getTime() < Date.now()) {
|
|
251
|
+
throw new import_common.BadRequestException({
|
|
252
|
+
code: "PENDING_REGISTRATION_EXPIRED"
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
if (pending.status !== "EMAIL_VERIFIED" && pending.status !== "PLAN_SELECTED" && pending.status !== "CHECKOUT_STARTED") {
|
|
256
|
+
throw new import_common.BadRequestException({
|
|
257
|
+
code: "INVALID_REGISTRATION_STATE"
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
const plan = await this.planCatalog.findPublicSignupPlan(input.planId);
|
|
261
|
+
if (!plan) {
|
|
262
|
+
throw new import_common.BadRequestException({
|
|
263
|
+
code: "PLAN_NOT_AVAILABLE"
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
const now = /* @__PURE__ */ new Date();
|
|
267
|
+
const expiresAt = new Date(now.getTime() + import_types2.PENDING_ONBOARDING_TTL_DAYS * 24 * 60 * 60 * 1e3);
|
|
268
|
+
const updated = await this.repo.update(pending.id, {
|
|
269
|
+
status: "PLAN_SELECTED",
|
|
270
|
+
currentStep: 4,
|
|
271
|
+
selectedPlanId: plan.id,
|
|
272
|
+
expiresAt
|
|
273
|
+
});
|
|
274
|
+
await this.record("PLAN_SELECTED", updated.id, context, {
|
|
275
|
+
planId: plan.id
|
|
276
|
+
});
|
|
277
|
+
return {
|
|
278
|
+
pendingRegistrationId: updated.id,
|
|
279
|
+
status: updated.status,
|
|
280
|
+
nextStep: import_types2.REGISTRATION_STEP_BY_STATUS[updated.status],
|
|
281
|
+
selectedPlanId: updated.selectedPlanId ?? plan.id
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
async startCheckout(input, context) {
|
|
285
|
+
const pending = await this.repo.findById(input.pendingRegistrationId);
|
|
286
|
+
if (!pending) {
|
|
287
|
+
throw new import_common.NotFoundException({
|
|
288
|
+
code: "PENDING_REGISTRATION_NOT_FOUND"
|
|
289
|
+
});
|
|
290
|
+
}
|
|
291
|
+
if (pending.expiresAt.getTime() < Date.now()) {
|
|
292
|
+
throw new import_common.BadRequestException({
|
|
293
|
+
code: "PENDING_REGISTRATION_EXPIRED"
|
|
294
|
+
});
|
|
295
|
+
}
|
|
296
|
+
if (pending.status !== "PLAN_SELECTED" && pending.status !== "CHECKOUT_STARTED") {
|
|
297
|
+
throw new import_common.BadRequestException({
|
|
298
|
+
code: "PLAN_NOT_SELECTED"
|
|
299
|
+
});
|
|
300
|
+
}
|
|
301
|
+
if (!pending.selectedPlanId) {
|
|
302
|
+
throw new import_common.BadRequestException({
|
|
303
|
+
code: "PLAN_NOT_SELECTED"
|
|
304
|
+
});
|
|
305
|
+
}
|
|
306
|
+
const plan = await this.planCatalog.findPublicSignupPlan(pending.selectedPlanId);
|
|
307
|
+
if (!plan) {
|
|
308
|
+
throw new import_common.BadRequestException({
|
|
309
|
+
code: "PLAN_NOT_AVAILABLE"
|
|
310
|
+
});
|
|
311
|
+
}
|
|
312
|
+
const session = await this.paymentProvider.createCheckoutSession({
|
|
313
|
+
pendingRegistrationId: pending.id,
|
|
314
|
+
planId: pending.selectedPlanId,
|
|
315
|
+
email: pending.email,
|
|
316
|
+
successUrl: input.successUrl,
|
|
317
|
+
cancelUrl: input.cancelUrl
|
|
318
|
+
});
|
|
319
|
+
const now = /* @__PURE__ */ new Date();
|
|
320
|
+
const expiresAt = new Date(now.getTime() + import_types2.PENDING_CHECKOUT_TTL_DAYS * 24 * 60 * 60 * 1e3);
|
|
321
|
+
const updated = await this.repo.update(pending.id, {
|
|
322
|
+
status: "CHECKOUT_STARTED",
|
|
323
|
+
currentStep: 4,
|
|
324
|
+
checkoutSessionId: session.sessionId,
|
|
325
|
+
checkoutStartedAt: now,
|
|
326
|
+
expiresAt
|
|
327
|
+
});
|
|
328
|
+
await this.record("CHECKOUT_STARTED", updated.id, context, {
|
|
329
|
+
sessionId: session.sessionId,
|
|
330
|
+
provider: session.provider
|
|
331
|
+
});
|
|
332
|
+
return {
|
|
333
|
+
pendingRegistrationId: updated.id,
|
|
334
|
+
status: updated.status,
|
|
335
|
+
nextStep: import_types2.REGISTRATION_STEP_BY_STATUS[updated.status],
|
|
336
|
+
checkoutSessionId: session.sessionId,
|
|
337
|
+
checkoutUrl: session.checkoutUrl
|
|
338
|
+
};
|
|
339
|
+
}
|
|
340
|
+
/**
|
|
341
|
+
* Processes a payment webhook idempotently:
|
|
342
|
+
* 1. `tryClaim` pins the event ID via a @unique INSERT. Duplicates are
|
|
343
|
+
* dropped silently (`ALREADY_PROCESSED`).
|
|
344
|
+
* 2. On status `SUCCEEDED` the PendingRegistration is resolved from the
|
|
345
|
+
* checkout session and the ActivationOrchestrator triggers the final
|
|
346
|
+
* user+tenant+subscription creation in a single transaction.
|
|
347
|
+
* 3. After activation the PendingRegistration is deleted.
|
|
348
|
+
*
|
|
349
|
+
* On errors in step 2/3 the EventLog entry remains — provider retries then
|
|
350
|
+
* run into ALREADY_PROCESSED. Operational repair is done manually (status
|
|
351
|
+
* inspection + cleanup).
|
|
352
|
+
*/
|
|
353
|
+
async handlePaymentEvent(input, context) {
|
|
354
|
+
const claimed = await this.paymentEventLog.tryClaim(input.eventId, {
|
|
355
|
+
provider: input.provider,
|
|
356
|
+
sessionId: input.sessionId,
|
|
357
|
+
status: input.status,
|
|
358
|
+
rawPayload: input.rawPayload
|
|
359
|
+
});
|
|
360
|
+
if (!claimed) {
|
|
361
|
+
this.logger.warn(`Idempotenz: PaymentEvent ${input.eventId} bereits verarbeitet \u2014 Duplikat verworfen.`);
|
|
362
|
+
await this.record("PAYMENT_DUPLICATE_IGNORED", null, context, {
|
|
363
|
+
eventId: input.eventId,
|
|
364
|
+
sessionId: input.sessionId
|
|
365
|
+
});
|
|
366
|
+
return {
|
|
367
|
+
activated: false,
|
|
368
|
+
reason: "ALREADY_PROCESSED"
|
|
369
|
+
};
|
|
370
|
+
}
|
|
371
|
+
if (input.status !== "SUCCEEDED") {
|
|
372
|
+
await this.record("PAYMENT_FAILED", null, context, {
|
|
373
|
+
eventId: input.eventId,
|
|
374
|
+
sessionId: input.sessionId
|
|
375
|
+
});
|
|
376
|
+
return {
|
|
377
|
+
activated: false,
|
|
378
|
+
reason: "PAYMENT_NOT_SUCCEEDED"
|
|
379
|
+
};
|
|
380
|
+
}
|
|
381
|
+
if (!input.sessionId) {
|
|
382
|
+
return {
|
|
383
|
+
activated: false,
|
|
384
|
+
reason: "MISSING_SESSION_ID"
|
|
385
|
+
};
|
|
386
|
+
}
|
|
387
|
+
const pending = await this.repo.findByCheckoutSession(input.sessionId);
|
|
388
|
+
if (!pending) {
|
|
389
|
+
return {
|
|
390
|
+
activated: false,
|
|
391
|
+
reason: "PENDING_REGISTRATION_NOT_FOUND"
|
|
392
|
+
};
|
|
393
|
+
}
|
|
394
|
+
if (pending.status !== "CHECKOUT_STARTED" && pending.status !== "PLAN_SELECTED") {
|
|
395
|
+
return {
|
|
396
|
+
activated: false,
|
|
397
|
+
reason: "INVALID_STATE"
|
|
398
|
+
};
|
|
399
|
+
}
|
|
400
|
+
await this.record("PAYMENT_RECEIVED", pending.id, context, {
|
|
401
|
+
eventId: input.eventId,
|
|
402
|
+
sessionId: input.sessionId
|
|
403
|
+
});
|
|
404
|
+
const result = await this.activationOrchestrator.activate(pending);
|
|
405
|
+
await this.repo.delete(pending.id);
|
|
406
|
+
await this.record("ACTIVATION_COMPLETED", pending.id, context, {
|
|
407
|
+
userId: result.userId,
|
|
408
|
+
tenantId: result.tenantId,
|
|
409
|
+
subscriptionId: result.subscriptionId
|
|
410
|
+
});
|
|
411
|
+
this.logger.log(`Aktivierung erfolgreich: pending=${pending.id} \u2192 user=${result.userId} tenant=${result.tenantId} subscription=${result.subscriptionId}`);
|
|
412
|
+
return {
|
|
413
|
+
activated: true,
|
|
414
|
+
result
|
|
415
|
+
};
|
|
416
|
+
}
|
|
417
|
+
/**
|
|
418
|
+
* Deletes all PendingRegistration records with `expiresAt < now`.
|
|
419
|
+
* Full deletion (no anonymization path) is the simpler option: the schema
|
|
420
|
+
* holds `email`, `firstName`, `lastName`, `passwordHash` as NOT NULL, and
|
|
421
|
+
* the unique email is exactly what needs to become free again after expiry.
|
|
422
|
+
*
|
|
423
|
+
* The batch `limit` guards against memory spikes on large backlogs — the
|
|
424
|
+
* caller (cron) keeps calling until `moreAvailable === false`.
|
|
425
|
+
*/
|
|
426
|
+
async runCleanup(now, limit = 500) {
|
|
427
|
+
if (limit <= 0) {
|
|
428
|
+
return {
|
|
429
|
+
deleted: 0,
|
|
430
|
+
moreAvailable: false
|
|
431
|
+
};
|
|
432
|
+
}
|
|
433
|
+
const expired = await this.repo.findExpired(now, limit);
|
|
434
|
+
for (const pending of expired) {
|
|
435
|
+
try {
|
|
436
|
+
await this.repo.delete(pending.id);
|
|
437
|
+
} catch (error) {
|
|
438
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
439
|
+
this.logger.warn(`Cleanup: PendingRegistration ${pending.id} konnte nicht geloescht werden (${message}).`);
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
if (expired.length > 0) {
|
|
443
|
+
this.logger.log(`Cleanup: ${expired.length} abgelaufene PendingRegistrations geloescht.`);
|
|
444
|
+
}
|
|
445
|
+
return {
|
|
446
|
+
deleted: expired.length,
|
|
447
|
+
moreAvailable: expired.length >= limit
|
|
448
|
+
};
|
|
449
|
+
}
|
|
450
|
+
async start(input, context) {
|
|
451
|
+
const email = normalizeEmail(input.email);
|
|
452
|
+
if (await this.userLookup.hasActiveUser(email)) {
|
|
453
|
+
this.logger.log(`register/start fuer aktiven Account ignoriert (${email}).`);
|
|
454
|
+
await this.record("REGISTRATION_NEUTRAL_ACTIVE_USER", null, context);
|
|
455
|
+
return {
|
|
456
|
+
neutral: true
|
|
457
|
+
};
|
|
458
|
+
}
|
|
459
|
+
const existing = await this.repo.findByEmail(email);
|
|
460
|
+
if (existing) {
|
|
461
|
+
if (existing.expiresAt.getTime() < Date.now()) {
|
|
462
|
+
await this.repo.delete(existing.id);
|
|
463
|
+
await this.record("REGISTRATION_NEUTRAL_EXPIRED", existing.id, context);
|
|
464
|
+
} else if (existing.status === "PENDING_EMAIL_VERIFICATION") {
|
|
465
|
+
await this.regenerateOtp(existing);
|
|
466
|
+
await this.record("REGISTRATION_NEUTRAL_REPLAY", existing.id, context, {
|
|
467
|
+
status: existing.status
|
|
468
|
+
});
|
|
469
|
+
return {
|
|
470
|
+
neutral: true
|
|
471
|
+
};
|
|
472
|
+
} else {
|
|
473
|
+
if (this.resumeTokenSigner && this.resumeDelivery) {
|
|
474
|
+
await this.sendResumeLink(existing);
|
|
475
|
+
} else {
|
|
476
|
+
await this.regenerateOtp(existing);
|
|
477
|
+
}
|
|
478
|
+
await this.record("REGISTRATION_NEUTRAL_REPLAY", existing.id, context, {
|
|
479
|
+
status: existing.status
|
|
480
|
+
});
|
|
481
|
+
return {
|
|
482
|
+
neutral: true
|
|
483
|
+
};
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
const slug = await this.resolveSlug(input.tenantName, input.tenantSlug);
|
|
487
|
+
const passwordHash = await this.passwordHasher.hash(input.password);
|
|
488
|
+
const code = generateOtpCode();
|
|
489
|
+
const otpHash = hashOtpCode(code);
|
|
490
|
+
const now = /* @__PURE__ */ new Date();
|
|
491
|
+
const otpExpiresAt = new Date(now.getTime() + import_types2.OTP_TTL_MINUTES * 60 * 1e3);
|
|
492
|
+
const expiresAt = new Date(now.getTime() + import_types2.PENDING_EMAIL_TTL_HOURS * 60 * 60 * 1e3);
|
|
493
|
+
const pending = await this.repo.create({
|
|
494
|
+
tenantName: input.tenantName.trim(),
|
|
495
|
+
tenantSlug: slug,
|
|
496
|
+
salutation: nullable(input.salutation),
|
|
497
|
+
firstName: input.firstName.trim(),
|
|
498
|
+
lastName: input.lastName.trim(),
|
|
499
|
+
email,
|
|
500
|
+
passwordHash,
|
|
501
|
+
locale: input.locale ?? "de",
|
|
502
|
+
otpHash,
|
|
503
|
+
otpExpiresAt,
|
|
504
|
+
expiresAt
|
|
505
|
+
});
|
|
506
|
+
await this.deliverOtp(pending, code);
|
|
507
|
+
await this.record("REGISTRATION_STARTED", pending.id, context);
|
|
508
|
+
return {
|
|
509
|
+
neutral: true
|
|
510
|
+
};
|
|
511
|
+
}
|
|
512
|
+
async verifyOtp(emailRaw, code, context) {
|
|
513
|
+
const email = normalizeEmail(emailRaw);
|
|
514
|
+
const pending = await this.repo.findByEmail(email);
|
|
515
|
+
if (!pending) {
|
|
516
|
+
await this.record("OTP_VERIFY_FAILED", null, context, {
|
|
517
|
+
reason: "unknown_email"
|
|
518
|
+
});
|
|
519
|
+
throw new import_common.BadRequestException({
|
|
520
|
+
code: "OTP_INVALID"
|
|
521
|
+
});
|
|
522
|
+
}
|
|
523
|
+
if (pending.status !== "PENDING_EMAIL_VERIFICATION") {
|
|
524
|
+
return {
|
|
525
|
+
status: pending.status,
|
|
526
|
+
nextStep: import_types2.REGISTRATION_STEP_BY_STATUS[pending.status],
|
|
527
|
+
pendingRegistrationId: pending.id
|
|
528
|
+
};
|
|
529
|
+
}
|
|
530
|
+
if (!pending.otpHash || !pending.otpExpiresAt) {
|
|
531
|
+
await this.record("OTP_VERIFY_FAILED", pending.id, context, {
|
|
532
|
+
reason: "no_otp_set"
|
|
533
|
+
});
|
|
534
|
+
throw new import_common.BadRequestException({
|
|
535
|
+
code: "OTP_INVALID"
|
|
536
|
+
});
|
|
537
|
+
}
|
|
538
|
+
if (pending.otpExpiresAt.getTime() < Date.now()) {
|
|
539
|
+
await this.record("OTP_VERIFY_FAILED", pending.id, context, {
|
|
540
|
+
reason: "expired"
|
|
541
|
+
});
|
|
542
|
+
throw new import_common.BadRequestException({
|
|
543
|
+
code: "OTP_EXPIRED"
|
|
544
|
+
});
|
|
545
|
+
}
|
|
546
|
+
const maxAttempts = resolveOtpVerifyMaxAttempts();
|
|
547
|
+
if (pending.otpAttemptCount >= maxAttempts) {
|
|
548
|
+
await this.record("OTP_VERIFY_FAILED", pending.id, context, {
|
|
549
|
+
reason: "locked"
|
|
550
|
+
});
|
|
551
|
+
throw new import_common.BadRequestException({
|
|
552
|
+
code: "OTP_LOCKED"
|
|
553
|
+
});
|
|
554
|
+
}
|
|
555
|
+
const attemptCount = await this.repo.incrementOtpAttemptCount(pending.id);
|
|
556
|
+
if (attemptCount > maxAttempts) {
|
|
557
|
+
await this.record("OTP_VERIFY_FAILED", pending.id, context, {
|
|
558
|
+
reason: "locked"
|
|
559
|
+
});
|
|
560
|
+
throw new import_common.BadRequestException({
|
|
561
|
+
code: "OTP_LOCKED"
|
|
562
|
+
});
|
|
563
|
+
}
|
|
564
|
+
if (!verifyOtpCode(pending.otpHash, code)) {
|
|
565
|
+
await this.record("OTP_VERIFY_FAILED", pending.id, context, {
|
|
566
|
+
reason: "wrong_code"
|
|
567
|
+
});
|
|
568
|
+
throw new import_common.BadRequestException({
|
|
569
|
+
code: "OTP_INVALID"
|
|
570
|
+
});
|
|
571
|
+
}
|
|
572
|
+
const now = /* @__PURE__ */ new Date();
|
|
573
|
+
const updated = await this.repo.update(pending.id, {
|
|
574
|
+
status: "EMAIL_VERIFIED",
|
|
575
|
+
currentStep: 3,
|
|
576
|
+
emailVerifiedAt: now,
|
|
577
|
+
otpHash: null,
|
|
578
|
+
otpExpiresAt: null,
|
|
579
|
+
expiresAt: new Date(now.getTime() + import_types2.PENDING_ONBOARDING_TTL_DAYS * 24 * 60 * 60 * 1e3)
|
|
580
|
+
});
|
|
581
|
+
await this.record("OTP_VERIFIED", updated.id, context);
|
|
582
|
+
return {
|
|
583
|
+
status: updated.status,
|
|
584
|
+
nextStep: import_types2.REGISTRATION_STEP_BY_STATUS[updated.status],
|
|
585
|
+
pendingRegistrationId: updated.id
|
|
586
|
+
};
|
|
587
|
+
}
|
|
588
|
+
async resendOtp(emailRaw, context) {
|
|
589
|
+
const email = normalizeEmail(emailRaw);
|
|
590
|
+
const pending = await this.repo.findByEmail(email);
|
|
591
|
+
if (!pending) {
|
|
592
|
+
return {
|
|
593
|
+
neutral: true
|
|
594
|
+
};
|
|
595
|
+
}
|
|
596
|
+
if (pending.status !== "PENDING_EMAIL_VERIFICATION") {
|
|
597
|
+
return {
|
|
598
|
+
neutral: true
|
|
599
|
+
};
|
|
600
|
+
}
|
|
601
|
+
await this.regenerateOtp(pending);
|
|
602
|
+
await this.record("OTP_RESEND_REQUESTED", pending.id, context);
|
|
603
|
+
return {
|
|
604
|
+
neutral: true
|
|
605
|
+
};
|
|
606
|
+
}
|
|
607
|
+
async regenerateOtp(pending) {
|
|
608
|
+
const now = /* @__PURE__ */ new Date();
|
|
609
|
+
const windowMs = import_types2.OTP_RATE_LIMIT_WINDOW_MINUTES * 60 * 1e3;
|
|
610
|
+
const withinWindow = pending.lastOtpSentAt !== null && now.getTime() - pending.lastOtpSentAt.getTime() < windowMs;
|
|
611
|
+
if (withinWindow && pending.otpSendCount >= import_types2.OTP_RATE_LIMIT_MAX_SENDS) {
|
|
612
|
+
this.logger.warn(`OTP-Rate-Limit erreicht fuer ${pending.email} \u2014 Send wird still verworfen.`);
|
|
613
|
+
return;
|
|
614
|
+
}
|
|
615
|
+
const code = generateOtpCode();
|
|
616
|
+
const otpHash = hashOtpCode(code);
|
|
617
|
+
const otpExpiresAt = new Date(now.getTime() + import_types2.OTP_TTL_MINUTES * 60 * 1e3);
|
|
618
|
+
const otpSendCount = withinWindow ? pending.otpSendCount + 1 : 1;
|
|
619
|
+
await this.repo.update(pending.id, {
|
|
620
|
+
otpHash,
|
|
621
|
+
otpExpiresAt,
|
|
622
|
+
otpSendCount,
|
|
623
|
+
lastOtpSentAt: now,
|
|
624
|
+
// New code = fresh attempt budget. Overall throughput stays capped
|
|
625
|
+
// by the send rate limit above.
|
|
626
|
+
otpAttemptCount: 0
|
|
627
|
+
});
|
|
628
|
+
await this.deliverOtp(pending, code);
|
|
629
|
+
}
|
|
630
|
+
async deliverOtp(pending, code) {
|
|
631
|
+
try {
|
|
632
|
+
await this.otpDelivery.sendVerificationOtp({
|
|
633
|
+
to: pending.email,
|
|
634
|
+
code,
|
|
635
|
+
firstName: pending.firstName,
|
|
636
|
+
locale: pending.locale
|
|
637
|
+
});
|
|
638
|
+
} catch (error) {
|
|
639
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
640
|
+
this.logger.error(`OTP-Mail an ${pending.email} fehlgeschlagen: ${message}`);
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
/**
|
|
644
|
+
* Creates a signed resume token (60 min TTL) and sends a magic link to the
|
|
645
|
+
* stored email. Mail send errors are logged but not escalated
|
|
646
|
+
* (account-enumeration protection).
|
|
647
|
+
*/
|
|
648
|
+
async sendResumeLink(pending) {
|
|
649
|
+
if (!this.resumeTokenSigner || !this.resumeDelivery) {
|
|
650
|
+
return;
|
|
651
|
+
}
|
|
652
|
+
const token = await this.resumeTokenSigner.sign({
|
|
653
|
+
pendingRegistrationId: pending.id,
|
|
654
|
+
ttlMinutes: import_types.REGISTRATION_RESUME_TTL_MINUTES
|
|
655
|
+
});
|
|
656
|
+
const base = (this.resumeBaseUrl ?? "http://localhost").replace(/\/+$/, "");
|
|
657
|
+
const url = `${base}/login?resume=${encodeURIComponent(token)}`;
|
|
658
|
+
try {
|
|
659
|
+
await this.resumeDelivery.sendResumeEmail({
|
|
660
|
+
to: pending.email,
|
|
661
|
+
firstName: pending.firstName,
|
|
662
|
+
locale: pending.locale,
|
|
663
|
+
resumeUrl: url
|
|
664
|
+
});
|
|
665
|
+
} catch (error) {
|
|
666
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
667
|
+
this.logger.error(`Resume-Mail an ${pending.email} fehlgeschlagen: ${message}`);
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
/**
|
|
671
|
+
* Resolves a resume token to the matching onboarding step. Called by the
|
|
672
|
+
* frontend from `/register?resume=<jwt>`. On an invalid token the method
|
|
673
|
+
* throws `RESUME_TOKEN_INVALID`.
|
|
674
|
+
*
|
|
675
|
+
* Also returns a public-safe snapshot of the PendingRegistration so the
|
|
676
|
+
* frontend can prefill completed steps with the existing data (first name /
|
|
677
|
+
* last name, organization name, plan selection ...).
|
|
678
|
+
*/
|
|
679
|
+
async resumeWithToken(input, context) {
|
|
680
|
+
if (!this.resumeTokenSigner) {
|
|
681
|
+
throw new import_common.BadRequestException({
|
|
682
|
+
code: "RESUME_NOT_CONFIGURED"
|
|
683
|
+
});
|
|
684
|
+
}
|
|
685
|
+
let decoded;
|
|
686
|
+
try {
|
|
687
|
+
decoded = await this.resumeTokenSigner.verify(input.token);
|
|
688
|
+
} catch {
|
|
689
|
+
throw new import_common.BadRequestException({
|
|
690
|
+
code: "RESUME_TOKEN_INVALID"
|
|
691
|
+
});
|
|
692
|
+
}
|
|
693
|
+
const pending = await this.repo.findById(decoded.pendingRegistrationId);
|
|
694
|
+
if (!pending) {
|
|
695
|
+
throw new import_common.BadRequestException({
|
|
696
|
+
code: "RESUME_TOKEN_INVALID"
|
|
697
|
+
});
|
|
698
|
+
}
|
|
699
|
+
if (pending.expiresAt.getTime() < Date.now()) {
|
|
700
|
+
throw new import_common.BadRequestException({
|
|
701
|
+
code: "PENDING_REGISTRATION_EXPIRED"
|
|
702
|
+
});
|
|
703
|
+
}
|
|
704
|
+
await this.record("REGISTRATION_NEUTRAL_REPLAY", pending.id, context, {
|
|
705
|
+
via: "resume_token",
|
|
706
|
+
status: pending.status
|
|
707
|
+
});
|
|
708
|
+
return {
|
|
709
|
+
pendingRegistrationId: pending.id,
|
|
710
|
+
status: pending.status,
|
|
711
|
+
nextStep: import_types2.REGISTRATION_STEP_BY_STATUS[pending.status],
|
|
712
|
+
snapshot: toSnapshot(pending)
|
|
713
|
+
};
|
|
714
|
+
}
|
|
715
|
+
/**
|
|
716
|
+
* Signs a short-lived resume token for an existing PendingRegistration —
|
|
717
|
+
* the login endpoint uses this to send the user into the wizard with
|
|
718
|
+
* prefill after successful password verification.
|
|
719
|
+
*/
|
|
720
|
+
async signResumeToken(pendingRegistrationId) {
|
|
721
|
+
if (!this.resumeTokenSigner) return null;
|
|
722
|
+
return this.resumeTokenSigner.sign({
|
|
723
|
+
pendingRegistrationId
|
|
724
|
+
});
|
|
725
|
+
}
|
|
726
|
+
/* ─── Configurator (Step 3) ────────────────────────────────────────── */
|
|
727
|
+
/** Returns the (app-specific) configurator catalog. */
|
|
728
|
+
async getConfiguratorCatalog() {
|
|
729
|
+
if (!this.configuratorLookup) {
|
|
730
|
+
throw new import_common.BadRequestException({
|
|
731
|
+
code: "CONFIGURATOR_NOT_CONFIGURED"
|
|
732
|
+
});
|
|
733
|
+
}
|
|
734
|
+
return this.configuratorLookup.getCatalog();
|
|
735
|
+
}
|
|
736
|
+
/**
|
|
737
|
+
* Saves the configurator selection (model + cycle + promo) and returns the
|
|
738
|
+
* full price breakdown.
|
|
739
|
+
*
|
|
740
|
+
* Sets:
|
|
741
|
+
* - `status = PLAN_SELECTED`
|
|
742
|
+
* - `currentStep = 4`
|
|
743
|
+
* - `selectedPlanId = <planId from the chosen model>`
|
|
744
|
+
* - `configJson` / `billingCycle` / `appliedPromoCode`
|
|
745
|
+
* - `expiresAt = now + ONBOARDING_TTL`
|
|
746
|
+
*/
|
|
747
|
+
async saveConfiguration(input, context) {
|
|
748
|
+
if (!this.configuratorLookup) {
|
|
749
|
+
throw new import_common.BadRequestException({
|
|
750
|
+
code: "CONFIGURATOR_NOT_CONFIGURED"
|
|
751
|
+
});
|
|
752
|
+
}
|
|
753
|
+
const pending = await this.repo.findById(input.pendingRegistrationId);
|
|
754
|
+
if (!pending) {
|
|
755
|
+
throw new import_common.NotFoundException({
|
|
756
|
+
code: "PENDING_REGISTRATION_NOT_FOUND"
|
|
757
|
+
});
|
|
758
|
+
}
|
|
759
|
+
if (pending.expiresAt.getTime() < Date.now()) {
|
|
760
|
+
throw new import_common.BadRequestException({
|
|
761
|
+
code: "PENDING_REGISTRATION_EXPIRED"
|
|
762
|
+
});
|
|
763
|
+
}
|
|
764
|
+
if (pending.status !== "EMAIL_VERIFIED" && pending.status !== "PLAN_SELECTED" && pending.status !== "CHECKOUT_STARTED") {
|
|
765
|
+
throw new import_common.BadRequestException({
|
|
766
|
+
code: "INVALID_REGISTRATION_STATE"
|
|
767
|
+
});
|
|
768
|
+
}
|
|
769
|
+
const catalog = await this.configuratorLookup.getCatalog();
|
|
770
|
+
const model = catalog.models.find((m) => m.id === input.selection.modelId);
|
|
771
|
+
if (!model) {
|
|
772
|
+
throw new import_common.BadRequestException({
|
|
773
|
+
code: "MODEL_NOT_AVAILABLE"
|
|
774
|
+
});
|
|
775
|
+
}
|
|
776
|
+
if (input.selection.businessTypeVersionId && this.businessTypeLookup) {
|
|
777
|
+
const view = await this.businessTypeLookup.findPublishedVersion(input.selection.businessTypeVersionId);
|
|
778
|
+
if (!view) {
|
|
779
|
+
throw new import_common.BadRequestException({
|
|
780
|
+
code: "BUSINESS_TYPE_NOT_AVAILABLE"
|
|
781
|
+
});
|
|
782
|
+
}
|
|
783
|
+
}
|
|
784
|
+
let promoEval;
|
|
785
|
+
if (input.selection.appliedPromoCode && this.promoPreview) {
|
|
786
|
+
const dryRun = computeBreakdown(catalog, {
|
|
787
|
+
...input.selection,
|
|
788
|
+
appliedPromoCode: null
|
|
789
|
+
});
|
|
790
|
+
const previewResult = await this.promoPreview.preview({
|
|
791
|
+
code: input.selection.appliedPromoCode,
|
|
792
|
+
planId: model.planId,
|
|
793
|
+
billingCycle: input.selection.billingCycle,
|
|
794
|
+
subtotalGross: dryRun.totalGross,
|
|
795
|
+
email: pending.email
|
|
796
|
+
});
|
|
797
|
+
if (previewResult.valid && previewResult.discountAmount && previewResult.percent !== void 0 && previewResult.label) {
|
|
798
|
+
promoEval = {
|
|
799
|
+
discountAmount: previewResult.discountAmount,
|
|
800
|
+
percent: previewResult.percent,
|
|
801
|
+
label: previewResult.label
|
|
802
|
+
};
|
|
803
|
+
}
|
|
804
|
+
}
|
|
805
|
+
const breakdown = computeBreakdown(catalog, input.selection, promoEval);
|
|
806
|
+
if (promoEval) {
|
|
807
|
+
breakdown.appliedPromo = {
|
|
808
|
+
code: input.selection.appliedPromoCode,
|
|
809
|
+
label: promoEval.label,
|
|
810
|
+
percent: promoEval.percent
|
|
811
|
+
};
|
|
812
|
+
}
|
|
813
|
+
const now = /* @__PURE__ */ new Date();
|
|
814
|
+
const expiresAt = new Date(now.getTime() + import_types2.PENDING_ONBOARDING_TTL_DAYS * 24 * 60 * 60 * 1e3);
|
|
815
|
+
const updated = await this.repo.update(pending.id, {
|
|
816
|
+
status: "PLAN_SELECTED",
|
|
817
|
+
currentStep: 4,
|
|
818
|
+
selectedPlanId: model.planId,
|
|
819
|
+
configJson: input.selection,
|
|
820
|
+
billingCycle: input.selection.billingCycle,
|
|
821
|
+
appliedPromoCode: input.selection.appliedPromoCode ?? null,
|
|
822
|
+
expiresAt
|
|
823
|
+
});
|
|
824
|
+
await this.record("PLAN_SELECTED", updated.id, context, {
|
|
825
|
+
modelId: model.id,
|
|
826
|
+
planId: model.planId,
|
|
827
|
+
cycle: input.selection.billingCycle,
|
|
828
|
+
hasPromo: !!promoEval
|
|
829
|
+
});
|
|
830
|
+
return {
|
|
831
|
+
pendingRegistrationId: updated.id,
|
|
832
|
+
status: updated.status,
|
|
833
|
+
nextStep: import_types2.REGISTRATION_STEP_BY_STATUS[updated.status],
|
|
834
|
+
selection: input.selection,
|
|
835
|
+
breakdown
|
|
836
|
+
};
|
|
837
|
+
}
|
|
838
|
+
/**
|
|
839
|
+
* Validates a promo code against the currently stored configuration and
|
|
840
|
+
* returns a new breakdown. Does NOT modify the pending — for live preview
|
|
841
|
+
* in the frontend. If `null` is passed as `code`, the promo effect is
|
|
842
|
+
* removed.
|
|
843
|
+
*/
|
|
844
|
+
async previewConfigPromo(pendingRegistrationId, code) {
|
|
845
|
+
if (!this.configuratorLookup) {
|
|
846
|
+
throw new import_common.BadRequestException({
|
|
847
|
+
code: "CONFIGURATOR_NOT_CONFIGURED"
|
|
848
|
+
});
|
|
849
|
+
}
|
|
850
|
+
const pending = await this.repo.findById(pendingRegistrationId);
|
|
851
|
+
if (!pending) {
|
|
852
|
+
throw new import_common.NotFoundException({
|
|
853
|
+
code: "PENDING_REGISTRATION_NOT_FOUND"
|
|
854
|
+
});
|
|
855
|
+
}
|
|
856
|
+
const config = pending.configJson;
|
|
857
|
+
if (!config) {
|
|
858
|
+
throw new import_common.BadRequestException({
|
|
859
|
+
code: "CONFIG_NOT_SAVED"
|
|
860
|
+
});
|
|
861
|
+
}
|
|
862
|
+
const catalog = await this.configuratorLookup.getCatalog();
|
|
863
|
+
const model = catalog.models.find((m) => m.id === config.modelId);
|
|
864
|
+
if (!model) {
|
|
865
|
+
throw new import_common.BadRequestException({
|
|
866
|
+
code: "MODEL_NOT_AVAILABLE"
|
|
867
|
+
});
|
|
868
|
+
}
|
|
869
|
+
let promoEval;
|
|
870
|
+
if (code && this.promoPreview) {
|
|
871
|
+
const dryRun = computeBreakdown(catalog, {
|
|
872
|
+
...config,
|
|
873
|
+
appliedPromoCode: null
|
|
874
|
+
});
|
|
875
|
+
const previewResult = await this.promoPreview.preview({
|
|
876
|
+
code,
|
|
877
|
+
planId: model.planId,
|
|
878
|
+
billingCycle: config.billingCycle,
|
|
879
|
+
subtotalGross: dryRun.totalGross,
|
|
880
|
+
email: pending.email
|
|
881
|
+
});
|
|
882
|
+
if (previewResult.valid && previewResult.discountAmount && previewResult.percent !== void 0 && previewResult.label) {
|
|
883
|
+
promoEval = {
|
|
884
|
+
discountAmount: previewResult.discountAmount,
|
|
885
|
+
percent: previewResult.percent,
|
|
886
|
+
label: previewResult.label
|
|
887
|
+
};
|
|
888
|
+
}
|
|
889
|
+
}
|
|
890
|
+
const breakdown = computeBreakdown(catalog, config, promoEval);
|
|
891
|
+
if (promoEval && code) {
|
|
892
|
+
breakdown.appliedPromo = {
|
|
893
|
+
code,
|
|
894
|
+
label: promoEval.label,
|
|
895
|
+
percent: promoEval.percent
|
|
896
|
+
};
|
|
897
|
+
}
|
|
898
|
+
return breakdown;
|
|
899
|
+
}
|
|
900
|
+
/**
|
|
901
|
+
* Helper for step 3 resume: the frontend needs the current breakdown when
|
|
902
|
+
* the pending already has a configuration. Pure recomputation against the
|
|
903
|
+
* catalog (without promo validation, since promos should be freshly checked
|
|
904
|
+
* anyway).
|
|
905
|
+
*/
|
|
906
|
+
async getCurrentBreakdown(pendingRegistrationId) {
|
|
907
|
+
if (!this.configuratorLookup) return null;
|
|
908
|
+
const pending = await this.repo.findById(pendingRegistrationId);
|
|
909
|
+
if (!pending || !pending.configJson) return null;
|
|
910
|
+
const catalog = await this.configuratorLookup.getCatalog();
|
|
911
|
+
return computeBreakdown(catalog, pending.configJson);
|
|
912
|
+
}
|
|
913
|
+
async resolveSlug(tenantName, requested) {
|
|
914
|
+
const requestedSlug = requested?.trim().toLowerCase() ?? "";
|
|
915
|
+
const base = requestedSlug.length > 0 ? requestedSlug : slugify(tenantName);
|
|
916
|
+
if (await this.slugCheck.isSlugAvailable(base)) {
|
|
917
|
+
return base;
|
|
918
|
+
}
|
|
919
|
+
for (let i = 2; i < 1e3; i++) {
|
|
920
|
+
const candidate = `${base}-${i}`;
|
|
921
|
+
if (await this.slugCheck.isSlugAvailable(candidate)) {
|
|
922
|
+
return candidate;
|
|
923
|
+
}
|
|
924
|
+
}
|
|
925
|
+
throw new Error(`Konnte keinen freien Slug fuer "${tenantName}" finden.`);
|
|
926
|
+
}
|
|
927
|
+
};
|
|
928
|
+
PendingRegistrationService = _ts_decorate([
|
|
929
|
+
(0, import_common.Injectable)(),
|
|
930
|
+
_ts_param(0, (0, import_common.Inject)(PENDING_REGISTRATION_REPOSITORY_TOKEN)),
|
|
931
|
+
_ts_param(1, (0, import_common.Inject)(REGISTRATION_OTP_DELIVERY_TOKEN)),
|
|
932
|
+
_ts_param(2, (0, import_common.Inject)(USER_ACCOUNT_LOOKUP_TOKEN)),
|
|
933
|
+
_ts_param(3, (0, import_common.Inject)(SLUG_AVAILABILITY_CHECK_TOKEN)),
|
|
934
|
+
_ts_param(4, (0, import_common.Inject)(PASSWORD_HASHER_TOKEN)),
|
|
935
|
+
_ts_param(5, (0, import_common.Inject)(PLAN_CATALOG_LOOKUP_TOKEN)),
|
|
936
|
+
_ts_param(6, (0, import_common.Inject)(PAYMENT_PROVIDER_TOKEN)),
|
|
937
|
+
_ts_param(7, (0, import_common.Inject)(PAYMENT_EVENT_LOG_TOKEN)),
|
|
938
|
+
_ts_param(8, (0, import_common.Inject)(ACTIVATION_ORCHESTRATOR_TOKEN)),
|
|
939
|
+
_ts_param(9, (0, import_common.Inject)(REGISTRATION_AUDIT_LOGGER_TOKEN)),
|
|
940
|
+
_ts_param(10, (0, import_common.Optional)()),
|
|
941
|
+
_ts_param(10, (0, import_common.Inject)(REGISTRATION_RESUME_TOKEN_SIGNER_TOKEN)),
|
|
942
|
+
_ts_param(11, (0, import_common.Optional)()),
|
|
943
|
+
_ts_param(11, (0, import_common.Inject)(REGISTRATION_RESUME_DELIVERY_TOKEN)),
|
|
944
|
+
_ts_param(12, (0, import_common.Optional)()),
|
|
945
|
+
_ts_param(12, (0, import_common.Inject)(REGISTRATION_RESUME_BASE_URL_TOKEN)),
|
|
946
|
+
_ts_param(13, (0, import_common.Optional)()),
|
|
947
|
+
_ts_param(13, (0, import_common.Inject)(REGISTRATION_CONFIGURATOR_LOOKUP_TOKEN)),
|
|
948
|
+
_ts_param(14, (0, import_common.Optional)()),
|
|
949
|
+
_ts_param(14, (0, import_common.Inject)(REGISTRATION_PROMO_PREVIEW_TOKEN)),
|
|
950
|
+
_ts_param(15, (0, import_common.Optional)()),
|
|
951
|
+
_ts_param(15, (0, import_common.Inject)(REGISTRATION_BUSINESS_TYPE_LOOKUP_TOKEN)),
|
|
952
|
+
_ts_metadata("design:type", Function),
|
|
953
|
+
_ts_metadata("design:paramtypes", [
|
|
954
|
+
typeof PendingRegistrationRepository === "undefined" ? Object : PendingRegistrationRepository,
|
|
955
|
+
typeof RegistrationOtpDelivery === "undefined" ? Object : RegistrationOtpDelivery,
|
|
956
|
+
typeof UserAccountLookup === "undefined" ? Object : UserAccountLookup,
|
|
957
|
+
typeof SlugAvailabilityCheck === "undefined" ? Object : SlugAvailabilityCheck,
|
|
958
|
+
typeof PasswordHasher === "undefined" ? Object : PasswordHasher,
|
|
959
|
+
typeof PlanCatalogLookup === "undefined" ? Object : PlanCatalogLookup,
|
|
960
|
+
typeof PaymentProvider === "undefined" ? Object : PaymentProvider,
|
|
961
|
+
typeof PaymentEventLog === "undefined" ? Object : PaymentEventLog,
|
|
962
|
+
typeof ActivationOrchestrator === "undefined" ? Object : ActivationOrchestrator,
|
|
963
|
+
typeof RegistrationAuditLogger === "undefined" ? Object : RegistrationAuditLogger,
|
|
964
|
+
typeof RegistrationResumeTokenSigner === "undefined" ? Object : RegistrationResumeTokenSigner,
|
|
965
|
+
typeof RegistrationResumeDelivery === "undefined" ? Object : RegistrationResumeDelivery,
|
|
966
|
+
String,
|
|
967
|
+
typeof RegistrationConfiguratorLookup === "undefined" ? Object : RegistrationConfiguratorLookup,
|
|
968
|
+
typeof RegistrationPromoPreview === "undefined" ? Object : RegistrationPromoPreview,
|
|
969
|
+
typeof RegistrationBusinessTypeLookup === "undefined" ? Object : RegistrationBusinessTypeLookup
|
|
970
|
+
])
|
|
971
|
+
], PendingRegistrationService);
|
|
972
|
+
function normalizeEmail(value) {
|
|
973
|
+
return value.trim().toLowerCase();
|
|
974
|
+
}
|
|
975
|
+
__name(normalizeEmail, "normalizeEmail");
|
|
976
|
+
function resolveOtpVerifyMaxAttempts() {
|
|
977
|
+
const raw = process.env.SAAS_PLATFORM_OTP_VERIFY_MAX_ATTEMPTS;
|
|
978
|
+
if (!raw) return import_types2.OTP_VERIFY_MAX_ATTEMPTS;
|
|
979
|
+
const parsed = Number.parseInt(raw, 10);
|
|
980
|
+
return Number.isInteger(parsed) && parsed > 0 ? parsed : import_types2.OTP_VERIFY_MAX_ATTEMPTS;
|
|
981
|
+
}
|
|
982
|
+
__name(resolveOtpVerifyMaxAttempts, "resolveOtpVerifyMaxAttempts");
|
|
983
|
+
function toSnapshot(pending) {
|
|
984
|
+
return {
|
|
985
|
+
tenantName: pending.tenantName,
|
|
986
|
+
tenantSlug: pending.tenantSlug,
|
|
987
|
+
salutation: pending.salutation,
|
|
988
|
+
firstName: pending.firstName,
|
|
989
|
+
lastName: pending.lastName,
|
|
990
|
+
email: pending.email,
|
|
991
|
+
locale: pending.locale,
|
|
992
|
+
status: pending.status,
|
|
993
|
+
currentStep: pending.currentStep,
|
|
994
|
+
emailVerifiedAt: pending.emailVerifiedAt?.toISOString() ?? null,
|
|
995
|
+
selectedPlanId: pending.selectedPlanId,
|
|
996
|
+
config: pending.configJson,
|
|
997
|
+
billingCycle: pending.billingCycle,
|
|
998
|
+
appliedPromoCode: pending.appliedPromoCode,
|
|
999
|
+
checkoutSessionId: pending.checkoutSessionId
|
|
1000
|
+
};
|
|
1001
|
+
}
|
|
1002
|
+
__name(toSnapshot, "toSnapshot");
|
|
1003
|
+
function nullable(value) {
|
|
1004
|
+
const trimmed = value?.trim();
|
|
1005
|
+
return trimmed && trimmed.length > 0 ? trimmed : null;
|
|
1006
|
+
}
|
|
1007
|
+
__name(nullable, "nullable");
|
|
1008
|
+
|
|
1009
|
+
// src/registration/cleanup.cron.ts
|
|
1010
|
+
var import_common2 = require("@nestjs/common");
|
|
1011
|
+
var import_schedule = require("@nestjs/schedule");
|
|
1012
|
+
function _ts_decorate2(decorators, target, key, desc) {
|
|
1013
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
1014
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
1015
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
1016
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
1017
|
+
}
|
|
1018
|
+
__name(_ts_decorate2, "_ts_decorate");
|
|
1019
|
+
function _ts_metadata2(k, v) {
|
|
1020
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
1021
|
+
}
|
|
1022
|
+
__name(_ts_metadata2, "_ts_metadata");
|
|
1023
|
+
var RegistrationCleanupCron = class _RegistrationCleanupCron {
|
|
1024
|
+
static {
|
|
1025
|
+
__name(this, "RegistrationCleanupCron");
|
|
1026
|
+
}
|
|
1027
|
+
service;
|
|
1028
|
+
logger = new import_common2.Logger(_RegistrationCleanupCron.name);
|
|
1029
|
+
constructor(service) {
|
|
1030
|
+
this.service = service;
|
|
1031
|
+
}
|
|
1032
|
+
async runDaily() {
|
|
1033
|
+
await this.runCleanup();
|
|
1034
|
+
}
|
|
1035
|
+
/**
|
|
1036
|
+
* Public API: can be triggered manually from an admin endpoint /
|
|
1037
|
+
* `/admin/registration/run-cleanup` (same pattern as
|
|
1038
|
+
* `PromoCodeExpirer.run` in the consuming apps).
|
|
1039
|
+
*/
|
|
1040
|
+
async runCleanup(now = /* @__PURE__ */ new Date(), batchSize = 500) {
|
|
1041
|
+
let totalDeleted = 0;
|
|
1042
|
+
let iterations = 0;
|
|
1043
|
+
const MAX_ITERATIONS = 50;
|
|
1044
|
+
while (iterations < MAX_ITERATIONS) {
|
|
1045
|
+
const result = await this.service.runCleanup(now, batchSize);
|
|
1046
|
+
totalDeleted += result.deleted;
|
|
1047
|
+
iterations++;
|
|
1048
|
+
if (!result.moreAvailable) {
|
|
1049
|
+
break;
|
|
1050
|
+
}
|
|
1051
|
+
}
|
|
1052
|
+
if (totalDeleted > 0) {
|
|
1053
|
+
this.logger.log(`PendingRegistration-Cleanup abgeschlossen: ${totalDeleted} geloescht in ${iterations} Batches.`);
|
|
1054
|
+
}
|
|
1055
|
+
}
|
|
1056
|
+
};
|
|
1057
|
+
_ts_decorate2([
|
|
1058
|
+
(0, import_schedule.Cron)("15 4 * * *", {
|
|
1059
|
+
timeZone: "Europe/Berlin"
|
|
1060
|
+
}),
|
|
1061
|
+
_ts_metadata2("design:type", Function),
|
|
1062
|
+
_ts_metadata2("design:paramtypes", []),
|
|
1063
|
+
_ts_metadata2("design:returntype", Promise)
|
|
1064
|
+
], RegistrationCleanupCron.prototype, "runDaily", null);
|
|
1065
|
+
RegistrationCleanupCron = _ts_decorate2([
|
|
1066
|
+
(0, import_common2.Injectable)(),
|
|
1067
|
+
_ts_metadata2("design:type", Function),
|
|
1068
|
+
_ts_metadata2("design:paramtypes", [
|
|
1069
|
+
typeof PendingRegistrationService === "undefined" ? Object : PendingRegistrationService
|
|
1070
|
+
])
|
|
1071
|
+
], RegistrationCleanupCron);
|
|
1072
|
+
|
|
1073
|
+
// src/registration/ip-rate-limit.guard.ts
|
|
1074
|
+
var import_common3 = require("@nestjs/common");
|
|
1075
|
+
function _ts_decorate3(decorators, target, key, desc) {
|
|
1076
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
1077
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
1078
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
1079
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
1080
|
+
}
|
|
1081
|
+
__name(_ts_decorate3, "_ts_decorate");
|
|
1082
|
+
function _ts_metadata3(k, v) {
|
|
1083
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
1084
|
+
}
|
|
1085
|
+
__name(_ts_metadata3, "_ts_metadata");
|
|
1086
|
+
var BaseIpRateLimitGuard = class {
|
|
1087
|
+
static {
|
|
1088
|
+
__name(this, "BaseIpRateLimitGuard");
|
|
1089
|
+
}
|
|
1090
|
+
options;
|
|
1091
|
+
buckets = /* @__PURE__ */ new Map();
|
|
1092
|
+
constructor(options) {
|
|
1093
|
+
this.options = options;
|
|
1094
|
+
}
|
|
1095
|
+
canActivate(context) {
|
|
1096
|
+
if (process.env.SAAS_PLATFORM_SKIP_RATE_LIMITS === "1" && process.env.NODE_ENV !== "production") {
|
|
1097
|
+
return true;
|
|
1098
|
+
}
|
|
1099
|
+
const req = context.switchToHttp().getRequest();
|
|
1100
|
+
const key = ipFingerprint(req);
|
|
1101
|
+
if (this.exceeded(key)) {
|
|
1102
|
+
throw new import_common3.HttpException({
|
|
1103
|
+
code: "RATE_LIMITED",
|
|
1104
|
+
reason: this.options.name,
|
|
1105
|
+
retryAfterSeconds: Math.ceil(this.options.windowMs / 1e3)
|
|
1106
|
+
}, import_common3.HttpStatus.TOO_MANY_REQUESTS);
|
|
1107
|
+
}
|
|
1108
|
+
return true;
|
|
1109
|
+
}
|
|
1110
|
+
exceeded(key) {
|
|
1111
|
+
const now = Date.now();
|
|
1112
|
+
const bucket = this.buckets.get(key);
|
|
1113
|
+
if (!bucket || now - bucket.windowStart > this.options.windowMs) {
|
|
1114
|
+
this.buckets.set(key, {
|
|
1115
|
+
count: 1,
|
|
1116
|
+
windowStart: now
|
|
1117
|
+
});
|
|
1118
|
+
return false;
|
|
1119
|
+
}
|
|
1120
|
+
bucket.count += 1;
|
|
1121
|
+
return bucket.count > this.options.limit;
|
|
1122
|
+
}
|
|
1123
|
+
};
|
|
1124
|
+
BaseIpRateLimitGuard = _ts_decorate3([
|
|
1125
|
+
(0, import_common3.Injectable)(),
|
|
1126
|
+
_ts_metadata3("design:type", Function),
|
|
1127
|
+
_ts_metadata3("design:paramtypes", [
|
|
1128
|
+
Object
|
|
1129
|
+
])
|
|
1130
|
+
], BaseIpRateLimitGuard);
|
|
1131
|
+
function ipFingerprint(req) {
|
|
1132
|
+
const fwd = req.headers["x-forwarded-for"];
|
|
1133
|
+
const first = Array.isArray(fwd) ? fwd[0] : fwd?.split(",")[0]?.trim();
|
|
1134
|
+
return first || req.ip || "unknown";
|
|
1135
|
+
}
|
|
1136
|
+
__name(ipFingerprint, "ipFingerprint");
|
|
1137
|
+
|
|
1138
|
+
// src/registration/module.ts
|
|
1139
|
+
var import_common4 = require("@nestjs/common");
|
|
1140
|
+
|
|
1141
|
+
// src/core/di.ts
|
|
1142
|
+
function asProvider(token, impl) {
|
|
1143
|
+
if (typeof impl === "object" && impl !== null && "useFactory" in impl && typeof impl.useFactory === "function") {
|
|
1144
|
+
const factory = impl;
|
|
1145
|
+
return {
|
|
1146
|
+
provide: token,
|
|
1147
|
+
useFactory: factory.useFactory,
|
|
1148
|
+
inject: factory.inject
|
|
1149
|
+
};
|
|
1150
|
+
}
|
|
1151
|
+
return {
|
|
1152
|
+
provide: token,
|
|
1153
|
+
useValue: impl
|
|
1154
|
+
};
|
|
1155
|
+
}
|
|
1156
|
+
__name(asProvider, "asProvider");
|
|
1157
|
+
|
|
1158
|
+
// src/registration/module.ts
|
|
1159
|
+
function _ts_decorate4(decorators, target, key, desc) {
|
|
1160
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
1161
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
1162
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
1163
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
1164
|
+
}
|
|
1165
|
+
__name(_ts_decorate4, "_ts_decorate");
|
|
1166
|
+
var RegistrationModule = class _RegistrationModule {
|
|
1167
|
+
static {
|
|
1168
|
+
__name(this, "RegistrationModule");
|
|
1169
|
+
}
|
|
1170
|
+
static forRoot(options) {
|
|
1171
|
+
const providers = [
|
|
1172
|
+
...options.extraProviders ?? [],
|
|
1173
|
+
asProvider(PENDING_REGISTRATION_REPOSITORY_TOKEN, options.pendingRegistrationRepository),
|
|
1174
|
+
asProvider(REGISTRATION_OTP_DELIVERY_TOKEN, options.otpDelivery),
|
|
1175
|
+
asProvider(USER_ACCOUNT_LOOKUP_TOKEN, options.userAccountLookup),
|
|
1176
|
+
asProvider(SLUG_AVAILABILITY_CHECK_TOKEN, options.slugAvailabilityCheck),
|
|
1177
|
+
asProvider(PASSWORD_HASHER_TOKEN, options.passwordHasher),
|
|
1178
|
+
asProvider(PLAN_CATALOG_LOOKUP_TOKEN, options.planCatalogLookup),
|
|
1179
|
+
asProvider(PAYMENT_PROVIDER_TOKEN, options.paymentProvider),
|
|
1180
|
+
asProvider(PAYMENT_EVENT_LOG_TOKEN, options.paymentEventLog),
|
|
1181
|
+
asProvider(ACTIVATION_ORCHESTRATOR_TOKEN, options.activationOrchestrator),
|
|
1182
|
+
asProvider(REGISTRATION_AUDIT_LOGGER_TOKEN, options.auditLogger),
|
|
1183
|
+
PendingRegistrationService
|
|
1184
|
+
];
|
|
1185
|
+
if (options.resumeTokenSigner) {
|
|
1186
|
+
providers.push(asProvider(REGISTRATION_RESUME_TOKEN_SIGNER_TOKEN, options.resumeTokenSigner));
|
|
1187
|
+
}
|
|
1188
|
+
if (options.resumeDelivery) {
|
|
1189
|
+
providers.push(asProvider(REGISTRATION_RESUME_DELIVERY_TOKEN, options.resumeDelivery));
|
|
1190
|
+
}
|
|
1191
|
+
if (options.resumeBaseUrl !== void 0) {
|
|
1192
|
+
providers.push({
|
|
1193
|
+
provide: REGISTRATION_RESUME_BASE_URL_TOKEN,
|
|
1194
|
+
useValue: options.resumeBaseUrl
|
|
1195
|
+
});
|
|
1196
|
+
}
|
|
1197
|
+
if (options.configuratorLookup) {
|
|
1198
|
+
providers.push(asProvider(REGISTRATION_CONFIGURATOR_LOOKUP_TOKEN, options.configuratorLookup));
|
|
1199
|
+
}
|
|
1200
|
+
if (options.promoPreview) {
|
|
1201
|
+
providers.push(asProvider(REGISTRATION_PROMO_PREVIEW_TOKEN, options.promoPreview));
|
|
1202
|
+
}
|
|
1203
|
+
if (options.businessTypeLookup) {
|
|
1204
|
+
providers.push(asProvider(REGISTRATION_BUSINESS_TYPE_LOOKUP_TOKEN, options.businessTypeLookup));
|
|
1205
|
+
}
|
|
1206
|
+
const includeCleanupCron = options.includeCleanupCron ?? true;
|
|
1207
|
+
if (includeCleanupCron) {
|
|
1208
|
+
providers.push(RegistrationCleanupCron);
|
|
1209
|
+
}
|
|
1210
|
+
return {
|
|
1211
|
+
module: _RegistrationModule,
|
|
1212
|
+
global: options.global ?? false,
|
|
1213
|
+
imports: options.imports ?? [],
|
|
1214
|
+
providers,
|
|
1215
|
+
exports: includeCleanupCron ? [
|
|
1216
|
+
PendingRegistrationService,
|
|
1217
|
+
RegistrationCleanupCron
|
|
1218
|
+
] : [
|
|
1219
|
+
PendingRegistrationService
|
|
1220
|
+
]
|
|
1221
|
+
};
|
|
1222
|
+
}
|
|
1223
|
+
};
|
|
1224
|
+
RegistrationModule = _ts_decorate4([
|
|
1225
|
+
(0, import_common4.Module)({})
|
|
1226
|
+
], RegistrationModule);
|
|
1227
|
+
|
|
1228
|
+
// src/registration/dto/register-start.dto.ts
|
|
1229
|
+
var import_class_validator = require("class-validator");
|
|
1230
|
+
function _ts_decorate5(decorators, target, key, desc) {
|
|
1231
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
1232
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
1233
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
1234
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
1235
|
+
}
|
|
1236
|
+
__name(_ts_decorate5, "_ts_decorate");
|
|
1237
|
+
function _ts_metadata4(k, v) {
|
|
1238
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
1239
|
+
}
|
|
1240
|
+
__name(_ts_metadata4, "_ts_metadata");
|
|
1241
|
+
var RegisterStartDto = class {
|
|
1242
|
+
static {
|
|
1243
|
+
__name(this, "RegisterStartDto");
|
|
1244
|
+
}
|
|
1245
|
+
/** Association/tenant name (required). */
|
|
1246
|
+
tenantName;
|
|
1247
|
+
/** Optional slug. If left empty, it is generated from `tenantName`. */
|
|
1248
|
+
tenantSlug;
|
|
1249
|
+
/** Salutation of the administrator (optional). */
|
|
1250
|
+
salutation;
|
|
1251
|
+
firstName;
|
|
1252
|
+
lastName;
|
|
1253
|
+
email;
|
|
1254
|
+
password;
|
|
1255
|
+
/** Terms / privacy / data processing — mandatory acceptance (must be `true`). */
|
|
1256
|
+
termsAccepted;
|
|
1257
|
+
locale;
|
|
1258
|
+
};
|
|
1259
|
+
_ts_decorate5([
|
|
1260
|
+
(0, import_class_validator.IsString)(),
|
|
1261
|
+
(0, import_class_validator.MinLength)(2),
|
|
1262
|
+
(0, import_class_validator.MaxLength)(120),
|
|
1263
|
+
_ts_metadata4("design:type", String)
|
|
1264
|
+
], RegisterStartDto.prototype, "tenantName", void 0);
|
|
1265
|
+
_ts_decorate5([
|
|
1266
|
+
(0, import_class_validator.IsOptional)(),
|
|
1267
|
+
(0, import_class_validator.IsString)(),
|
|
1268
|
+
(0, import_class_validator.Matches)(/^[a-z0-9-]+$/, {
|
|
1269
|
+
message: "tenantSlug darf nur Kleinbuchstaben, Ziffern und Bindestriche enthalten."
|
|
1270
|
+
}),
|
|
1271
|
+
(0, import_class_validator.MaxLength)(60),
|
|
1272
|
+
_ts_metadata4("design:type", String)
|
|
1273
|
+
], RegisterStartDto.prototype, "tenantSlug", void 0);
|
|
1274
|
+
_ts_decorate5([
|
|
1275
|
+
(0, import_class_validator.IsOptional)(),
|
|
1276
|
+
(0, import_class_validator.IsString)(),
|
|
1277
|
+
(0, import_class_validator.MaxLength)(40),
|
|
1278
|
+
_ts_metadata4("design:type", String)
|
|
1279
|
+
], RegisterStartDto.prototype, "salutation", void 0);
|
|
1280
|
+
_ts_decorate5([
|
|
1281
|
+
(0, import_class_validator.IsString)(),
|
|
1282
|
+
(0, import_class_validator.MinLength)(1),
|
|
1283
|
+
(0, import_class_validator.MaxLength)(60),
|
|
1284
|
+
_ts_metadata4("design:type", String)
|
|
1285
|
+
], RegisterStartDto.prototype, "firstName", void 0);
|
|
1286
|
+
_ts_decorate5([
|
|
1287
|
+
(0, import_class_validator.IsString)(),
|
|
1288
|
+
(0, import_class_validator.MinLength)(1),
|
|
1289
|
+
(0, import_class_validator.MaxLength)(60),
|
|
1290
|
+
_ts_metadata4("design:type", String)
|
|
1291
|
+
], RegisterStartDto.prototype, "lastName", void 0);
|
|
1292
|
+
_ts_decorate5([
|
|
1293
|
+
(0, import_class_validator.IsEmail)(),
|
|
1294
|
+
(0, import_class_validator.MaxLength)(160),
|
|
1295
|
+
_ts_metadata4("design:type", String)
|
|
1296
|
+
], RegisterStartDto.prototype, "email", void 0);
|
|
1297
|
+
_ts_decorate5([
|
|
1298
|
+
(0, import_class_validator.IsString)(),
|
|
1299
|
+
(0, import_class_validator.MinLength)(8),
|
|
1300
|
+
(0, import_class_validator.MaxLength)(160),
|
|
1301
|
+
_ts_metadata4("design:type", String)
|
|
1302
|
+
], RegisterStartDto.prototype, "password", void 0);
|
|
1303
|
+
_ts_decorate5([
|
|
1304
|
+
(0, import_class_validator.Equals)(true, {
|
|
1305
|
+
message: "AGB, Datenschutzerklaerung und Auftragsverarbeitung muessen zugestimmt werden."
|
|
1306
|
+
}),
|
|
1307
|
+
_ts_metadata4("design:type", Boolean)
|
|
1308
|
+
], RegisterStartDto.prototype, "termsAccepted", void 0);
|
|
1309
|
+
_ts_decorate5([
|
|
1310
|
+
(0, import_class_validator.IsOptional)(),
|
|
1311
|
+
(0, import_class_validator.IsIn)([
|
|
1312
|
+
"de",
|
|
1313
|
+
"tr"
|
|
1314
|
+
]),
|
|
1315
|
+
_ts_metadata4("design:type", String)
|
|
1316
|
+
], RegisterStartDto.prototype, "locale", void 0);
|
|
1317
|
+
|
|
1318
|
+
// src/registration/dto/verify-registration-otp.dto.ts
|
|
1319
|
+
var import_class_validator2 = require("class-validator");
|
|
1320
|
+
function _ts_decorate6(decorators, target, key, desc) {
|
|
1321
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
1322
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
1323
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
1324
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
1325
|
+
}
|
|
1326
|
+
__name(_ts_decorate6, "_ts_decorate");
|
|
1327
|
+
function _ts_metadata5(k, v) {
|
|
1328
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
1329
|
+
}
|
|
1330
|
+
__name(_ts_metadata5, "_ts_metadata");
|
|
1331
|
+
var VerifyRegistrationOtpDto = class {
|
|
1332
|
+
static {
|
|
1333
|
+
__name(this, "VerifyRegistrationOtpDto");
|
|
1334
|
+
}
|
|
1335
|
+
email;
|
|
1336
|
+
otp;
|
|
1337
|
+
};
|
|
1338
|
+
_ts_decorate6([
|
|
1339
|
+
(0, import_class_validator2.IsEmail)(),
|
|
1340
|
+
_ts_metadata5("design:type", String)
|
|
1341
|
+
], VerifyRegistrationOtpDto.prototype, "email", void 0);
|
|
1342
|
+
_ts_decorate6([
|
|
1343
|
+
(0, import_class_validator2.IsString)(),
|
|
1344
|
+
(0, import_class_validator2.Matches)(/^\d{6}$/, {
|
|
1345
|
+
message: "OTP muss 6 Ziffern sein."
|
|
1346
|
+
}),
|
|
1347
|
+
_ts_metadata5("design:type", String)
|
|
1348
|
+
], VerifyRegistrationOtpDto.prototype, "otp", void 0);
|
|
1349
|
+
|
|
1350
|
+
// src/registration/dto/resend-registration-otp.dto.ts
|
|
1351
|
+
var import_class_validator3 = require("class-validator");
|
|
1352
|
+
function _ts_decorate7(decorators, target, key, desc) {
|
|
1353
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
1354
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
1355
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
1356
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
1357
|
+
}
|
|
1358
|
+
__name(_ts_decorate7, "_ts_decorate");
|
|
1359
|
+
function _ts_metadata6(k, v) {
|
|
1360
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
1361
|
+
}
|
|
1362
|
+
__name(_ts_metadata6, "_ts_metadata");
|
|
1363
|
+
var ResendRegistrationOtpDto = class {
|
|
1364
|
+
static {
|
|
1365
|
+
__name(this, "ResendRegistrationOtpDto");
|
|
1366
|
+
}
|
|
1367
|
+
email;
|
|
1368
|
+
};
|
|
1369
|
+
_ts_decorate7([
|
|
1370
|
+
(0, import_class_validator3.IsEmail)(),
|
|
1371
|
+
_ts_metadata6("design:type", String)
|
|
1372
|
+
], ResendRegistrationOtpDto.prototype, "email", void 0);
|
|
1373
|
+
|
|
1374
|
+
// src/registration/dto/select-plan.dto.ts
|
|
1375
|
+
var import_class_validator4 = require("class-validator");
|
|
1376
|
+
function _ts_decorate8(decorators, target, key, desc) {
|
|
1377
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
1378
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
1379
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
1380
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
1381
|
+
}
|
|
1382
|
+
__name(_ts_decorate8, "_ts_decorate");
|
|
1383
|
+
function _ts_metadata7(k, v) {
|
|
1384
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
1385
|
+
}
|
|
1386
|
+
__name(_ts_metadata7, "_ts_metadata");
|
|
1387
|
+
var SelectRegistrationPlanDto = class {
|
|
1388
|
+
static {
|
|
1389
|
+
__name(this, "SelectRegistrationPlanDto");
|
|
1390
|
+
}
|
|
1391
|
+
pendingRegistrationId;
|
|
1392
|
+
planId;
|
|
1393
|
+
};
|
|
1394
|
+
_ts_decorate8([
|
|
1395
|
+
(0, import_class_validator4.IsString)(),
|
|
1396
|
+
(0, import_class_validator4.MinLength)(1),
|
|
1397
|
+
(0, import_class_validator4.MaxLength)(80),
|
|
1398
|
+
_ts_metadata7("design:type", String)
|
|
1399
|
+
], SelectRegistrationPlanDto.prototype, "pendingRegistrationId", void 0);
|
|
1400
|
+
_ts_decorate8([
|
|
1401
|
+
(0, import_class_validator4.IsString)(),
|
|
1402
|
+
(0, import_class_validator4.MinLength)(1),
|
|
1403
|
+
(0, import_class_validator4.MaxLength)(40),
|
|
1404
|
+
_ts_metadata7("design:type", String)
|
|
1405
|
+
], SelectRegistrationPlanDto.prototype, "planId", void 0);
|
|
1406
|
+
|
|
1407
|
+
// src/registration/dto/start-checkout.dto.ts
|
|
1408
|
+
var import_class_validator5 = require("class-validator");
|
|
1409
|
+
function _ts_decorate9(decorators, target, key, desc) {
|
|
1410
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
1411
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
1412
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
1413
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
1414
|
+
}
|
|
1415
|
+
__name(_ts_decorate9, "_ts_decorate");
|
|
1416
|
+
function _ts_metadata8(k, v) {
|
|
1417
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
1418
|
+
}
|
|
1419
|
+
__name(_ts_metadata8, "_ts_metadata");
|
|
1420
|
+
var StartRegistrationCheckoutDto = class {
|
|
1421
|
+
static {
|
|
1422
|
+
__name(this, "StartRegistrationCheckoutDto");
|
|
1423
|
+
}
|
|
1424
|
+
pendingRegistrationId;
|
|
1425
|
+
successUrl;
|
|
1426
|
+
cancelUrl;
|
|
1427
|
+
};
|
|
1428
|
+
_ts_decorate9([
|
|
1429
|
+
(0, import_class_validator5.IsString)(),
|
|
1430
|
+
(0, import_class_validator5.MinLength)(1),
|
|
1431
|
+
(0, import_class_validator5.MaxLength)(80),
|
|
1432
|
+
_ts_metadata8("design:type", String)
|
|
1433
|
+
], StartRegistrationCheckoutDto.prototype, "pendingRegistrationId", void 0);
|
|
1434
|
+
_ts_decorate9([
|
|
1435
|
+
(0, import_class_validator5.IsUrl)({
|
|
1436
|
+
require_tld: false
|
|
1437
|
+
}),
|
|
1438
|
+
(0, import_class_validator5.MaxLength)(500),
|
|
1439
|
+
_ts_metadata8("design:type", String)
|
|
1440
|
+
], StartRegistrationCheckoutDto.prototype, "successUrl", void 0);
|
|
1441
|
+
_ts_decorate9([
|
|
1442
|
+
(0, import_class_validator5.IsUrl)({
|
|
1443
|
+
require_tld: false
|
|
1444
|
+
}),
|
|
1445
|
+
(0, import_class_validator5.MaxLength)(500),
|
|
1446
|
+
_ts_metadata8("design:type", String)
|
|
1447
|
+
], StartRegistrationCheckoutDto.prototype, "cancelUrl", void 0);
|
|
1448
|
+
|
|
1449
|
+
// src/registration/dto/payment-webhook.dto.ts
|
|
1450
|
+
var import_class_validator6 = require("class-validator");
|
|
1451
|
+
function _ts_decorate10(decorators, target, key, desc) {
|
|
1452
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
1453
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
1454
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
1455
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
1456
|
+
}
|
|
1457
|
+
__name(_ts_decorate10, "_ts_decorate");
|
|
1458
|
+
function _ts_metadata9(k, v) {
|
|
1459
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
1460
|
+
}
|
|
1461
|
+
__name(_ts_metadata9, "_ts_metadata");
|
|
1462
|
+
var PaymentWebhookDto = class {
|
|
1463
|
+
static {
|
|
1464
|
+
__name(this, "PaymentWebhookDto");
|
|
1465
|
+
}
|
|
1466
|
+
eventId;
|
|
1467
|
+
sessionId;
|
|
1468
|
+
provider;
|
|
1469
|
+
status;
|
|
1470
|
+
payload;
|
|
1471
|
+
};
|
|
1472
|
+
_ts_decorate10([
|
|
1473
|
+
(0, import_class_validator6.IsString)(),
|
|
1474
|
+
(0, import_class_validator6.MinLength)(1),
|
|
1475
|
+
(0, import_class_validator6.MaxLength)(120),
|
|
1476
|
+
_ts_metadata9("design:type", String)
|
|
1477
|
+
], PaymentWebhookDto.prototype, "eventId", void 0);
|
|
1478
|
+
_ts_decorate10([
|
|
1479
|
+
(0, import_class_validator6.IsOptional)(),
|
|
1480
|
+
(0, import_class_validator6.IsString)(),
|
|
1481
|
+
(0, import_class_validator6.MaxLength)(120),
|
|
1482
|
+
_ts_metadata9("design:type", String)
|
|
1483
|
+
], PaymentWebhookDto.prototype, "sessionId", void 0);
|
|
1484
|
+
_ts_decorate10([
|
|
1485
|
+
(0, import_class_validator6.IsString)(),
|
|
1486
|
+
(0, import_class_validator6.MaxLength)(40),
|
|
1487
|
+
_ts_metadata9("design:type", String)
|
|
1488
|
+
], PaymentWebhookDto.prototype, "provider", void 0);
|
|
1489
|
+
_ts_decorate10([
|
|
1490
|
+
(0, import_class_validator6.IsIn)([
|
|
1491
|
+
"SUCCEEDED",
|
|
1492
|
+
"FAILED"
|
|
1493
|
+
]),
|
|
1494
|
+
_ts_metadata9("design:type", String)
|
|
1495
|
+
], PaymentWebhookDto.prototype, "status", void 0);
|
|
1496
|
+
_ts_decorate10([
|
|
1497
|
+
(0, import_class_validator6.IsOptional)(),
|
|
1498
|
+
(0, import_class_validator6.IsObject)(),
|
|
1499
|
+
_ts_metadata9("design:type", typeof Record === "undefined" ? Object : Record)
|
|
1500
|
+
], PaymentWebhookDto.prototype, "payload", void 0);
|
|
1501
|
+
|
|
1502
|
+
// src/registration/dto/continue-registration.dto.ts
|
|
1503
|
+
var import_class_validator7 = require("class-validator");
|
|
1504
|
+
function _ts_decorate11(decorators, target, key, desc) {
|
|
1505
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
1506
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
1507
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
1508
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
1509
|
+
}
|
|
1510
|
+
__name(_ts_decorate11, "_ts_decorate");
|
|
1511
|
+
function _ts_metadata10(k, v) {
|
|
1512
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
1513
|
+
}
|
|
1514
|
+
__name(_ts_metadata10, "_ts_metadata");
|
|
1515
|
+
var ContinueRegistrationDto = class {
|
|
1516
|
+
static {
|
|
1517
|
+
__name(this, "ContinueRegistrationDto");
|
|
1518
|
+
}
|
|
1519
|
+
token;
|
|
1520
|
+
};
|
|
1521
|
+
_ts_decorate11([
|
|
1522
|
+
(0, import_class_validator7.IsString)(),
|
|
1523
|
+
(0, import_class_validator7.MinLength)(10),
|
|
1524
|
+
(0, import_class_validator7.MaxLength)(2e3),
|
|
1525
|
+
_ts_metadata10("design:type", String)
|
|
1526
|
+
], ContinueRegistrationDto.prototype, "token", void 0);
|
|
1527
|
+
|
|
1528
|
+
// src/registration/dto/save-configuration.dto.ts
|
|
1529
|
+
var import_class_validator8 = require("class-validator");
|
|
1530
|
+
var import_class_transformer = require("class-transformer");
|
|
1531
|
+
function _ts_decorate12(decorators, target, key, desc) {
|
|
1532
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
1533
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
1534
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
1535
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
1536
|
+
}
|
|
1537
|
+
__name(_ts_decorate12, "_ts_decorate");
|
|
1538
|
+
function _ts_metadata11(k, v) {
|
|
1539
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
1540
|
+
}
|
|
1541
|
+
__name(_ts_metadata11, "_ts_metadata");
|
|
1542
|
+
var SaveRegistrationConfigSelectionDto = class {
|
|
1543
|
+
static {
|
|
1544
|
+
__name(this, "SaveRegistrationConfigSelectionDto");
|
|
1545
|
+
}
|
|
1546
|
+
modelId;
|
|
1547
|
+
billingCycle;
|
|
1548
|
+
appliedPromoCode;
|
|
1549
|
+
/**
|
|
1550
|
+
* SPEC_V2 §11.1 M5.3 — optional business-type choice. UUID of a
|
|
1551
|
+
* **published** BusinessTypeVersion. The service validates existence +
|
|
1552
|
+
* live status against `RegistrationBusinessTypeLookup`. `null` = no
|
|
1553
|
+
* choice (legacy Plan-only path).
|
|
1554
|
+
*/
|
|
1555
|
+
businessTypeVersionId;
|
|
1556
|
+
/**
|
|
1557
|
+
* METAMODELL §17a — Bundle consistency. ID of the `CheckoutOffer` that was
|
|
1558
|
+
* created on the pricing page (`<app>/register?offer=<id>`). Passed
|
|
1559
|
+
* through unchanged in `configJson`; the ActivationOrchestrator freezes it
|
|
1560
|
+
* as `packageSnapshot` when the subscription is created and sets the offer
|
|
1561
|
+
* to `consumed`. `null` = direct registration without a website offer.
|
|
1562
|
+
*/
|
|
1563
|
+
offerId;
|
|
1564
|
+
};
|
|
1565
|
+
_ts_decorate12([
|
|
1566
|
+
(0, import_class_validator8.IsString)(),
|
|
1567
|
+
(0, import_class_validator8.MinLength)(1),
|
|
1568
|
+
(0, import_class_validator8.MaxLength)(40),
|
|
1569
|
+
_ts_metadata11("design:type", String)
|
|
1570
|
+
], SaveRegistrationConfigSelectionDto.prototype, "modelId", void 0);
|
|
1571
|
+
_ts_decorate12([
|
|
1572
|
+
(0, import_class_validator8.IsIn)([
|
|
1573
|
+
"MONTHLY",
|
|
1574
|
+
"YEARLY"
|
|
1575
|
+
]),
|
|
1576
|
+
_ts_metadata11("design:type", String)
|
|
1577
|
+
], SaveRegistrationConfigSelectionDto.prototype, "billingCycle", void 0);
|
|
1578
|
+
_ts_decorate12([
|
|
1579
|
+
(0, import_class_validator8.IsOptional)(),
|
|
1580
|
+
(0, import_class_validator8.IsString)(),
|
|
1581
|
+
(0, import_class_validator8.MaxLength)(60),
|
|
1582
|
+
_ts_metadata11("design:type", Object)
|
|
1583
|
+
], SaveRegistrationConfigSelectionDto.prototype, "appliedPromoCode", void 0);
|
|
1584
|
+
_ts_decorate12([
|
|
1585
|
+
(0, import_class_validator8.IsOptional)(),
|
|
1586
|
+
(0, import_class_validator8.ValidateIf)((_o, value) => value !== null),
|
|
1587
|
+
(0, import_class_validator8.IsUUID)(),
|
|
1588
|
+
_ts_metadata11("design:type", Object)
|
|
1589
|
+
], SaveRegistrationConfigSelectionDto.prototype, "businessTypeVersionId", void 0);
|
|
1590
|
+
_ts_decorate12([
|
|
1591
|
+
(0, import_class_validator8.IsOptional)(),
|
|
1592
|
+
(0, import_class_validator8.ValidateIf)((_o, value) => value !== null),
|
|
1593
|
+
(0, import_class_validator8.IsUUID)(),
|
|
1594
|
+
_ts_metadata11("design:type", Object)
|
|
1595
|
+
], SaveRegistrationConfigSelectionDto.prototype, "offerId", void 0);
|
|
1596
|
+
var SaveRegistrationConfigDto = class {
|
|
1597
|
+
static {
|
|
1598
|
+
__name(this, "SaveRegistrationConfigDto");
|
|
1599
|
+
}
|
|
1600
|
+
pendingRegistrationId;
|
|
1601
|
+
selection;
|
|
1602
|
+
};
|
|
1603
|
+
_ts_decorate12([
|
|
1604
|
+
(0, import_class_validator8.IsString)(),
|
|
1605
|
+
(0, import_class_validator8.MinLength)(1),
|
|
1606
|
+
(0, import_class_validator8.MaxLength)(80),
|
|
1607
|
+
_ts_metadata11("design:type", String)
|
|
1608
|
+
], SaveRegistrationConfigDto.prototype, "pendingRegistrationId", void 0);
|
|
1609
|
+
_ts_decorate12([
|
|
1610
|
+
(0, import_class_validator8.ValidateNested)(),
|
|
1611
|
+
(0, import_class_transformer.Type)(() => SaveRegistrationConfigSelectionDto),
|
|
1612
|
+
_ts_metadata11("design:type", typeof SaveRegistrationConfigSelectionDto === "undefined" ? Object : SaveRegistrationConfigSelectionDto)
|
|
1613
|
+
], SaveRegistrationConfigDto.prototype, "selection", void 0);
|
|
1614
|
+
|
|
1615
|
+
// src/registration/dto/preview-promo.dto.ts
|
|
1616
|
+
var import_class_validator9 = require("class-validator");
|
|
1617
|
+
function _ts_decorate13(decorators, target, key, desc) {
|
|
1618
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
1619
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
1620
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
1621
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
1622
|
+
}
|
|
1623
|
+
__name(_ts_decorate13, "_ts_decorate");
|
|
1624
|
+
function _ts_metadata12(k, v) {
|
|
1625
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
1626
|
+
}
|
|
1627
|
+
__name(_ts_metadata12, "_ts_metadata");
|
|
1628
|
+
var PreviewRegistrationPromoDto = class {
|
|
1629
|
+
static {
|
|
1630
|
+
__name(this, "PreviewRegistrationPromoDto");
|
|
1631
|
+
}
|
|
1632
|
+
pendingRegistrationId;
|
|
1633
|
+
code;
|
|
1634
|
+
};
|
|
1635
|
+
_ts_decorate13([
|
|
1636
|
+
(0, import_class_validator9.IsString)(),
|
|
1637
|
+
(0, import_class_validator9.MinLength)(1),
|
|
1638
|
+
(0, import_class_validator9.MaxLength)(80),
|
|
1639
|
+
_ts_metadata12("design:type", String)
|
|
1640
|
+
], PreviewRegistrationPromoDto.prototype, "pendingRegistrationId", void 0);
|
|
1641
|
+
_ts_decorate13([
|
|
1642
|
+
(0, import_class_validator9.IsOptional)(),
|
|
1643
|
+
(0, import_class_validator9.IsString)(),
|
|
1644
|
+
(0, import_class_validator9.MaxLength)(60),
|
|
1645
|
+
_ts_metadata12("design:type", String)
|
|
1646
|
+
], PreviewRegistrationPromoDto.prototype, "code", void 0);
|
|
1647
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
1648
|
+
0 && (module.exports = {
|
|
1649
|
+
ACTIVATION_ORCHESTRATOR_TOKEN,
|
|
1650
|
+
BaseIpRateLimitGuard,
|
|
1651
|
+
ContinueRegistrationDto,
|
|
1652
|
+
CronExpression,
|
|
1653
|
+
PASSWORD_HASHER_TOKEN,
|
|
1654
|
+
PAYMENT_EVENT_LOG_TOKEN,
|
|
1655
|
+
PAYMENT_PROVIDER_TOKEN,
|
|
1656
|
+
PENDING_REGISTRATION_REPOSITORY_TOKEN,
|
|
1657
|
+
PLAN_CATALOG_LOOKUP_TOKEN,
|
|
1658
|
+
PaymentWebhookDto,
|
|
1659
|
+
PendingRegistrationService,
|
|
1660
|
+
PreviewRegistrationPromoDto,
|
|
1661
|
+
REGISTRATION_AUDIT_LOGGER_TOKEN,
|
|
1662
|
+
REGISTRATION_BUSINESS_TYPE_LOOKUP_TOKEN,
|
|
1663
|
+
REGISTRATION_CONFIGURATOR_LOOKUP_TOKEN,
|
|
1664
|
+
REGISTRATION_CONFIG_TOKEN,
|
|
1665
|
+
REGISTRATION_OTP_DELIVERY_TOKEN,
|
|
1666
|
+
REGISTRATION_PROMO_PREVIEW_TOKEN,
|
|
1667
|
+
REGISTRATION_RESUME_BASE_URL_TOKEN,
|
|
1668
|
+
REGISTRATION_RESUME_DELIVERY_TOKEN,
|
|
1669
|
+
REGISTRATION_RESUME_TOKEN_SIGNER_TOKEN,
|
|
1670
|
+
RegisterStartDto,
|
|
1671
|
+
RegistrationCleanupCron,
|
|
1672
|
+
RegistrationModule,
|
|
1673
|
+
ResendRegistrationOtpDto,
|
|
1674
|
+
SLUG_AVAILABILITY_CHECK_TOKEN,
|
|
1675
|
+
SaveRegistrationConfigDto,
|
|
1676
|
+
SaveRegistrationConfigSelectionDto,
|
|
1677
|
+
SelectRegistrationPlanDto,
|
|
1678
|
+
StartRegistrationCheckoutDto,
|
|
1679
|
+
USER_ACCOUNT_LOOKUP_TOKEN,
|
|
1680
|
+
VerifyRegistrationOtpDto,
|
|
1681
|
+
computeBreakdown,
|
|
1682
|
+
generateOtpCode,
|
|
1683
|
+
hashOtpCode,
|
|
1684
|
+
resolveModel,
|
|
1685
|
+
slugify,
|
|
1686
|
+
verifyOtpCode
|
|
1687
|
+
});
|