@experts_hub/shared 1.0.611 → 1.0.614

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -1127,7 +1127,7 @@ var ClientServiceAgreementUploadDto = class {
1127
1127
  __decorateClass([
1128
1128
  IsOptional15(),
1129
1129
  IsString16()
1130
- ], ClientServiceAgreementUploadDto.prototype, "agreementType", 2);
1130
+ ], ClientServiceAgreementUploadDto.prototype, "serviceAgreementSignedOn", 2);
1131
1131
 
1132
1132
  // src/modules/user/client-profile/dto/client-e-signature.dto.ts
1133
1133
  import { IsNotEmpty as IsNotEmpty34, IsUUID as IsUUID14 } from "class-validator";
@@ -2234,7 +2234,7 @@ import {
2234
2234
  Entity as Entity64,
2235
2235
  Column as Column65,
2236
2236
  OneToMany as OneToMany23,
2237
- OneToOne as OneToOne12,
2237
+ OneToOne as OneToOne13,
2238
2238
  Index as Index57,
2239
2239
  ManyToOne as ManyToOne59,
2240
2240
  JoinColumn as JoinColumn60
@@ -4178,16 +4178,16 @@ JobRecommendation = __decorateClass([
4178
4178
  ], JobRecommendation);
4179
4179
 
4180
4180
  // src/entities/contract.entity.ts
4181
- import { Entity as Entity30, Column as Column31, Index as Index23, ManyToOne as ManyToOne29, JoinColumn as JoinColumn29, OneToOne as OneToOne9 } from "typeorm";
4181
+ import { Entity as Entity30, Column as Column31, Index as Index23, ManyToOne as ManyToOne29, JoinColumn as JoinColumn29, OneToOne as OneToOne10 } from "typeorm";
4182
4182
 
4183
4183
  // src/entities/escrow-wallet.entity.ts
4184
- import { Entity as Entity28, Column as Column29, Index as Index21, JoinColumn as JoinColumn27, OneToOne as OneToOne8, OneToMany as OneToMany11, ManyToOne as ManyToOne27 } from "typeorm";
4184
+ import { Entity as Entity29, Column as Column30, Index as Index22, JoinColumn as JoinColumn28, OneToOne as OneToOne9, OneToMany as OneToMany12, ManyToOne as ManyToOne28 } from "typeorm";
4185
4185
 
4186
4186
  // src/entities/escrow-wallet-transaction.entity.ts
4187
- import { Entity as Entity27, Column as Column28, Index as Index20, ManyToOne as ManyToOne26, JoinColumn as JoinColumn26, OneToOne as OneToOne7 } from "typeorm";
4187
+ import { Entity as Entity28, Column as Column29, Index as Index21, ManyToOne as ManyToOne27, JoinColumn as JoinColumn27, OneToOne as OneToOne8 } from "typeorm";
4188
4188
 
4189
4189
  // src/entities/invoice.entity.ts
4190
- import { Entity as Entity26, Column as Column27, Index as Index19, JoinColumn as JoinColumn25, ManyToOne as ManyToOne25, OneToOne as OneToOne6 } from "typeorm";
4190
+ import { Entity as Entity27, Column as Column28, Index as Index20, JoinColumn as JoinColumn26, ManyToOne as ManyToOne26, OneToOne as OneToOne7 } from "typeorm";
4191
4191
 
4192
4192
  // src/entities/timesheet-line.entity.ts
4193
4193
  import { Entity as Entity24, Column as Column25, Index as Index17, JoinColumn as JoinColumn23, OneToMany as OneToMany10, ManyToOne as ManyToOne23 } from "typeorm";
@@ -4396,7 +4396,110 @@ TimesheetLine = __decorateClass([
4396
4396
  ], TimesheetLine);
4397
4397
 
4398
4398
  // src/entities/hiring.entity.ts
4399
- import { Entity as Entity25, Column as Column26, Index as Index18, ManyToOne as ManyToOne24, JoinColumn as JoinColumn24, OneToOne as OneToOne5 } from "typeorm";
4399
+ import { Entity as Entity26, Column as Column27, Index as Index19, ManyToOne as ManyToOne25, JoinColumn as JoinColumn25, OneToOne as OneToOne6 } from "typeorm";
4400
+
4401
+ // src/entities/contract-summary.entity.ts
4402
+ import { Entity as Entity25, Column as Column26, Index as Index18, ManyToOne as ManyToOne24, JoinColumn as JoinColumn24, OneToMany as OneToMany11, OneToOne as OneToOne5 } from "typeorm";
4403
+ var ContractSummaryStatusEnum = /* @__PURE__ */ ((ContractSummaryStatusEnum2) => {
4404
+ ContractSummaryStatusEnum2["PENDING"] = "PENDING";
4405
+ ContractSummaryStatusEnum2["ACTIVE"] = "ACTIVE";
4406
+ ContractSummaryStatusEnum2["COMPLETED"] = "COMPLETED";
4407
+ return ContractSummaryStatusEnum2;
4408
+ })(ContractSummaryStatusEnum || {});
4409
+ var ContractSummary = class extends BaseEntity {
4410
+ };
4411
+ __decorateClass([
4412
+ Column26({ name: "job_id", type: "integer", nullable: true }),
4413
+ Index18()
4414
+ ], ContractSummary.prototype, "jobId", 2);
4415
+ __decorateClass([
4416
+ ManyToOne24(() => Job, (job) => job.contracts),
4417
+ JoinColumn24({ name: "job_id" })
4418
+ ], ContractSummary.prototype, "job", 2);
4419
+ __decorateClass([
4420
+ Column26({ name: "client_id", type: "integer", nullable: true }),
4421
+ Index18()
4422
+ ], ContractSummary.prototype, "clientId", 2);
4423
+ __decorateClass([
4424
+ ManyToOne24(() => User, (user) => user.clientContracts),
4425
+ JoinColumn24({ name: "client_id" })
4426
+ ], ContractSummary.prototype, "client", 2);
4427
+ __decorateClass([
4428
+ Column26({ name: "freelancer_id", type: "integer", nullable: true }),
4429
+ Index18()
4430
+ ], ContractSummary.prototype, "freelancerId", 2);
4431
+ __decorateClass([
4432
+ ManyToOne24(() => User, (user) => user.freelancerContracts),
4433
+ JoinColumn24({ name: "freelancer_id" })
4434
+ ], ContractSummary.prototype, "freelancer", 2);
4435
+ __decorateClass([
4436
+ Column26({
4437
+ name: "start_date",
4438
+ type: "timestamp with time zone",
4439
+ nullable: true
4440
+ })
4441
+ ], ContractSummary.prototype, "startDate", 2);
4442
+ __decorateClass([
4443
+ Column26({
4444
+ name: "end_date",
4445
+ type: "timestamp with time zone",
4446
+ nullable: true
4447
+ })
4448
+ ], ContractSummary.prototype, "endDate", 2);
4449
+ __decorateClass([
4450
+ Column26({ name: "duration", type: "integer", nullable: true })
4451
+ ], ContractSummary.prototype, "duration", 2);
4452
+ __decorateClass([
4453
+ Column26({
4454
+ name: "contract_value",
4455
+ type: "decimal",
4456
+ precision: 10,
4457
+ scale: 2,
4458
+ nullable: true
4459
+ })
4460
+ ], ContractSummary.prototype, "contractValue", 2);
4461
+ __decorateClass([
4462
+ Column26({ name: "invoicing_cycle", type: "varchar", nullable: true })
4463
+ ], ContractSummary.prototype, "invoicingCycle", 2);
4464
+ __decorateClass([
4465
+ Column26({ name: "is_msa_signed", type: "boolean", default: false })
4466
+ ], ContractSummary.prototype, "isMsaSigned", 2);
4467
+ __decorateClass([
4468
+ Column26({ name: "is_sow_signed", type: "boolean", default: false })
4469
+ ], ContractSummary.prototype, "isSowSigned", 2);
4470
+ __decorateClass([
4471
+ Column26({ name: "is_escrow_deposited", type: "boolean", default: false })
4472
+ ], ContractSummary.prototype, "isEscrowDeposited", 2);
4473
+ __decorateClass([
4474
+ Column26({
4475
+ name: "escrow_deposite_amount",
4476
+ type: "decimal",
4477
+ precision: 10,
4478
+ scale: 2,
4479
+ nullable: true
4480
+ })
4481
+ ], ContractSummary.prototype, "escrowDepositeAmount", 2);
4482
+ __decorateClass([
4483
+ Column26({
4484
+ name: "status",
4485
+ type: "enum",
4486
+ enum: ContractSummaryStatusEnum,
4487
+ nullable: true
4488
+ })
4489
+ ], ContractSummary.prototype, "status", 2);
4490
+ __decorateClass([
4491
+ OneToMany11(() => Contract, (contract) => contract.contractSummary, {
4492
+ cascade: true
4493
+ })
4494
+ ], ContractSummary.prototype, "contracts", 2);
4495
+ __decorateClass([
4496
+ OneToOne5(() => Hiring, (hiring) => hiring.contractSummary)
4497
+ ], ContractSummary.prototype, "hirings", 2);
4498
+ ContractSummary = __decorateClass([
4499
+ Entity25("contract_summaries")
4500
+ ], ContractSummary);
4501
+
4502
+ // src/entities/hiring.entity.ts
4400
4503
  var HiredFreelancerNatureOfWorkEnum = /* @__PURE__ */ ((HiredFreelancerNatureOfWorkEnum2) => {
4401
4504
  HiredFreelancerNatureOfWorkEnum2["FTE"] = "FTE";
4402
4505
  HiredFreelancerNatureOfWorkEnum2["FREELANCE"] = "FREELANCE";
@@ -4405,39 +4508,50 @@ var HiredFreelancerNatureOfWorkEnum = /* @__PURE__ */ ((HiredFreelancerNatureOfW
4405
4508
  var Hiring = class extends BaseEntity {
4406
4509
  };
4407
4510
  __decorateClass([
4408
- Column26({ name: "job_id", type: "integer", nullable: true }),
4409
- Index18()
4511
+ Column27({ name: "job_id", type: "integer", nullable: true }),
4512
+ Index19()
4410
4513
  ], Hiring.prototype, "jobId", 2);
4411
4514
  __decorateClass([
4412
- ManyToOne24(() => Job, (job) => job.hirings),
4413
- JoinColumn24({ name: "job_id" })
4515
+ ManyToOne25(() => Job, (job) => job.hirings),
4516
+ JoinColumn25({ name: "job_id" })
4414
4517
  ], Hiring.prototype, "job", 2);
4415
4518
  __decorateClass([
4416
- Column26({ name: "client_id", type: "integer", nullable: true }),
4417
- Index18()
4519
+ Column27({ name: "client_id", type: "integer", nullable: true }),
4520
+ Index19()
4418
4521
  ], Hiring.prototype, "clientId", 2);
4419
4522
  __decorateClass([
4420
- ManyToOne24(() => User, (user) => user.clientHirings),
4421
- JoinColumn24({ name: "client_id" })
4523
+ ManyToOne25(() => User, (user) => user.clientHirings),
4524
+ JoinColumn25({ name: "client_id" })
4422
4525
  ], Hiring.prototype, "client", 2);
4423
4526
  __decorateClass([
4424
- Column26({ name: "freelancer_id", type: "integer", nullable: true }),
4425
- Index18()
4527
+ Column27({ name: "freelancer_id", type: "integer", nullable: true }),
4528
+ Index19()
4426
4529
  ], Hiring.prototype, "freelancerId", 2);
4427
4530
  __decorateClass([
4428
- ManyToOne24(() => User, (user) => user.freelancerHirings),
4429
- JoinColumn24({ name: "freelancer_id" })
4531
+ ManyToOne25(() => User, (user) => user.freelancerHirings),
4532
+ JoinColumn25({ name: "freelancer_id" })
4430
4533
  ], Hiring.prototype, "freelancer", 2);
4431
4534
  __decorateClass([
4432
- Column26({ name: "invoice_id", type: "integer", nullable: true }),
4433
- Index18()
4535
+ Column27({ name: "invoice_id", type: "integer", nullable: true }),
4536
+ Index19()
4434
4537
  ], Hiring.prototype, "invoiceId", 2);
4435
4538
  __decorateClass([
4436
- OneToOne5(() => Invoice, (invoice) => invoice.hiring),
4437
- JoinColumn24({ name: "invoice_id" })
4539
+ OneToOne6(() => Invoice, (invoice) => invoice.hiring),
4540
+ JoinColumn25({ name: "invoice_id" })
4438
4541
  ], Hiring.prototype, "invoice", 2);
4439
4542
  __decorateClass([
4440
- Column26({
4543
+ Column27({ name: "contract_summary_id", type: "integer", nullable: true }),
4544
+ Index19()
4545
+ ], Hiring.prototype, "contractSummaryId", 2);
4546
+ __decorateClass([
4547
+ OneToOne6(
4548
+ () => ContractSummary,
4549
+ (contractSummary) => contractSummary.hirings
4550
+ ),
4551
+ JoinColumn25({ name: "contract_summary_id" })
4552
+ ], Hiring.prototype, "contractSummary", 2);
4553
+ __decorateClass([
4554
+ Column27({
4441
4555
  name: "freelancer_nature_of_work",
4442
4556
  type: "enum",
4443
4557
  enum: HiredFreelancerNatureOfWorkEnum,
@@ -4445,10 +4559,10 @@ __decorateClass([
4445
4559
  })
4446
4560
  ], Hiring.prototype, "freelancerNatureOfWork", 2);
4447
4561
  __decorateClass([
4448
- Column26({ name: "is_invoice_genrated", type: "boolean", default: false })
4562
+ Column27({ name: "is_invoice_genrated", type: "boolean", default: false })
4449
4563
  ], Hiring.prototype, "isInvoiceGenrated", 2);
4450
4564
  Hiring = __decorateClass([
4451
- Entity25("hirings")
4565
+ Entity26("hirings")
4452
4566
  ], Hiring);
4453
4567
 
4454
4568
  // src/entities/invoice.entity.ts
@@ -4476,100 +4590,100 @@ var InvoicePaymentStatusEnum = /* @__PURE__ */ ((InvoicePaymentStatusEnum2) => {
4476
4590
  var Invoice = class extends BaseEntity {
4477
4591
  };
4478
4592
  __decorateClass([
4479
- Column27({ name: "timesheet_line_id", type: "integer", nullable: true }),
4480
- Index19()
4593
+ Column28({ name: "timesheet_line_id", type: "integer", nullable: true }),
4594
+ Index20()
4481
4595
  ], Invoice.prototype, "timesheetLineId", 2);
4482
4596
  __decorateClass([
4483
- ManyToOne25(() => TimesheetLine, (timesheetLine) => timesheetLine.invoice),
4484
- JoinColumn25({ name: "timesheet_line_id" })
4597
+ ManyToOne26(() => TimesheetLine, (timesheetLine) => timesheetLine.invoice),
4598
+ JoinColumn26({ name: "timesheet_line_id" })
4485
4599
  ], Invoice.prototype, "timesheetLine", 2);
4486
4600
  __decorateClass([
4487
- Column27({ name: "job_id", type: "integer", nullable: true }),
4488
- Index19()
4601
+ Column28({ name: "job_id", type: "integer", nullable: true }),
4602
+ Index20()
4489
4603
  ], Invoice.prototype, "jobId", 2);
4490
4604
  __decorateClass([
4491
- ManyToOne25(() => Job, (job) => job.invoice),
4492
- JoinColumn25({ name: "job_id" })
4605
+ ManyToOne26(() => Job, (job) => job.invoice),
4606
+ JoinColumn26({ name: "job_id" })
4493
4607
  ], Invoice.prototype, "job", 2);
4494
4608
  __decorateClass([
4495
- Column27({ name: "client_id", type: "integer", nullable: true }),
4496
- Index19()
4609
+ Column28({ name: "client_id", type: "integer", nullable: true }),
4610
+ Index20()
4497
4611
  ], Invoice.prototype, "clientId", 2);
4498
4612
  __decorateClass([
4499
- ManyToOne25(() => User, (user) => user.clientInvoice),
4500
- JoinColumn25({ name: "client_id" })
4613
+ ManyToOne26(() => User, (user) => user.clientInvoice),
4614
+ JoinColumn26({ name: "client_id" })
4501
4615
  ], Invoice.prototype, "client", 2);
4502
4616
  __decorateClass([
4503
- Column27({ name: "freelancer_id", type: "integer", nullable: true }),
4504
- Index19()
4617
+ Column28({ name: "freelancer_id", type: "integer", nullable: true }),
4618
+ Index20()
4505
4619
  ], Invoice.prototype, "freelancerId", 2);
4506
4620
  __decorateClass([
4507
- ManyToOne25(() => User, (user) => user.freelancerInvoice),
4508
- JoinColumn25({ name: "freelancer_id" })
4621
+ ManyToOne26(() => User, (user) => user.freelancerInvoice),
4622
+ JoinColumn26({ name: "freelancer_id" })
4509
4623
  ], Invoice.prototype, "freelancer", 2);
4510
4624
  __decorateClass([
4511
- Column27({ name: "invoice_unique_id", type: "varchar", nullable: true })
4625
+ Column28({ name: "invoice_unique_id", type: "varchar", nullable: true })
4512
4626
  ], Invoice.prototype, "invoiceUniqueId", 2);
4513
4627
  __decorateClass([
4514
- Column27({
4628
+ Column28({
4515
4629
  name: "issued_at",
4516
4630
  type: "date",
4517
4631
  nullable: true
4518
4632
  })
4519
4633
  ], Invoice.prototype, "issuedAt", 2);
4520
4634
  __decorateClass([
4521
- Column27({
4635
+ Column28({
4522
4636
  name: "due_at",
4523
4637
  type: "date",
4524
4638
  nullable: true
4525
4639
  })
4526
4640
  ], Invoice.prototype, "dueAt", 2);
4527
4641
  __decorateClass([
4528
- Column27({
4642
+ Column28({
4529
4643
  name: "billing_cycle_from",
4530
4644
  type: "date",
4531
4645
  nullable: true
4532
4646
  })
4533
4647
  ], Invoice.prototype, "billingCycleFrom", 2);
4534
4648
  __decorateClass([
4535
- Column27({
4649
+ Column28({
4536
4650
  name: "billing_cycle_to",
4537
4651
  type: "date",
4538
4652
  nullable: true
4539
4653
  })
4540
4654
  ], Invoice.prototype, "billingCycleTo", 2);
4541
4655
  __decorateClass([
4542
- Column27({ name: "billing_hours", type: "varchar", nullable: true })
4656
+ Column28({ name: "billing_hours", type: "varchar", nullable: true })
4543
4657
  ], Invoice.prototype, "billingHours", 2);
4544
4658
  __decorateClass([
4545
- Column27({ name: "hourly_rate", type: "varchar", nullable: true })
4659
+ Column28({ name: "hourly_rate", type: "varchar", nullable: true })
4546
4660
  ], Invoice.prototype, "hourlyRate", 2);
4547
4661
  __decorateClass([
4548
- Column27({ name: "billing_amount", type: "varchar", nullable: true })
4662
+ Column28({ name: "billing_amount", type: "varchar", nullable: true })
4549
4663
  ], Invoice.prototype, "billingAmount", 2);
4550
4664
  __decorateClass([
4551
- Column27({ name: "invoice_type", type: "enum", enum: InvoiceTypeEnum, nullable: true })
4665
+ Column28({ name: "invoice_type", type: "enum", enum: InvoiceTypeEnum, nullable: true })
4552
4666
  ], Invoice.prototype, "invoiceType", 2);
4553
4667
  __decorateClass([
4554
- Column27({ name: "status", type: "enum", enum: InvoiceStatusEnum, nullable: true })
4668
+ Column28({ name: "status", type: "enum", enum: InvoiceStatusEnum, nullable: true })
4555
4669
  ], Invoice.prototype, "status", 2);
4556
4670
  __decorateClass([
4557
- Column27({ name: "payment_status", type: "enum", enum: InvoicePaymentStatusEnum, nullable: true })
4671
+ Column28({ name: "payment_status", type: "enum", enum: InvoicePaymentStatusEnum, nullable: true })
4558
4672
  ], Invoice.prototype, "paymentStatus", 2);
4559
4673
  __decorateClass([
4560
- Column27({ name: "client_invoice_url", type: "varchar", nullable: true })
4674
+ Column28({ name: "client_invoice_url", type: "varchar", nullable: true })
4561
4675
  ], Invoice.prototype, "clientInvoiceUrl", 2);
4562
4676
  __decorateClass([
4563
- Column27({ name: "freelancer_invoice_url", type: "varchar", nullable: true })
4677
+ Column28({ name: "freelancer_invoice_url", type: "varchar", nullable: true })
4564
4678
  ], Invoice.prototype, "freelancerInvoiceUrl", 2);
4565
4679
  __decorateClass([
4566
- OneToOne6(() => EscrowWalletTransaction, (escrowWalletTransaction) => escrowWalletTransaction.invoice)
4680
+ OneToOne7(() => EscrowWalletTransaction, (escrowWalletTransaction) => escrowWalletTransaction.invoice)
4567
4681
  ], Invoice.prototype, "escrowWalletTransaction", 2);
4568
4682
  __decorateClass([
4569
- OneToOne6(() => Hiring, (hiring) => hiring.invoice)
4683
+ OneToOne7(() => Hiring, (hiring) => hiring.invoice)
4570
4684
  ], Invoice.prototype, "hiring", 2);
4571
4685
  Invoice = __decorateClass([
4572
- Entity26("invoices")
4686
+ Entity27("invoices")
4573
4687
  ], Invoice);
4574
4688
 
4575
4689
  // src/entities/escrow-wallet-transaction.entity.ts
@@ -4586,189 +4700,91 @@ var EscrowWalletTransactionForEnum = /* @__PURE__ */ ((EscrowWalletTransactionFo
4586
4700
  var EscrowWalletTransaction = class extends BaseEntity {
4587
4701
  };
4588
4702
  __decorateClass([
4589
- Column28({ name: "escrow_wallet_id", type: "integer", nullable: true }),
4590
- Index20()
4703
+ Column29({ name: "escrow_wallet_id", type: "integer", nullable: true }),
4704
+ Index21()
4591
4705
  ], EscrowWalletTransaction.prototype, "escrowWalletId", 2);
4592
4706
  __decorateClass([
4593
- ManyToOne26(() => EscrowWallet, (escrowWallet) => escrowWallet.escrowWalletTransactions),
4594
- JoinColumn26({ name: "wallet_id" })
4707
+ ManyToOne27(() => EscrowWallet, (escrowWallet) => escrowWallet.escrowWalletTransactions),
4708
+ JoinColumn27({ name: "wallet_id" })
4595
4709
  ], EscrowWalletTransaction.prototype, "escrowWallet", 2);
4596
4710
  __decorateClass([
4597
- Column28({ name: "invoice_id", type: "integer", nullable: true }),
4598
- Index20()
4711
+ Column29({ name: "invoice_id", type: "integer", nullable: true }),
4712
+ Index21()
4599
4713
  ], EscrowWalletTransaction.prototype, "invoiceId", 2);
4600
4714
  __decorateClass([
4601
- OneToOne7(() => Invoice, (invoice) => invoice.escrowWalletTransaction),
4602
- JoinColumn26({ name: "invoice_id" })
4715
+ OneToOne8(() => Invoice, (invoice) => invoice.escrowWalletTransaction),
4716
+ JoinColumn27({ name: "invoice_id" })
4603
4717
  ], EscrowWalletTransaction.prototype, "invoice", 2);
4604
4718
  __decorateClass([
4605
- Column28({ name: "amount", type: "bigint", nullable: true })
4719
+ Column29({ name: "amount", type: "bigint", nullable: true })
4606
4720
  ], EscrowWalletTransaction.prototype, "amount", 2);
4607
4721
  __decorateClass([
4608
- Column28({ name: "escrow_type", type: "enum", enum: EscrowWalletTransactionTypeEnum })
4722
+ Column29({ name: "escrow_type", type: "enum", enum: EscrowWalletTransactionTypeEnum })
4609
4723
  ], EscrowWalletTransaction.prototype, "escrowType", 2);
4610
4724
  __decorateClass([
4611
- Column28({ name: "description", type: "text", nullable: true })
4725
+ Column29({ name: "description", type: "text", nullable: true })
4612
4726
  ], EscrowWalletTransaction.prototype, "description", 2);
4613
4727
  __decorateClass([
4614
- Column28({ name: "completed_at", type: "timestamptz", nullable: true })
4728
+ Column29({ name: "completed_at", type: "timestamptz", nullable: true })
4615
4729
  ], EscrowWalletTransaction.prototype, "completedAt", 2);
4616
4730
  __decorateClass([
4617
- Column28({ name: "escrow_transaction_for", type: "enum", enum: EscrowWalletTransactionForEnum })
4731
+ Column29({ name: "escrow_transaction_for", type: "enum", enum: EscrowWalletTransactionForEnum })
4618
4732
  ], EscrowWalletTransaction.prototype, "escrowTransactionFor", 2);
4619
4733
  __decorateClass([
4620
- Column28({ name: "meta_data", type: "varchar", nullable: true })
4734
+ Column29({ name: "meta_data", type: "varchar", nullable: true })
4621
4735
  ], EscrowWalletTransaction.prototype, "metaData", 2);
4622
4736
  EscrowWalletTransaction = __decorateClass([
4623
- Entity27("escrow_wallet_transactions")
4737
+ Entity28("escrow_wallet_transactions")
4624
4738
  ], EscrowWalletTransaction);
4625
4739
 
4626
4740
  // src/entities/escrow-wallet.entity.ts
4627
4741
  var EscrowWallet = class extends BaseEntity {
4628
4742
  };
4629
4743
  __decorateClass([
4630
- Column29({ name: "job_id", type: "integer", nullable: true }),
4631
- Index21()
4744
+ Column30({ name: "job_id", type: "integer", nullable: true }),
4745
+ Index22()
4632
4746
  ], EscrowWallet.prototype, "jobId", 2);
4633
4747
  __decorateClass([
4634
- ManyToOne27(() => Job, (job) => job.escrowWallets),
4635
- JoinColumn27({ name: "job_id" })
4748
+ ManyToOne28(() => Job, (job) => job.escrowWallets),
4749
+ JoinColumn28({ name: "job_id" })
4636
4750
  ], EscrowWallet.prototype, "job", 2);
4637
4751
  __decorateClass([
4638
- Column29({ name: "client_id", type: "integer", nullable: true }),
4639
- Index21()
4752
+ Column30({ name: "client_id", type: "integer", nullable: true }),
4753
+ Index22()
4640
4754
  ], EscrowWallet.prototype, "clientId", 2);
4641
4755
  __decorateClass([
4642
- ManyToOne27(() => User, (user) => user.clientEscrowWallets),
4643
- JoinColumn27({ name: "client_id" })
4756
+ ManyToOne28(() => User, (user) => user.clientEscrowWallets),
4757
+ JoinColumn28({ name: "client_id" })
4644
4758
  ], EscrowWallet.prototype, "client", 2);
4645
4759
  __decorateClass([
4646
- Column29({ name: "freelancer_id", type: "integer", nullable: true }),
4647
- Index21()
4760
+ Column30({ name: "freelancer_id", type: "integer", nullable: true }),
4761
+ Index22()
4648
4762
  ], EscrowWallet.prototype, "freelancerId", 2);
4649
4763
  __decorateClass([
4650
- ManyToOne27(() => User, (user) => user.freelancerEscrowWallets),
4651
- JoinColumn27({ name: "freelancer_id" })
4764
+ ManyToOne28(() => User, (user) => user.freelancerEscrowWallets),
4765
+ JoinColumn28({ name: "freelancer_id" })
4652
4766
  ], EscrowWallet.prototype, "freelancer", 2);
4653
4767
  __decorateClass([
4654
- Column29({ name: "contract_id", type: "integer", nullable: true }),
4655
- Index21()
4768
+ Column30({ name: "contract_id", type: "integer", nullable: true }),
4769
+ Index22()
4656
4770
  ], EscrowWallet.prototype, "contractId", 2);
4657
4771
  __decorateClass([
4658
- OneToOne8(() => Contract, (contract) => contract.escrowWallet),
4659
- JoinColumn27({ name: "contract_id" })
4772
+ OneToOne9(() => Contract, (contract) => contract.escrowWallet),
4773
+ JoinColumn28({ name: "contract_id" })
4660
4774
  ], EscrowWallet.prototype, "contract", 2);
4661
4775
  __decorateClass([
4662
- Column29({ name: "wallet_balance", type: "varchar", default: "0" })
4776
+ Column30({ name: "wallet_balance", type: "varchar", default: "0" })
4663
4777
  ], EscrowWallet.prototype, "escrowBalance", 2);
4664
4778
  __decorateClass([
4665
- Column29({ name: "metadata", type: "jsonb", nullable: true })
4779
+ Column30({ name: "metadata", type: "jsonb", nullable: true })
4666
4780
  ], EscrowWallet.prototype, "metadata", 2);
4667
4781
  __decorateClass([
4668
- OneToMany11(() => EscrowWalletTransaction, (escrowWalletTransaction) => escrowWalletTransaction.escrowWallet)
4782
+ OneToMany12(() => EscrowWalletTransaction, (escrowWalletTransaction) => escrowWalletTransaction.escrowWallet)
4669
4783
  ], EscrowWallet.prototype, "escrowWalletTransactions", 2);
4670
4784
  EscrowWallet = __decorateClass([
4671
- Entity28("escrow_wallets")
4785
+ Entity29("escrow_wallets")
4672
4786
  ], EscrowWallet);
4673
4787
 
4674
- // src/entities/contract-summary.entity.ts
4675
- import { Entity as Entity29, Column as Column30, Index as Index22, ManyToOne as ManyToOne28, JoinColumn as JoinColumn28, OneToMany as OneToMany12 } from "typeorm";
4676
- var ContractSummaryStatusEnum = /* @__PURE__ */ ((ContractSummaryStatusEnum2) => {
4677
- ContractSummaryStatusEnum2["PENDING"] = "PENDING";
4678
- ContractSummaryStatusEnum2["ACTIVE"] = "ACTIVE";
4679
- ContractSummaryStatusEnum2["COMPLETED"] = "COMPLETED";
4680
- return ContractSummaryStatusEnum2;
4681
- })(ContractSummaryStatusEnum || {});
4682
- var ContractSummary = class extends BaseEntity {
4683
- };
4684
- __decorateClass([
4685
- Column30({ name: "job_id", type: "integer", nullable: true }),
4686
- Index22()
4687
- ], ContractSummary.prototype, "jobId", 2);
4688
- __decorateClass([
4689
- ManyToOne28(() => Job, (job) => job.contracts),
4690
- JoinColumn28({ name: "job_id" })
4691
- ], ContractSummary.prototype, "job", 2);
4692
- __decorateClass([
4693
- Column30({ name: "client_id", type: "integer", nullable: true }),
4694
- Index22()
4695
- ], ContractSummary.prototype, "clientId", 2);
4696
- __decorateClass([
4697
- ManyToOne28(() => User, (user) => user.clientContracts),
4698
- JoinColumn28({ name: "client_id" })
4699
- ], ContractSummary.prototype, "client", 2);
4700
- __decorateClass([
4701
- Column30({ name: "freelancer_id", type: "integer", nullable: true }),
4702
- Index22()
4703
- ], ContractSummary.prototype, "freelancerId", 2);
4704
- __decorateClass([
4705
- ManyToOne28(() => User, (user) => user.freelancerContracts),
4706
- JoinColumn28({ name: "freelancer_id" })
4707
- ], ContractSummary.prototype, "freelancer", 2);
4708
- __decorateClass([
4709
- Column30({
4710
- name: "start_date",
4711
- type: "timestamp with time zone",
4712
- nullable: true
4713
- })
4714
- ], ContractSummary.prototype, "startDate", 2);
4715
- __decorateClass([
4716
- Column30({
4717
- name: "end_date",
4718
- type: "timestamp with time zone",
4719
- nullable: true
4720
- })
4721
- ], ContractSummary.prototype, "endDate", 2);
4722
- __decorateClass([
4723
- Column30({ name: "duration", type: "integer", nullable: true })
4724
- ], ContractSummary.prototype, "duration", 2);
4725
- __decorateClass([
4726
- Column30({
4727
- name: "contract_value",
4728
- type: "decimal",
4729
- precision: 10,
4730
- scale: 2,
4731
- nullable: true
4732
- })
4733
- ], ContractSummary.prototype, "contractValue", 2);
4734
- __decorateClass([
4735
- Column30({ name: "invoicing_cycle", type: "varchar", nullable: true })
4736
- ], ContractSummary.prototype, "invoicingCycle", 2);
4737
- __decorateClass([
4738
- Column30({ name: "is_msa_signed", type: "boolean", default: false })
4739
- ], ContractSummary.prototype, "isMsaSigned", 2);
4740
- __decorateClass([
4741
- Column30({ name: "is_sow_signed", type: "boolean", default: false })
4742
- ], ContractSummary.prototype, "isSowSigned", 2);
4743
- __decorateClass([
4744
- Column30({ name: "is_escrow_deposited", type: "boolean", default: false })
4745
- ], ContractSummary.prototype, "isEscrowDeposited", 2);
4746
- __decorateClass([
4747
- Column30({
4748
- name: "escrow_deposite_amount",
4749
- type: "decimal",
4750
- precision: 10,
4751
- scale: 2,
4752
- nullable: true
4753
- })
4754
- ], ContractSummary.prototype, "escrowDepositeAmount", 2);
4755
- __decorateClass([
4756
- Column30({
4757
- name: "status",
4758
- type: "enum",
4759
- enum: ContractSummaryStatusEnum,
4760
- nullable: true
4761
- })
4762
- ], ContractSummary.prototype, "status", 2);
4763
- __decorateClass([
4764
- OneToMany12(() => Contract, (contract) => contract.contractSummary, {
4765
- cascade: true
4766
- })
4767
- ], ContractSummary.prototype, "contracts", 2);
4768
- ContractSummary = __decorateClass([
4769
- Entity29("contract_summaries")
4770
- ], ContractSummary);
4771
-
4772
4788
  // src/entities/contract.entity.ts
4773
4789
  var ContractStatusEnum = /* @__PURE__ */ ((ContractStatusEnum2) => {
4774
4790
  ContractStatusEnum2["GENERATED"] = "GENERATED";
@@ -4928,7 +4944,7 @@ __decorateClass([
4928
4944
  Column31({ name: "meta_data", type: "jsonb", nullable: true })
4929
4945
  ], Contract.prototype, "metaData", 2);
4930
4946
  __decorateClass([
4931
- OneToOne9(() => EscrowWallet, (escrowWallet) => escrowWallet.contract)
4947
+ OneToOne10(() => EscrowWallet, (escrowWallet) => escrowWallet.contract)
4932
4948
  ], Contract.prototype, "escrowWallet", 2);
4933
4949
  Contract = __decorateClass([
4934
4950
  Entity30("contracts")
@@ -6608,7 +6624,7 @@ StripeTransaction = __decorateClass([
6608
6624
  ], StripeTransaction);
6609
6625
 
6610
6626
  // src/entities/wallet.entity.ts
6611
- import { Entity as Entity62, Column as Column63, Index as Index55, JoinColumn as JoinColumn58, OneToOne as OneToOne11, OneToMany as OneToMany22 } from "typeorm";
6627
+ import { Entity as Entity62, Column as Column63, Index as Index55, JoinColumn as JoinColumn58, OneToOne as OneToOne12, OneToMany as OneToMany22 } from "typeorm";
6612
6628
 
6613
6629
  // src/entities/wallet-transaction.entity.ts
6614
6630
  import { Entity as Entity61, Column as Column62, Index as Index54, ManyToOne as ManyToOne57, JoinColumn as JoinColumn57 } from "typeorm";
@@ -6689,7 +6705,7 @@ __decorateClass([
6689
6705
  Index55()
6690
6706
  ], Wallet.prototype, "userId", 2);
6691
6707
  __decorateClass([
6692
- OneToOne11(() => User, (user) => user.wallet),
6708
+ OneToOne12(() => User, (user) => user.wallet),
6693
6709
  JoinColumn58({ name: "user_id" })
6694
6710
  ], Wallet.prototype, "user", 2);
6695
6711
  __decorateClass([
@@ -6918,7 +6934,7 @@ __decorateClass([
6918
6934
  OneToMany23(() => SenseloafLog, (senseloafLog) => senseloafLog.user)
6919
6935
  ], User.prototype, "senseloafLogs", 2);
6920
6936
  __decorateClass([
6921
- OneToOne12(() => CompanyProfile, (companyProfile) => companyProfile.user)
6937
+ OneToOne13(() => CompanyProfile, (companyProfile) => companyProfile.user)
6922
6938
  ], User.prototype, "companyProfile", 2);
6923
6939
  __decorateClass([
6924
6940
  OneToMany23(() => CompanySkill, (companySkill) => companySkill.user)
@@ -6936,13 +6952,13 @@ __decorateClass([
6936
6952
  OneToMany23(() => F2FInterview, (F2FInterview2) => F2FInterview2.interviwer)
6937
6953
  ], User.prototype, "clientF2FInterviews", 2);
6938
6954
  __decorateClass([
6939
- OneToOne12(
6955
+ OneToOne13(
6940
6956
  () => FreelancerProfile,
6941
6957
  (freelancerProfile) => freelancerProfile.user
6942
6958
  )
6943
6959
  ], User.prototype, "freelancerProfile", 2);
6944
6960
  __decorateClass([
6945
- OneToOne12(() => FreelancerResume, (freelancerResume) => freelancerResume.user)
6961
+ OneToOne13(() => FreelancerResume, (freelancerResume) => freelancerResume.user)
6946
6962
  ], User.prototype, "freelancerResume", 2);
6947
6963
  __decorateClass([
6948
6964
  OneToMany23(
@@ -7005,7 +7021,7 @@ __decorateClass([
7005
7021
  )
7006
7022
  ], User.prototype, "freelancerFramework", 2);
7007
7023
  __decorateClass([
7008
- OneToOne12(
7024
+ OneToOne13(
7009
7025
  () => FreelancerDeclaration,
7010
7026
  (freelancerDeclaration) => freelancerDeclaration.user
7011
7027
  )
@@ -7093,7 +7109,7 @@ __decorateClass([
7093
7109
  OneToMany23(() => EscrowWallet, (escrowWallet) => escrowWallet.freelancer)
7094
7110
  ], User.prototype, "freelancerEscrowWallets", 2);
7095
7111
  __decorateClass([
7096
- OneToOne12(() => Signature, (signature) => signature.user)
7112
+ OneToOne13(() => Signature, (signature) => signature.user)
7097
7113
  ], User.prototype, "signatures", 2);
7098
7114
  __decorateClass([
7099
7115
  OneToMany23(() => Timesheet, (timesheet) => timesheet.client)
@@ -7126,7 +7142,7 @@ __decorateClass([
7126
7142
  OneToMany23(() => Dispute, (dispute) => dispute.respondent, { cascade: true })
7127
7143
  ], User.prototype, "respondentDisputes", 2);
7128
7144
  __decorateClass([
7129
- OneToOne12(() => Wallet, (wallet) => wallet.user)
7145
+ OneToOne13(() => Wallet, (wallet) => wallet.user)
7130
7146
  ], User.prototype, "wallet", 2);
7131
7147
  __decorateClass([
7132
7148
  OneToMany23(() => StripeTransaction, (stripeTransaction) => stripeTransaction.user, { cascade: true })
@@ -9679,7 +9695,7 @@ var RejectF2FInterviewRescheduleRequestDto = class {
9679
9695
  __decorateClass([
9680
9696
  IsOptional58(),
9681
9697
  IsString60()
9682
- ], RejectF2FInterviewRescheduleRequestDto.prototype, "reason", 2);
9698
+ ], RejectF2FInterviewRescheduleRequestDto.prototype, "clientRejectReason", 2);
9683
9699
 
9684
9700
  // src/modules/interview/dto/capture-ai-interview-result-public.dto.ts
9685
9701
  import { IsNotEmpty as IsNotEmpty85, IsString as IsString61, IsOptional as IsOptional59, IsObject as IsObject6 } from "class-validator";
@@ -10673,14 +10689,1113 @@ __decorateClass([
10673
10689
  IsNotEmpty119({ message: "Invoice UUID is required." })
10674
10690
  ], DebitCommissionFteHiringDto.prototype, "invoiceUuid", 2);
10675
10691
 
10692
+ // src/modules/discord/discord-alert.interface.ts
10693
+ var AlertSeverity = /* @__PURE__ */ ((AlertSeverity2) => {
10694
+ AlertSeverity2["CRITICAL"] = "CRITICAL";
10695
+ AlertSeverity2["ERROR"] = "ERROR";
10696
+ AlertSeverity2["WARNING"] = "WARNING";
10697
+ AlertSeverity2["SECURITY"] = "SECURITY";
10698
+ AlertSeverity2["INFO"] = "INFO";
10699
+ AlertSeverity2["SUCCESS"] = "SUCCESS";
10700
+ return AlertSeverity2;
10701
+ })(AlertSeverity || {});
10702
+ var AlertCategory = /* @__PURE__ */ ((AlertCategory2) => {
10703
+ AlertCategory2["CRITICAL_ERROR"] = "CRITICAL_ERROR";
10704
+ AlertCategory2["DATABASE"] = "DATABASE";
10705
+ AlertCategory2["PAYMENT"] = "PAYMENT";
10706
+ AlertCategory2["SECURITY"] = "SECURITY";
10707
+ AlertCategory2["PERFORMANCE"] = "PERFORMANCE";
10708
+ AlertCategory2["BUSINESS_EVENT"] = "BUSINESS_EVENT";
10709
+ return AlertCategory2;
10710
+ })(AlertCategory || {});
10711
+
10712
+ // src/modules/discord/alert-builder.ts
10713
+ var AlertBuilder = class {
10714
+ /**
10715
+ * Builds a Discord embed from alert options
10716
+ *
10717
+ * @param options - Alert configuration
10718
+ * @returns Discord-compatible embed object
10719
+ */
10720
+ static buildEmbed(options) {
10721
+ const fields = [];
10722
+ if (options.metadata?.microservice) {
10723
+ fields.push({
10724
+ name: "\u{1F527} Microservice",
10725
+ value: this.formatValue(options.metadata.microservice),
10726
+ inline: true
10727
+ });
10728
+ }
10729
+ if (options.metadata?.environment) {
10730
+ fields.push({
10731
+ name: "\u{1F30D} Environment",
10732
+ value: this.formatEnvironment(options.metadata.environment),
10733
+ inline: true
10734
+ });
10735
+ }
10736
+ fields.push({
10737
+ name: "\u23F0 Time",
10738
+ value: this.formatTimestamp(/* @__PURE__ */ new Date()),
10739
+ inline: true
10740
+ });
10741
+ if (options.fields) {
10742
+ Object.entries(options.fields).forEach(([key, value]) => {
10743
+ if (value !== null && value !== void 0 && value !== "") {
10744
+ fields.push({
10745
+ name: key,
10746
+ value: this.formatValue(value),
10747
+ inline: this.shouldBeInline(key)
10748
+ });
10749
+ }
10750
+ });
10751
+ }
10752
+ if (options.metadata?.userId) {
10753
+ fields.push({
10754
+ name: "\u{1F464} User ID",
10755
+ value: `\`${options.metadata.userId}\``,
10756
+ inline: true
10757
+ });
10758
+ }
10759
+ if (options.metadata?.correlationId) {
10760
+ fields.push({
10761
+ name: "\u{1F50D} Correlation ID",
10762
+ value: `\`${options.metadata.correlationId}\``,
10763
+ inline: false
10764
+ });
10765
+ }
10766
+ if (options.metadata?.requestPath) {
10767
+ const method = options.metadata.requestMethod || "UNKNOWN";
10768
+ fields.push({
10769
+ name: "\u{1F310} Request",
10770
+ value: `\`${method} ${options.metadata.requestPath}\``,
10771
+ inline: false
10772
+ });
10773
+ }
10774
+ if (options.metadata?.errorStack && (options.severity === "CRITICAL" /* CRITICAL */ || options.severity === "ERROR" /* ERROR */)) {
10775
+ const truncatedStack = this.truncateStack(options.metadata.errorStack);
10776
+ fields.push({
10777
+ name: "\u{1F4CB} Stack Trace",
10778
+ value: `\`\`\`${truncatedStack}\`\`\``,
10779
+ inline: false
10780
+ });
10781
+ }
10782
+ const embed = {
10783
+ title: this.formatTitle(options.severity, options.title),
10784
+ description: this.truncateDescription(options.description),
10785
+ color: this.COLORS[options.severity],
10786
+ fields,
10787
+ footer: {
10788
+ text: this.buildFooter(options)
10789
+ },
10790
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
10791
+ };
10792
+ return embed;
10793
+ }
10794
+ /**
10795
+ * Formats the embed title with severity emoji
10796
+ */
10797
+ static formatTitle(severity, title) {
10798
+ const emoji = this.SEVERITY_EMOJIS[severity];
10799
+ return `${emoji} ${title}`;
10800
+ }
10801
+ /**
10802
+ * Formats environment with visual emphasis
10803
+ */
10804
+ static formatEnvironment(env) {
10805
+ const formatted = env.toUpperCase();
10806
+ if (env.toLowerCase() === "production") {
10807
+ return `\u{1F6A8} **${formatted}**`;
10808
+ }
10809
+ return `**${formatted}**`;
10810
+ }
10811
+ /**
10812
+ * Formats a timestamp for display
10813
+ */
10814
+ static formatTimestamp(date) {
10815
+ return `<t:${Math.floor(date.getTime() / 1e3)}:F>`;
10816
+ }
10817
+ /**
10818
+ * Formats a field value with appropriate styling
10819
+ */
10820
+ static formatValue(value) {
10821
+ if (typeof value === "boolean") {
10822
+ return value ? "\u2705 Yes" : "\u274C No";
10823
+ }
10824
+ if (typeof value === "number") {
10825
+ return value.toLocaleString();
10826
+ }
10827
+ const strValue = String(value);
10828
+ if (strValue.startsWith("http://") || strValue.startsWith("https://")) {
10829
+ return strValue;
10830
+ }
10831
+ if (this.looksLikeCode(strValue)) {
10832
+ return `\`${strValue}\``;
10833
+ }
10834
+ return strValue;
10835
+ }
10836
+ /**
10837
+ * Determines if a field should be displayed inline
10838
+ * Long values or certain field types should be full-width
10839
+ */
10840
+ static shouldBeInline(fieldName) {
10841
+ const fullWidthFields = [
10842
+ "stack",
10843
+ "trace",
10844
+ "error",
10845
+ "message",
10846
+ "description",
10847
+ "url",
10848
+ "path",
10849
+ "query"
10850
+ ];
10851
+ const lowerFieldName = fieldName.toLowerCase();
10852
+ return !fullWidthFields.some((keyword) => lowerFieldName.includes(keyword));
10853
+ }
10854
+ /**
10855
+ * Checks if a string looks like code/ID that should be formatted
10856
+ */
10857
+ static looksLikeCode(value) {
10858
+ if (/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(value)) {
10859
+ return true;
10860
+ }
10861
+ if (/^[A-Z0-9_-]{3,20}$/i.test(value) && value.includes("_")) {
10862
+ return true;
10863
+ }
10864
+ return false;
10865
+ }
10866
+ /**
10867
+ * Truncates error stack trace to fit Discord limits
10868
+ * Discord field value limit: 1024 characters
10869
+ */
10870
+ static truncateStack(stack) {
10871
+ const maxLength = 900;
10872
+ if (stack.length <= maxLength) {
10873
+ return stack;
10874
+ }
10875
+ const truncated = stack.substring(0, maxLength);
10876
+ const lastNewline = truncated.lastIndexOf("\n");
10877
+ if (lastNewline > maxLength - 100) {
10878
+ return truncated.substring(0, lastNewline) + "\n... (truncated)";
10879
+ }
10880
+ return truncated + "... (truncated)";
10881
+ }
10882
+ /**
10883
+ * Truncates description to fit Discord limits
10884
+ * Discord embed description limit: 4096 characters
10885
+ */
10886
+ static truncateDescription(description) {
10887
+ const maxLength = 4e3;
10888
+ if (description.length <= maxLength) {
10889
+ return description;
10890
+ }
10891
+ return description.substring(0, maxLength) + "... (truncated)";
10892
+ }
10893
+ /**
10894
+ * Builds the footer text with server information
10895
+ */
10896
+ static buildFooter(options) {
10897
+ const parts = [];
10898
+ if (options.metadata?.hostname) {
10899
+ parts.push(`Server: ${options.metadata.hostname}`);
10900
+ }
10901
+ if (parts.length === 0) {
10902
+ parts.push("ExpertsHub Monitoring System");
10903
+ }
10904
+ return parts.join(" \u2022 ");
10905
+ }
10906
+ /**
10907
+ * Validates an embed structure before sending
10908
+ * Ensures it meets Discord API requirements
10909
+ *
10910
+ * @param embed - Embed to validate
10911
+ * @returns Validation errors (empty array if valid)
10912
+ */
10913
+ static validateEmbed(embed) {
10914
+ const errors = [];
10915
+ const LIMITS = {
10916
+ TITLE: 256,
10917
+ DESCRIPTION: 4096,
10918
+ FIELDS: 25,
10919
+ FIELD_NAME: 256,
10920
+ FIELD_VALUE: 1024,
10921
+ FOOTER_TEXT: 2048,
10922
+ TOTAL_CHARACTERS: 6e3
10923
+ };
10924
+ if (embed.title && embed.title.length > LIMITS.TITLE) {
10925
+ errors.push(`Title exceeds ${LIMITS.TITLE} characters`);
10926
+ }
10927
+ if (embed.description && embed.description.length > LIMITS.DESCRIPTION) {
10928
+ errors.push(`Description exceeds ${LIMITS.DESCRIPTION} characters`);
10929
+ }
10930
+ if (embed.fields && embed.fields.length > LIMITS.FIELDS) {
10931
+ errors.push(`Too many fields (max ${LIMITS.FIELDS})`);
10932
+ }
10933
+ embed.fields?.forEach((field, index) => {
10934
+ if (field.name.length > LIMITS.FIELD_NAME) {
10935
+ errors.push(`Field ${index} name exceeds ${LIMITS.FIELD_NAME} characters`);
10936
+ }
10937
+ if (field.value.length > LIMITS.FIELD_VALUE) {
10938
+ errors.push(`Field ${index} value exceeds ${LIMITS.FIELD_VALUE} characters`);
10939
+ }
10940
+ });
10941
+ if (embed.footer && embed.footer.text.length > LIMITS.FOOTER_TEXT) {
10942
+ errors.push(`Footer exceeds ${LIMITS.FOOTER_TEXT} characters`);
10943
+ }
10944
+ const totalChars = (embed.title?.length || 0) + (embed.description?.length || 0) + (embed.footer?.text.length || 0) + (embed.fields?.reduce((sum, f) => sum + f.name.length + f.value.length, 0) || 0);
10945
+ if (totalChars > LIMITS.TOTAL_CHARACTERS) {
10946
+ errors.push(`Total characters (${totalChars}) exceed limit of ${LIMITS.TOTAL_CHARACTERS}`);
10947
+ }
10948
+ return errors;
10949
+ }
10950
+ };
10951
+ /**
10952
+ * Color mapping for different severity levels
10953
+ * Colors are in decimal format (Discord API requirement)
10954
+ *
10955
+ * Hex to Decimal conversion:
10956
+ * - Red (#FF0000) = 16711680
10957
+ * - Orange (#FF6B00) = 16739072
10958
+ * - Yellow (#FFFF00) = 16776960
10959
+ * - Purple (#9B59B6) = 10181046
10960
+ * - Blue (#3498DB) = 3447003
10961
+ * - Green (#00FF00) = 65280
10962
+ */
10963
+ AlertBuilder.COLORS = {
10964
+ ["CRITICAL" /* CRITICAL */]: 16711680,
10965
+ // Red
10966
+ ["ERROR" /* ERROR */]: 16739072,
10967
+ // Orange
10968
+ ["WARNING" /* WARNING */]: 16776960,
10969
+ // Yellow
10970
+ ["SECURITY" /* SECURITY */]: 10181046,
10971
+ // Purple
10972
+ ["INFO" /* INFO */]: 3447003,
10973
+ // Blue
10974
+ ["SUCCESS" /* SUCCESS */]: 65280
10975
+ // Green
10976
+ };
10977
+ /**
10978
+ * Emoji mapping for severity levels
10979
+ * Makes alerts more visually distinct and easier to scan
10980
+ */
10981
+ AlertBuilder.SEVERITY_EMOJIS = {
10982
+ ["CRITICAL" /* CRITICAL */]: "\u{1F534}",
10983
+ ["ERROR" /* ERROR */]: "\u{1F7E0}",
10984
+ ["WARNING" /* WARNING */]: "\u{1F7E1}",
10985
+ ["SECURITY" /* SECURITY */]: "\u{1F7E3}",
10986
+ ["INFO" /* INFO */]: "\u{1F535}",
10987
+ ["SUCCESS" /* SUCCESS */]: "\u{1F7E2}"
10988
+ };
10989
+
10990
+ // src/modules/discord/rate-limiter.ts
10991
+ var RateLimiter = class {
10992
+ /**
10993
+ * Creates a new rate limiter instance
10994
+ *
10995
+ * @param maxAlertsPerMinute - Maximum alerts allowed per minute per webhook (default: 20)
10996
+ * Discord allows 30/min, we use 20 for safety margin
10997
+ */
10998
+ constructor(maxAlertsPerMinute = 20) {
10999
+ this.maxAlertsPerMinute = maxAlertsPerMinute;
11000
+ /**
11001
+ * Stores timestamps of recent alerts per webhook URL
11002
+ * Key: webhook URL
11003
+ * Value: array of timestamps (milliseconds since epoch)
11004
+ */
11005
+ this.alertCounts = /* @__PURE__ */ new Map();
11006
+ /**
11007
+ * Stores timestamps of recent unique alerts for deduplication
11008
+ * Key: alert signature (generated from severity, category, title, microservice)
11009
+ * Value: timestamp when alert was last sent (milliseconds since epoch)
11010
+ */
11011
+ this.recentAlerts = /* @__PURE__ */ new Map();
11012
+ /**
11013
+ * Time window for deduplication (5 minutes in milliseconds)
11014
+ * Same alert within this window will be skipped
11015
+ */
11016
+ this.deduplicationWindow = 5 * 60 * 1e3;
11017
+ // 5 minutes
11018
+ /**
11019
+ * Cleanup interval (10 minutes in milliseconds)
11020
+ * Old entries are cleaned up periodically to prevent memory leaks
11021
+ */
11022
+ this.cleanupInterval = 10 * 60 * 1e3;
11023
+ this.startCleanup();
11024
+ }
11025
+ /**
11026
+ * Checks if an alert can be sent to a specific webhook
11027
+ * Based on rate limiting (alerts per minute)
11028
+ *
11029
+ * @param webhookUrl - The webhook URL to check
11030
+ * @returns true if alert can be sent, false if rate limit exceeded
11031
+ *
11032
+ * @example
11033
+ * ```typescript
11034
+ * if (rateLimiter.canSendAlert(webhookUrl)) {
11035
+ * await sendToDiscord(webhookUrl, alert);
11036
+ * } else {
11037
+ * console.log('Rate limit exceeded, skipping alert');
11038
+ * }
11039
+ * ```
11040
+ */
11041
+ canSendAlert(webhookUrl) {
11042
+ const now = Date.now();
11043
+ const oneMinuteAgo = now - 6e4;
11044
+ let timestamps = this.alertCounts.get(webhookUrl) || [];
11045
+ timestamps = timestamps.filter((timestamp) => timestamp > oneMinuteAgo);
11046
+ this.alertCounts.set(webhookUrl, timestamps);
11047
+ return timestamps.length < this.maxAlertsPerMinute;
11048
+ }
11049
+ /**
11050
+ * Records that an alert was sent to a webhook
11051
+ * Used for rate limiting tracking
11052
+ *
11053
+ * @param webhookUrl - The webhook URL where alert was sent
11054
+ *
11055
+ * @example
11056
+ * ```typescript
11057
+ * await sendToDiscord(webhookUrl, alert);
11058
+ * rateLimiter.recordAlert(webhookUrl);
11059
+ * ```
11060
+ */
11061
+ recordAlert(webhookUrl) {
11062
+ const now = Date.now();
11063
+ const timestamps = this.alertCounts.get(webhookUrl) || [];
11064
+ timestamps.push(now);
11065
+ this.alertCounts.set(webhookUrl, timestamps);
11066
+ }
11067
+ /**
11068
+ * Checks if an alert is a duplicate
11069
+ * Based on alert signature and deduplication window
11070
+ *
11071
+ * @param alertKey - Unique key identifying the alert
11072
+ * @returns true if duplicate (should skip), false if unique (should send)
11073
+ *
11074
+ * @example
11075
+ * ```typescript
11076
+ * const alertKey = rateLimiter.generateAlertKey(options);
11077
+ * if (rateLimiter.isDuplicate(alertKey)) {
11078
+ * console.log('Duplicate alert, skipping');
11079
+ * return;
11080
+ * }
11081
+ * ```
11082
+ */
11083
+ isDuplicate(alertKey) {
11084
+ const now = Date.now();
11085
+ const lastSent = this.recentAlerts.get(alertKey);
11086
+ if (lastSent && now - lastSent < this.deduplicationWindow) {
11087
+ return true;
11088
+ }
11089
+ this.recentAlerts.set(alertKey, now);
11090
+ return false;
11091
+ }
11092
+ /**
11093
+ * Generates a unique key for an alert based on its characteristics
11094
+ * Used for deduplication
11095
+ *
11096
+ * The key includes:
11097
+ * - Severity (CRITICAL, ERROR, etc.)
11098
+ * - Category (DATABASE, PAYMENT, etc.)
11099
+ * - Title (error message or event name)
11100
+ * - Microservice name (to distinguish same error from different services)
11101
+ *
11102
+ * @param components - Components to build the key from
11103
+ * @returns Unique alert key string
11104
+ *
11105
+ * @example
11106
+ * ```typescript
11107
+ * const key = rateLimiter.generateAlertKey({
11108
+ * severity: AlertSeverity.CRITICAL,
11109
+ * category: AlertCategory.DATABASE,
11110
+ * title: 'Connection failed',
11111
+ * microservice: 'user-microservice'
11112
+ * });
11113
+ * // Returns: "CRITICAL:DATABASE:Connection failed:user-microservice"
11114
+ * ```
11115
+ */
11116
+ generateAlertKey(components) {
11117
+ const parts = [
11118
+ components.severity,
11119
+ components.category,
11120
+ components.title,
11121
+ components.microservice || "unknown"
11122
+ ];
11123
+ return parts.join(":");
11124
+ }
11125
+ /**
11126
+ * Gets statistics about current rate limiter state
11127
+ * Useful for monitoring and debugging
11128
+ *
11129
+ * @returns Statistics object
11130
+ */
11131
+ getStats() {
11132
+ const now = Date.now();
11133
+ const oneMinuteAgo = now - 6e4;
11134
+ const webhookStats = Array.from(this.alertCounts.entries()).map(
11135
+ ([webhookUrl, timestamps]) => {
11136
+ const recentCount = timestamps.filter((t) => t > oneMinuteAgo).length;
11137
+ return {
11138
+ webhook: this.maskWebhookUrl(webhookUrl),
11139
+ alertsInLastMinute: recentCount,
11140
+ remainingCapacity: this.maxAlertsPerMinute - recentCount
11141
+ };
11142
+ }
11143
+ );
11144
+ return {
11145
+ totalWebhooks: this.alertCounts.size,
11146
+ totalUniqueAlerts: this.recentAlerts.size,
11147
+ webhookStats
11148
+ };
11149
+ }
11150
+ /**
11151
+ * Masks webhook URL for safe logging
11152
+ * Hides the token part for security
11153
+ *
11154
+ * @param url - Full webhook URL
11155
+ * @returns Masked URL
11156
+ *
11157
+ * @example
11158
+ * Input: "https://discord.com/api/webhooks/123456/abcdef123456"
11159
+ * Output: "https://discord.com/api/webhooks/123456/***"
11160
+ */
11161
+ maskWebhookUrl(url) {
11162
+ try {
11163
+ const parts = url.split("/");
11164
+ if (parts.length >= 2) {
11165
+ parts[parts.length - 1] = "***";
11166
+ }
11167
+ return parts.join("/");
11168
+ } catch {
11169
+ return "***";
11170
+ }
11171
+ }
11172
+ /**
11173
+ * Clears all rate limiting and deduplication data
11174
+ * Useful for testing or manual resets
11175
+ */
11176
+ clear() {
11177
+ this.alertCounts.clear();
11178
+ this.recentAlerts.clear();
11179
+ }
11180
+ /**
11181
+ * Starts periodic cleanup to prevent memory leaks
11182
+ * Removes old entries from both maps
11183
+ */
11184
+ startCleanup() {
11185
+ this.cleanupTimer = setInterval(() => {
11186
+ this.cleanup();
11187
+ }, this.cleanupInterval);
11188
+ if (this.cleanupTimer.unref) {
11189
+ this.cleanupTimer.unref();
11190
+ }
11191
+ }
11192
+ /**
11193
+ * Performs cleanup of old entries
11194
+ * Removes timestamps older than needed for tracking
11195
+ */
11196
+ cleanup() {
11197
+ const now = Date.now();
11198
+ const oneMinuteAgo = now - 6e4;
11199
+ const deduplicationCutoff = now - this.deduplicationWindow;
11200
+ for (const [webhookUrl, timestamps] of this.alertCounts.entries()) {
11201
+ const filtered = timestamps.filter((t) => t > oneMinuteAgo);
11202
+ if (filtered.length === 0) {
11203
+ this.alertCounts.delete(webhookUrl);
11204
+ } else {
11205
+ this.alertCounts.set(webhookUrl, filtered);
11206
+ }
11207
+ }
11208
+ for (const [alertKey, timestamp] of this.recentAlerts.entries()) {
11209
+ if (timestamp < deduplicationCutoff) {
11210
+ this.recentAlerts.delete(alertKey);
11211
+ }
11212
+ }
11213
+ }
11214
+ /**
11215
+ * Stops the cleanup timer
11216
+ * Should be called when the rate limiter is no longer needed
11217
+ */
11218
+ destroy() {
11219
+ if (this.cleanupTimer) {
11220
+ clearInterval(this.cleanupTimer);
11221
+ this.cleanupTimer = void 0;
11222
+ }
11223
+ this.clear();
11224
+ }
11225
+ /**
11226
+ * Gets the current deduplication window in milliseconds
11227
+ */
11228
+ getDeduplicationWindow() {
11229
+ return this.deduplicationWindow;
11230
+ }
11231
+ /**
11232
+ * Gets the maximum alerts per minute setting
11233
+ */
11234
+ getMaxAlertsPerMinute() {
11235
+ return this.maxAlertsPerMinute;
11236
+ }
11237
+ /**
11238
+ * Checks if a specific webhook is currently rate limited
11239
+ *
11240
+ * @param webhookUrl - Webhook URL to check
11241
+ * @returns true if rate limited, false otherwise
11242
+ */
11243
+ isRateLimited(webhookUrl) {
11244
+ return !this.canSendAlert(webhookUrl);
11245
+ }
11246
+ /**
11247
+ * Gets remaining capacity for a webhook in the current minute
11248
+ *
11249
+ * @param webhookUrl - Webhook URL to check
11250
+ * @returns Number of alerts that can still be sent this minute
11251
+ */
11252
+ getRemainingCapacity(webhookUrl) {
11253
+ const now = Date.now();
11254
+ const oneMinuteAgo = now - 6e4;
11255
+ const timestamps = this.alertCounts.get(webhookUrl) || [];
11256
+ const recentCount = timestamps.filter((t) => t > oneMinuteAgo).length;
11257
+ return Math.max(0, this.maxAlertsPerMinute - recentCount);
11258
+ }
11259
+ };
11260
+
11261
+ // src/modules/discord/discord-alert.service.ts
11262
+ var DiscordAlertService = class {
11263
+ /**
11264
+ * Creates a new Discord alert service instance
11265
+ *
11266
+ * @param config - Configuration for Discord webhooks
11267
+ *
11268
+ * @example
11269
+ * ```typescript
11270
+ * const discordService = new DiscordAlertService({
11271
+ * errorWebhookUrl: process.env.DISCORD_ERROR_WEBHOOK_URL,
11272
+ * microserviceName: 'user-microservice',
11273
+ * environment: process.env.NODE_ENV,
11274
+ * enabled: true,
11275
+ * rateLimitPerMinute: 20,
11276
+ * });
11277
+ * ```
11278
+ */
11279
+ constructor(config11) {
11280
+ this.alertsSent = 0;
11281
+ this.alertsFailed = 0;
11282
+ this.config = {
11283
+ errorWebhookUrl: config11.errorWebhookUrl || "",
11284
+ securityWebhookUrl: config11.securityWebhookUrl || "",
11285
+ businessWebhookUrl: config11.businessWebhookUrl || "",
11286
+ performanceWebhookUrl: config11.performanceWebhookUrl || "",
11287
+ enabled: config11.enabled !== void 0 ? config11.enabled : true,
11288
+ rateLimitPerMinute: config11.rateLimitPerMinute || 20,
11289
+ environment: config11.environment || "development",
11290
+ microserviceName: config11.microserviceName || "unknown"
11291
+ };
11292
+ this.rateLimiter = new RateLimiter(this.config.rateLimitPerMinute);
11293
+ }
11294
+ /**
11295
+ * Sends an alert to Discord
11296
+ *
11297
+ * This is the main method for sending alerts. It handles:
11298
+ * - Metadata enrichment (adds microservice, environment, hostname)
11299
+ * - Deduplication (skips if same alert sent recently)
11300
+ * - Rate limiting (respects Discord webhook limits)
11301
+ * - Error handling (fails gracefully without crashing)
11302
+ *
11303
+ * @param options - Alert configuration
11304
+ * @returns Promise that resolves when alert is sent (or skipped)
11305
+ *
11306
+ * @example
11307
+ * ```typescript
11308
+ * await discordService.sendAlert({
11309
+ * severity: AlertSeverity.CRITICAL,
11310
+ * category: AlertCategory.DATABASE,
11311
+ * title: 'Database Connection Failed',
11312
+ * description: 'Unable to connect to PostgreSQL',
11313
+ * fields: {
11314
+ * 'Host': 'localhost:5432',
11315
+ * 'Error Code': 'ECONNREFUSED',
11316
+ * },
11317
+ * });
11318
+ * ```
11319
+ */
11320
+ async sendAlert(options) {
11321
+ try {
11322
+ if (!this.config.enabled) {
11323
+ this.log("Discord alerts disabled, skipping");
11324
+ return;
11325
+ }
11326
+ options.metadata = {
11327
+ ...options.metadata,
11328
+ microservice: options.metadata?.microservice || this.config.microserviceName,
11329
+ environment: options.metadata?.environment || this.config.environment,
11330
+ hostname: options.metadata?.hostname || this.getHostname()
11331
+ };
11332
+ const alertKey = this.rateLimiter.generateAlertKey({
11333
+ severity: options.severity,
11334
+ category: options.category,
11335
+ title: options.title,
11336
+ microservice: options.metadata.microservice
11337
+ });
11338
+ if (this.rateLimiter.isDuplicate(alertKey)) {
11339
+ this.log(`Skipping duplicate alert: ${alertKey}`);
11340
+ return;
11341
+ }
11342
+ const webhookUrl = this.getWebhookUrl(options.category);
11343
+ if (!webhookUrl) {
11344
+ this.log(`No webhook URL configured for category: ${options.category}`);
11345
+ return;
11346
+ }
11347
+ if (!this.rateLimiter.canSendAlert(webhookUrl)) {
11348
+ this.log(`Rate limit exceeded for category: ${options.category}`);
11349
+ this.alertsFailed++;
11350
+ return;
11351
+ }
11352
+ const embed = AlertBuilder.buildEmbed(options);
11353
+ const validationErrors = AlertBuilder.validateEmbed(embed);
11354
+ if (validationErrors.length > 0) {
11355
+ this.log(`Embed validation failed: ${validationErrors.join(", ")}`);
11356
+ this.alertsFailed++;
11357
+ return;
11358
+ }
11359
+ const payload = {
11360
+ embeds: [embed],
11361
+ username: `${this.config.microserviceName} Monitor`
11362
+ };
11363
+ await this.sendToWebhook(webhookUrl, payload);
11364
+ this.rateLimiter.recordAlert(webhookUrl);
11365
+ this.alertsSent++;
11366
+ this.log(`Alert sent successfully: ${options.title}`);
11367
+ } catch (error) {
11368
+ this.logError("Failed to send Discord alert", error);
11369
+ this.alertsFailed++;
11370
+ }
11371
+ }
11372
+ /**
11373
+ * Helper method: Send critical error alert
11374
+ *
11375
+ * @param error - Error object
11376
+ * @param context - Additional context information
11377
+ *
11378
+ * @example
11379
+ * ```typescript
11380
+ * try {
11381
+ * await someOperation();
11382
+ * } catch (error) {
11383
+ * await discordService.sendCriticalError(error, {
11384
+ * operation: 'someOperation',
11385
+ * userId: '12345',
11386
+ * });
11387
+ * }
11388
+ * ```
11389
+ */
11390
+ async sendCriticalError(error, context) {
11391
+ await this.sendAlert({
11392
+ severity: "CRITICAL" /* CRITICAL */,
11393
+ category: "CRITICAL_ERROR" /* CRITICAL_ERROR */,
11394
+ title: "Critical Error Occurred",
11395
+ description: error.message,
11396
+ fields: context,
11397
+ metadata: {
11398
+ errorStack: error.stack
11399
+ }
11400
+ });
11401
+ }
11402
+ /**
11403
+ * Helper method: Send database error alert
11404
+ *
11405
+ * @param message - Error message
11406
+ * @param context - Additional context
11407
+ */
11408
+ async sendDatabaseError(message, context) {
11409
+ await this.sendAlert({
11410
+ severity: "CRITICAL" /* CRITICAL */,
11411
+ category: "DATABASE" /* DATABASE */,
11412
+ title: "\u{1F5C4}\uFE0F Database Error",
11413
+ description: message,
11414
+ fields: context
11415
+ });
11416
+ }
11417
+ /**
11418
+ * Helper method: Send security alert
11419
+ *
11420
+ * @param title - Alert title
11421
+ * @param description - Alert description
11422
+ * @param context - Additional context
11423
+ */
11424
+ async sendSecurityAlert(title, description, context) {
11425
+ await this.sendAlert({
11426
+ severity: "SECURITY" /* SECURITY */,
11427
+ category: "SECURITY" /* SECURITY */,
11428
+ title,
11429
+ description,
11430
+ fields: context
11431
+ });
11432
+ }
11433
+ /**
11434
+ * Helper method: Send payment error alert
11435
+ *
11436
+ * @param message - Error message
11437
+ * @param context - Payment context
11438
+ */
11439
+ async sendPaymentError(message, context) {
11440
+ await this.sendAlert({
11441
+ severity: "CRITICAL" /* CRITICAL */,
11442
+ category: "PAYMENT" /* PAYMENT */,
11443
+ title: "\u{1F4B3} Payment Processing Error",
11444
+ description: message,
11445
+ fields: context
11446
+ });
11447
+ }
11448
+ /**
11449
+ * Helper method: Send business event alert
11450
+ *
11451
+ * @param title - Event title
11452
+ * @param description - Event description
11453
+ * @param context - Event context
11454
+ */
11455
+ async sendBusinessEvent(title, description, context) {
11456
+ await this.sendAlert({
11457
+ severity: "INFO" /* INFO */,
11458
+ category: "BUSINESS_EVENT" /* BUSINESS_EVENT */,
11459
+ title,
11460
+ description,
11461
+ fields: context
11462
+ });
11463
+ }
11464
+ /**
11465
+ * Helper method: Send performance warning
11466
+ *
11467
+ * @param title - Warning title
11468
+ * @param description - Warning description
11469
+ * @param metrics - Performance metrics
11470
+ */
11471
+ async sendPerformanceWarning(title, description, metrics) {
11472
+ await this.sendAlert({
11473
+ severity: "WARNING" /* WARNING */,
11474
+ category: "PERFORMANCE" /* PERFORMANCE */,
11475
+ title,
11476
+ description,
11477
+ fields: metrics
11478
+ });
11479
+ }
11480
+ /**
11481
+ * Gets the appropriate webhook URL for an alert category
11482
+ *
11483
+ * @param category - Alert category
11484
+ * @returns Webhook URL or undefined if not configured
11485
+ */
11486
+ getWebhookUrl(category) {
11487
+ switch (category) {
11488
+ case "CRITICAL_ERROR" /* CRITICAL_ERROR */:
11489
+ case "DATABASE" /* DATABASE */:
11490
+ return this.config.errorWebhookUrl || void 0;
11491
+ case "SECURITY" /* SECURITY */:
11492
+ return this.config.securityWebhookUrl || this.config.errorWebhookUrl || void 0;
11493
+ case "BUSINESS_EVENT" /* BUSINESS_EVENT */:
11494
+ case "PAYMENT" /* PAYMENT */:
11495
+ return this.config.businessWebhookUrl || this.config.errorWebhookUrl || void 0;
11496
+ case "PERFORMANCE" /* PERFORMANCE */:
11497
+ return this.config.performanceWebhookUrl || this.config.errorWebhookUrl || void 0;
11498
+ default:
11499
+ return this.config.errorWebhookUrl || void 0;
11500
+ }
11501
+ }
11502
+ /**
11503
+ * Sends payload to Discord webhook
11504
+ *
11505
+ * @param webhookUrl - Discord webhook URL
11506
+ * @param payload - Webhook payload
11507
+ */
11508
+ async sendToWebhook(webhookUrl, payload) {
11509
+ const response = await this.httpPost(webhookUrl, payload);
11510
+ if (!response.ok) {
11511
+ throw new Error(
11512
+ `Discord webhook failed: ${response.status} ${response.statusText}`
11513
+ );
11514
+ }
11515
+ }
11516
+ /**
11517
+ * Makes HTTP POST request to webhook
11518
+ * Uses native fetch if available, falls back to simple implementation
11519
+ *
11520
+ * @param url - Webhook URL
11521
+ * @param data - Payload data
11522
+ * @returns HTTP response
11523
+ */
11524
+ async httpPost(url, data) {
11525
+ try {
11526
+ if (typeof fetch !== "undefined") {
11527
+ const response = await fetch(url, {
11528
+ method: "POST",
11529
+ headers: {
11530
+ "Content-Type": "application/json"
11531
+ },
11532
+ body: JSON.stringify(data)
11533
+ });
11534
+ return {
11535
+ ok: response.ok,
11536
+ status: response.status,
11537
+ statusText: response.statusText
11538
+ };
11539
+ }
11540
+ throw new Error("fetch not available - please use Node.js 18+ or provide HTTP client");
11541
+ } catch (error) {
11542
+ throw new Error(`HTTP request failed: ${error instanceof Error ? error.message : "Unknown error"}`);
11543
+ }
11544
+ }
11545
+ /**
11546
+ * Gets hostname for alert metadata
11547
+ */
11548
+ getHostname() {
11549
+ try {
11550
+ return process.env.HOSTNAME || process.env.HOST || "unknown";
11551
+ } catch {
11552
+ return "unknown";
11553
+ }
11554
+ }
11555
+ /**
11556
+ * Simple logging (doesn't use logger to avoid circular dependencies)
11557
+ */
11558
+ log(message) {
11559
+ if (this.config.environment === "development") {
11560
+ console.log(`[DiscordAlertService] ${message}`);
11561
+ }
11562
+ }
11563
+ /**
11564
+ * Error logging
11565
+ */
11566
+ logError(message, error) {
11567
+ console.error(
11568
+ `[DiscordAlertService] ${message}:`,
11569
+ error instanceof Error ? error.message : String(error)
11570
+ );
11571
+ }
11572
+ /**
11573
+ * Gets service statistics
11574
+ *
11575
+ * @returns Statistics object
11576
+ */
11577
+ getStats() {
11578
+ const total = this.alertsSent + this.alertsFailed;
11579
+ const successRate = total > 0 ? this.alertsSent / total * 100 : 0;
11580
+ return {
11581
+ alertsSent: this.alertsSent,
11582
+ alertsFailed: this.alertsFailed,
11583
+ successRate: Math.round(successRate * 100) / 100,
11584
+ rateLimiterStats: this.rateLimiter.getStats()
11585
+ };
11586
+ }
11587
+ /**
11588
+ * Resets statistics
11589
+ */
11590
+ resetStats() {
11591
+ this.alertsSent = 0;
11592
+ this.alertsFailed = 0;
11593
+ }
11594
+ /**
11595
+ * Checks if service is properly configured
11596
+ *
11597
+ * @returns true if at least one webhook URL is configured
11598
+ */
11599
+ isConfigured() {
11600
+ return !!(this.config.errorWebhookUrl || this.config.securityWebhookUrl || this.config.businessWebhookUrl || this.config.performanceWebhookUrl);
11601
+ }
11602
+ /**
11603
+ * Gets current configuration (with masked webhook URLs for security)
11604
+ */
11605
+ getConfig() {
11606
+ return {
11607
+ enabled: this.config.enabled,
11608
+ rateLimitPerMinute: this.config.rateLimitPerMinute,
11609
+ environment: this.config.environment,
11610
+ microserviceName: this.config.microserviceName,
11611
+ webhooksConfigured: {
11612
+ error: !!this.config.errorWebhookUrl,
11613
+ security: !!this.config.securityWebhookUrl,
11614
+ business: !!this.config.businessWebhookUrl,
11615
+ performance: !!this.config.performanceWebhookUrl
11616
+ }
11617
+ };
11618
+ }
11619
+ /**
11620
+ * Cleanup method - call when service is no longer needed
11621
+ */
11622
+ destroy() {
11623
+ this.rateLimiter.destroy();
11624
+ }
11625
+ };
11626
+
11627
+ // src/modules/discord/winston-discord.transport.ts
11628
+ import Transport from "winston-transport";
11629
+ var DiscordTransport = class extends Transport {
11630
+ /**
11631
+ * Creates a new Winston Discord transport
11632
+ *
11633
+ * @param options - Transport configuration
11634
+ */
11635
+ constructor(options) {
11636
+ super({
11637
+ level: options.level || "error"
11638
+ });
11639
+ this.logsSent = 0;
11640
+ this.transportOptions = {
11641
+ level: options.level || "error",
11642
+ microserviceName: options.microserviceName,
11643
+ environment: options.environment,
11644
+ enabled: options.enabled !== void 0 ? options.enabled : true,
11645
+ category: options.category || "CRITICAL_ERROR" /* CRITICAL_ERROR */,
11646
+ severity: options.severity || "CRITICAL" /* CRITICAL */
11647
+ };
11648
+ this.discordService = new DiscordAlertService({
11649
+ errorWebhookUrl: options.webhookUrl,
11650
+ microserviceName: this.transportOptions.microserviceName,
11651
+ environment: this.transportOptions.environment,
11652
+ enabled: this.transportOptions.enabled
11653
+ });
11654
+ }
11655
+ /**
11656
+ * Log method called by Winston for each log entry
11657
+ * Required by winston-transport.Transport
11658
+ *
11659
+ * @param info - Winston log info object
11660
+ * @param callback - Callback to call when done
11661
+ */
11662
+ log(info, callback) {
11663
+ setImmediate(() => {
11664
+ this.emit("logged", info);
11665
+ });
11666
+ if (!this.transportOptions.enabled || !this.shouldLog(info.level)) {
11667
+ callback();
11668
+ return;
11669
+ }
11670
+ this.sendLogToDiscord(info).catch((error) => {
11671
+ console.error(
11672
+ `[DiscordTransport] Failed to send log to Discord:`,
11673
+ error instanceof Error ? error.message : String(error)
11674
+ );
11675
+ }).finally(() => {
11676
+ callback();
11677
+ });
11678
+ }
11679
+ /**
11680
+ * Determines if a log level should be sent to Discord
11681
+ *
11682
+ * @param level - Log level from Winston
11683
+ * @returns true if should be sent to Discord
11684
+ */
11685
+ shouldLog(level) {
11686
+ const levels = {
11687
+ error: 0,
11688
+ warn: 1,
11689
+ info: 2,
11690
+ http: 3,
11691
+ verbose: 4,
11692
+ debug: 5,
11693
+ silly: 6
11694
+ };
11695
+ const targetLevel = levels[this.transportOptions.level] || 0;
11696
+ const currentLevel = levels[level] || 999;
11697
+ return currentLevel <= targetLevel;
11698
+ }
11699
+ /**
11700
+ * Sends log info to Discord as an alert
11701
+ *
11702
+ * @param info - Winston log info
11703
+ */
11704
+ async sendLogToDiscord(info) {
11705
+ try {
11706
+ const fields = {};
11707
+ if (info.context) {
11708
+ fields["Context"] = info.context;
11709
+ }
11710
+ const excludeFields = [
11711
+ "level",
11712
+ "message",
11713
+ "timestamp",
11714
+ "stack",
11715
+ "correlationId",
11716
+ "context",
11717
+ "error"
11718
+ ];
11719
+ Object.entries(info).forEach(([key, value]) => {
11720
+ if (!excludeFields.includes(key) && typeof value !== "function" && typeof value !== "object") {
11721
+ fields[key] = value;
11722
+ }
11723
+ });
11724
+ const severity = this.mapLogLevelToSeverity(info.level);
11725
+ await this.discordService.sendAlert({
11726
+ severity,
11727
+ category: this.transportOptions.category,
11728
+ title: this.buildTitle(info),
11729
+ description: info.message,
11730
+ fields: Object.keys(fields).length > 0 ? fields : void 0,
11731
+ metadata: {
11732
+ correlationId: info.correlationId,
11733
+ errorStack: info.stack || info.error
11734
+ }
11735
+ });
11736
+ this.logsSent++;
11737
+ } catch (error) {
11738
+ throw error;
11739
+ }
11740
+ }
11741
+ /**
11742
+ * Maps Winston log level to Discord alert severity
11743
+ *
11744
+ * @param level - Winston log level
11745
+ * @returns Alert severity
11746
+ */
11747
+ mapLogLevelToSeverity(level) {
11748
+ switch (level.toLowerCase()) {
11749
+ case "error":
11750
+ return "CRITICAL" /* CRITICAL */;
11751
+ case "warn":
11752
+ return "WARNING" /* WARNING */;
11753
+ case "info":
11754
+ return "INFO" /* INFO */;
11755
+ default:
11756
+ return "ERROR" /* ERROR */;
11757
+ }
11758
+ }
11759
+ /**
11760
+ * Builds alert title from log info
11761
+ *
11762
+ * @param info - Winston log info
11763
+ * @returns Alert title
11764
+ */
11765
+ buildTitle(info) {
11766
+ if (info.context) {
11767
+ return `${info.context} Error`;
11768
+ }
11769
+ return `Application ${info.level.toUpperCase()}`;
11770
+ }
11771
+ /**
11772
+ * Gets transport statistics
11773
+ */
11774
+ getStats() {
11775
+ return {
11776
+ logsSent: this.logsSent,
11777
+ discordStats: this.discordService.getStats()
11778
+ };
11779
+ }
11780
+ /**
11781
+ * Cleanup method
11782
+ */
11783
+ close() {
11784
+ this.discordService.destroy();
11785
+ }
11786
+ };
11787
+ function createDiscordTransport(options) {
11788
+ return new DiscordTransport(options);
11789
+ }
11790
+
10676
11791
  // src/adapters/tcp/user.tcp.adapter.ts
10677
11792
  import { config } from "dotenv";
10678
- import { Transport } from "@nestjs/microservices";
11793
+ import { Transport as Transport2 } from "@nestjs/microservices";
10679
11794
  config();
10680
11795
  var UserTCPAdapter = () => {
10681
11796
  return {
10682
11797
  name: "USER_MICROSERVICE",
10683
- transport: Transport.TCP,
11798
+ transport: Transport2.TCP,
10684
11799
  options: {
10685
11800
  host: process.env.USER_MICROSERVICE_TCP_HOST || "localhost",
10686
11801
  port: parseInt(process.env.USER_MICROSERVICE_TCP_PORT || "4001", 10)
@@ -10690,12 +11805,12 @@ var UserTCPAdapter = () => {
10690
11805
 
10691
11806
  // src/adapters/tcp/job.tcp.adapter.ts
10692
11807
  import { config as config2 } from "dotenv";
10693
- import { Transport as Transport2 } from "@nestjs/microservices";
11808
+ import { Transport as Transport3 } from "@nestjs/microservices";
10694
11809
  config2();
10695
11810
  var JobTCPAdapter = () => {
10696
11811
  return {
10697
11812
  name: "JOB_MICROSERVICE",
10698
- transport: Transport2.TCP,
11813
+ transport: Transport3.TCP,
10699
11814
  options: {
10700
11815
  host: process.env.JOB_MICROSERVICE_TCP_HOST || "localhost",
10701
11816
  port: parseInt(process.env.JOB_MICROSERVICE_TCP_PORT || "4002", 10)
@@ -10705,12 +11820,12 @@ var JobTCPAdapter = () => {
10705
11820
 
10706
11821
  // src/adapters/tcp/contract.tcp.adapter.ts
10707
11822
  import { config as config3 } from "dotenv";
10708
- import { Transport as Transport3 } from "@nestjs/microservices";
11823
+ import { Transport as Transport4 } from "@nestjs/microservices";
10709
11824
  config3();
10710
11825
  var ContractTCPAdapter = () => {
10711
11826
  return {
10712
11827
  name: "CONTRACT_MICROSERVICE",
10713
- transport: Transport3.TCP,
11828
+ transport: Transport4.TCP,
10714
11829
  options: {
10715
11830
  host: process.env.CONTRACT_MICROSERVICE_TCP_HOST || "localhost",
10716
11831
  port: parseInt(process.env.CONTRACT_MICROSERVICE_TCP_PORT || "4004", 10)
@@ -10720,12 +11835,12 @@ var ContractTCPAdapter = () => {
10720
11835
 
10721
11836
  // src/adapters/tcp/notification.tcp.adapter.ts
10722
11837
  import { config as config4 } from "dotenv";
10723
- import { Transport as Transport4 } from "@nestjs/microservices";
11838
+ import { Transport as Transport5 } from "@nestjs/microservices";
10724
11839
  config4();
10725
11840
  var NotificationTCPAdapter = () => {
10726
11841
  return {
10727
11842
  name: "NOTIFICATION_MICROSERVICE",
10728
- transport: Transport4.TCP,
11843
+ transport: Transport5.TCP,
10729
11844
  options: {
10730
11845
  host: process.env.NOTIFICATION_MICROSERVICE_TCP_HOST || "localhost",
10731
11846
  port: parseInt(process.env.NOTIFICATION_MICROSERVICE_TCP_PORT || "4003", 10)
@@ -10735,12 +11850,12 @@ var NotificationTCPAdapter = () => {
10735
11850
 
10736
11851
  // src/adapters/tcp/chat.tcp.adapter.ts
10737
11852
  import { config as config5 } from "dotenv";
10738
- import { Transport as Transport5 } from "@nestjs/microservices";
11853
+ import { Transport as Transport6 } from "@nestjs/microservices";
10739
11854
  config5();
10740
11855
  var ChatTCPAdapter = () => {
10741
11856
  return {
10742
11857
  name: "CHAT_MICROSERVICE",
10743
- transport: Transport5.TCP,
11858
+ transport: Transport6.TCP,
10744
11859
  options: {
10745
11860
  host: process.env.CHAT_MICROSERVICE_TCP_HOST || "localhost",
10746
11861
  port: parseInt(process.env.CHAT_MICROSERVICE_TCP_PORT || "4005", 10)
@@ -10750,7 +11865,7 @@ var ChatTCPAdapter = () => {
10750
11865
 
10751
11866
  // src/adapters/rmq/user.rmq.adapter.ts
10752
11867
  import { config as config6 } from "dotenv";
10753
- import { Transport as Transport6 } from "@nestjs/microservices";
11868
+ import { Transport as Transport7 } from "@nestjs/microservices";
10754
11869
  config6();
10755
11870
  var UserRMQAdapter = (mode = "microservice") => {
10756
11871
  const urls = process.env.USER_MICROSERVICE_RMQ_URL?.split(",") || [
@@ -10770,7 +11885,7 @@ var UserRMQAdapter = (mode = "microservice") => {
10770
11885
  );
10771
11886
  const config11 = {
10772
11887
  name: "USER_MICROSERVICE",
10773
- transport: Transport6.RMQ,
11888
+ transport: Transport7.RMQ,
10774
11889
  options: {
10775
11890
  urls,
10776
11891
  queue,
@@ -10807,7 +11922,7 @@ var UserRMQAdapter = (mode = "microservice") => {
10807
11922
 
10808
11923
  // src/adapters/rmq/job.rmq.adapter.ts
10809
11924
  import { config as config7 } from "dotenv";
10810
- import { Transport as Transport7 } from "@nestjs/microservices";
11925
+ import { Transport as Transport8 } from "@nestjs/microservices";
10811
11926
  config7();
10812
11927
  var JobRMQAdapter = (mode = "microservice") => {
10813
11928
  const urls = process.env.JOB_MICROSERVICE_RMQ_URL?.split(",") || [
@@ -10827,7 +11942,7 @@ var JobRMQAdapter = (mode = "microservice") => {
10827
11942
  );
10828
11943
  const config11 = {
10829
11944
  name: "JOB_MICROSERVICE",
10830
- transport: Transport7.RMQ,
11945
+ transport: Transport8.RMQ,
10831
11946
  options: {
10832
11947
  urls,
10833
11948
  queue,
@@ -10864,7 +11979,7 @@ var JobRMQAdapter = (mode = "microservice") => {
10864
11979
 
10865
11980
  // src/adapters/rmq/contract.rmq.adapter.ts
10866
11981
  import { config as config8 } from "dotenv";
10867
- import { Transport as Transport8 } from "@nestjs/microservices";
11982
+ import { Transport as Transport9 } from "@nestjs/microservices";
10868
11983
  config8();
10869
11984
  var ContractRMQAdapter = (mode = "microservice") => {
10870
11985
  const urls = process.env.CONTRACT_MICROSERVICE_RMQ_URL?.split(",") || [
@@ -10884,7 +11999,7 @@ var ContractRMQAdapter = (mode = "microservice") => {
10884
11999
  );
10885
12000
  const config11 = {
10886
12001
  name: "CONTRACT_MICROSERVICE",
10887
- transport: Transport8.RMQ,
12002
+ transport: Transport9.RMQ,
10888
12003
  options: {
10889
12004
  urls,
10890
12005
  queue,
@@ -10921,7 +12036,7 @@ var ContractRMQAdapter = (mode = "microservice") => {
10921
12036
 
10922
12037
  // src/adapters/rmq/notification.rmq.adapter.ts
10923
12038
  import { config as config9 } from "dotenv";
10924
- import { Transport as Transport9 } from "@nestjs/microservices";
12039
+ import { Transport as Transport10 } from "@nestjs/microservices";
10925
12040
  config9();
10926
12041
  var NotificationRMQAdapter = (mode = "microservice") => {
10927
12042
  const urls = process.env.NOTIFICATION_MICROSERVICE_RMQ_URL?.split(",") || [
@@ -10941,7 +12056,7 @@ var NotificationRMQAdapter = (mode = "microservice") => {
10941
12056
  );
10942
12057
  const config11 = {
10943
12058
  name: "NOTIFICATION_MICROSERVICE",
10944
- transport: Transport9.RMQ,
12059
+ transport: Transport10.RMQ,
10945
12060
  options: {
10946
12061
  urls,
10947
12062
  queue,
@@ -10978,7 +12093,7 @@ var NotificationRMQAdapter = (mode = "microservice") => {
10978
12093
 
10979
12094
  // src/adapters/rmq/chat.rmq.adapter.ts
10980
12095
  import { config as config10 } from "dotenv";
10981
- import { Transport as Transport10 } from "@nestjs/microservices";
12096
+ import { Transport as Transport11 } from "@nestjs/microservices";
10982
12097
  config10();
10983
12098
  var ChatRMQAdapter = (mode = "microservice") => {
10984
12099
  const urls = process.env.CHAT_MICROSERVICE_RMQ_URL?.split(",") || [
@@ -10998,7 +12113,7 @@ var ChatRMQAdapter = (mode = "microservice") => {
10998
12113
  );
10999
12114
  const config11 = {
11000
12115
  name: "CHAT_MICROSERVICE",
11001
- transport: Transport10.RMQ,
12116
+ transport: Transport11.RMQ,
11002
12117
  options: {
11003
12118
  urls,
11004
12119
  queue,
@@ -11709,6 +12824,9 @@ export {
11709
12824
  AiInterviewStatusEnum,
11710
12825
  AiInterviewTemplateGenerationDto,
11711
12826
  AiQuestionItemDto,
12827
+ AlertBuilder,
12828
+ AlertCategory,
12829
+ AlertSeverity,
11712
12830
  AnswerTypeEnum,
11713
12831
  ApplicationStatusEnum,
11714
12832
  ApproveTimesheetsDto,
@@ -11807,6 +12925,8 @@ export {
11807
12925
  CustomQuestionItemDto,
11808
12926
  DISPUTE_PATTERN,
11809
12927
  DebitCommissionFteHiringDto,
12928
+ DiscordAlertService,
12929
+ DiscordTransport,
11810
12930
  Dispute,
11811
12931
  DisputeStatusEnum,
11812
12932
  DocumentType,
@@ -11963,6 +13083,7 @@ export {
11963
13083
  QuestionForEnum,
11964
13084
  RATING_PATTERN,
11965
13085
  RESUME_PARSER_PATTERN,
13086
+ RateLimiter,
11966
13087
  Rating,
11967
13088
  RatingTypeEnum,
11968
13089
  RecommendationWeightageConfig,
@@ -12082,6 +13203,7 @@ export {
12082
13203
  WalletTransactionTypeEnum,
12083
13204
  ZOOM_PATTERN,
12084
13205
  ZoomMeetingLog,
13206
+ createDiscordTransport,
12085
13207
  typeOfExperienceDtoEnumV2,
12086
13208
  typeOfExperienceEnum
12087
13209
  };