@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/entities/contract-summary.entity.d.ts +2 -0
- package/dist/entities/hiring.entity.d.ts +3 -0
- package/dist/index.d.mts +240 -28
- package/dist/index.d.ts +240 -28
- package/dist/index.js +1328 -189
- package/dist/index.mjs +1343 -221
- package/dist/modules/discord/alert-builder.d.ts +16 -0
- package/dist/modules/discord/discord-alert.interface.d.ts +84 -0
- package/dist/modules/discord/discord-alert.service.d.ts +39 -0
- package/dist/modules/discord/index.d.ts +5 -0
- package/dist/modules/discord/rate-limiter.d.ts +32 -0
- package/dist/modules/discord/winston-discord.transport.d.ts +40 -0
- package/dist/modules/index.d.ts +1 -0
- package/dist/modules/interview/dto/reject-f2f-interview-reschedule-request.dto.d.ts +1 -1
- package/dist/modules/user/client-profile/dto/client-service-agreement-upload.dto.d.ts +1 -1
- package/package.json +3 -2
package/dist/index.mjs
CHANGED
|
@@ -1127,7 +1127,7 @@ var ClientServiceAgreementUploadDto = class {
|
|
|
1127
1127
|
__decorateClass([
|
|
1128
1128
|
IsOptional15(),
|
|
1129
1129
|
IsString16()
|
|
1130
|
-
], ClientServiceAgreementUploadDto.prototype, "
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
4409
|
-
|
|
4511
|
+
Column27({ name: "job_id", type: "integer", nullable: true }),
|
|
4512
|
+
Index19()
|
|
4410
4513
|
], Hiring.prototype, "jobId", 2);
|
|
4411
4514
|
__decorateClass([
|
|
4412
|
-
|
|
4413
|
-
|
|
4515
|
+
ManyToOne25(() => Job, (job) => job.hirings),
|
|
4516
|
+
JoinColumn25({ name: "job_id" })
|
|
4414
4517
|
], Hiring.prototype, "job", 2);
|
|
4415
4518
|
__decorateClass([
|
|
4416
|
-
|
|
4417
|
-
|
|
4519
|
+
Column27({ name: "client_id", type: "integer", nullable: true }),
|
|
4520
|
+
Index19()
|
|
4418
4521
|
], Hiring.prototype, "clientId", 2);
|
|
4419
4522
|
__decorateClass([
|
|
4420
|
-
|
|
4421
|
-
|
|
4523
|
+
ManyToOne25(() => User, (user) => user.clientHirings),
|
|
4524
|
+
JoinColumn25({ name: "client_id" })
|
|
4422
4525
|
], Hiring.prototype, "client", 2);
|
|
4423
4526
|
__decorateClass([
|
|
4424
|
-
|
|
4425
|
-
|
|
4527
|
+
Column27({ name: "freelancer_id", type: "integer", nullable: true }),
|
|
4528
|
+
Index19()
|
|
4426
4529
|
], Hiring.prototype, "freelancerId", 2);
|
|
4427
4530
|
__decorateClass([
|
|
4428
|
-
|
|
4429
|
-
|
|
4531
|
+
ManyToOne25(() => User, (user) => user.freelancerHirings),
|
|
4532
|
+
JoinColumn25({ name: "freelancer_id" })
|
|
4430
4533
|
], Hiring.prototype, "freelancer", 2);
|
|
4431
4534
|
__decorateClass([
|
|
4432
|
-
|
|
4433
|
-
|
|
4535
|
+
Column27({ name: "invoice_id", type: "integer", nullable: true }),
|
|
4536
|
+
Index19()
|
|
4434
4537
|
], Hiring.prototype, "invoiceId", 2);
|
|
4435
4538
|
__decorateClass([
|
|
4436
|
-
|
|
4437
|
-
|
|
4539
|
+
OneToOne6(() => Invoice, (invoice) => invoice.hiring),
|
|
4540
|
+
JoinColumn25({ name: "invoice_id" })
|
|
4438
4541
|
], Hiring.prototype, "invoice", 2);
|
|
4439
4542
|
__decorateClass([
|
|
4440
|
-
|
|
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
|
-
|
|
4562
|
+
Column27({ name: "is_invoice_genrated", type: "boolean", default: false })
|
|
4449
4563
|
], Hiring.prototype, "isInvoiceGenrated", 2);
|
|
4450
4564
|
Hiring = __decorateClass([
|
|
4451
|
-
|
|
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
|
-
|
|
4480
|
-
|
|
4593
|
+
Column28({ name: "timesheet_line_id", type: "integer", nullable: true }),
|
|
4594
|
+
Index20()
|
|
4481
4595
|
], Invoice.prototype, "timesheetLineId", 2);
|
|
4482
4596
|
__decorateClass([
|
|
4483
|
-
|
|
4484
|
-
|
|
4597
|
+
ManyToOne26(() => TimesheetLine, (timesheetLine) => timesheetLine.invoice),
|
|
4598
|
+
JoinColumn26({ name: "timesheet_line_id" })
|
|
4485
4599
|
], Invoice.prototype, "timesheetLine", 2);
|
|
4486
4600
|
__decorateClass([
|
|
4487
|
-
|
|
4488
|
-
|
|
4601
|
+
Column28({ name: "job_id", type: "integer", nullable: true }),
|
|
4602
|
+
Index20()
|
|
4489
4603
|
], Invoice.prototype, "jobId", 2);
|
|
4490
4604
|
__decorateClass([
|
|
4491
|
-
|
|
4492
|
-
|
|
4605
|
+
ManyToOne26(() => Job, (job) => job.invoice),
|
|
4606
|
+
JoinColumn26({ name: "job_id" })
|
|
4493
4607
|
], Invoice.prototype, "job", 2);
|
|
4494
4608
|
__decorateClass([
|
|
4495
|
-
|
|
4496
|
-
|
|
4609
|
+
Column28({ name: "client_id", type: "integer", nullable: true }),
|
|
4610
|
+
Index20()
|
|
4497
4611
|
], Invoice.prototype, "clientId", 2);
|
|
4498
4612
|
__decorateClass([
|
|
4499
|
-
|
|
4500
|
-
|
|
4613
|
+
ManyToOne26(() => User, (user) => user.clientInvoice),
|
|
4614
|
+
JoinColumn26({ name: "client_id" })
|
|
4501
4615
|
], Invoice.prototype, "client", 2);
|
|
4502
4616
|
__decorateClass([
|
|
4503
|
-
|
|
4504
|
-
|
|
4617
|
+
Column28({ name: "freelancer_id", type: "integer", nullable: true }),
|
|
4618
|
+
Index20()
|
|
4505
4619
|
], Invoice.prototype, "freelancerId", 2);
|
|
4506
4620
|
__decorateClass([
|
|
4507
|
-
|
|
4508
|
-
|
|
4621
|
+
ManyToOne26(() => User, (user) => user.freelancerInvoice),
|
|
4622
|
+
JoinColumn26({ name: "freelancer_id" })
|
|
4509
4623
|
], Invoice.prototype, "freelancer", 2);
|
|
4510
4624
|
__decorateClass([
|
|
4511
|
-
|
|
4625
|
+
Column28({ name: "invoice_unique_id", type: "varchar", nullable: true })
|
|
4512
4626
|
], Invoice.prototype, "invoiceUniqueId", 2);
|
|
4513
4627
|
__decorateClass([
|
|
4514
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
4656
|
+
Column28({ name: "billing_hours", type: "varchar", nullable: true })
|
|
4543
4657
|
], Invoice.prototype, "billingHours", 2);
|
|
4544
4658
|
__decorateClass([
|
|
4545
|
-
|
|
4659
|
+
Column28({ name: "hourly_rate", type: "varchar", nullable: true })
|
|
4546
4660
|
], Invoice.prototype, "hourlyRate", 2);
|
|
4547
4661
|
__decorateClass([
|
|
4548
|
-
|
|
4662
|
+
Column28({ name: "billing_amount", type: "varchar", nullable: true })
|
|
4549
4663
|
], Invoice.prototype, "billingAmount", 2);
|
|
4550
4664
|
__decorateClass([
|
|
4551
|
-
|
|
4665
|
+
Column28({ name: "invoice_type", type: "enum", enum: InvoiceTypeEnum, nullable: true })
|
|
4552
4666
|
], Invoice.prototype, "invoiceType", 2);
|
|
4553
4667
|
__decorateClass([
|
|
4554
|
-
|
|
4668
|
+
Column28({ name: "status", type: "enum", enum: InvoiceStatusEnum, nullable: true })
|
|
4555
4669
|
], Invoice.prototype, "status", 2);
|
|
4556
4670
|
__decorateClass([
|
|
4557
|
-
|
|
4671
|
+
Column28({ name: "payment_status", type: "enum", enum: InvoicePaymentStatusEnum, nullable: true })
|
|
4558
4672
|
], Invoice.prototype, "paymentStatus", 2);
|
|
4559
4673
|
__decorateClass([
|
|
4560
|
-
|
|
4674
|
+
Column28({ name: "client_invoice_url", type: "varchar", nullable: true })
|
|
4561
4675
|
], Invoice.prototype, "clientInvoiceUrl", 2);
|
|
4562
4676
|
__decorateClass([
|
|
4563
|
-
|
|
4677
|
+
Column28({ name: "freelancer_invoice_url", type: "varchar", nullable: true })
|
|
4564
4678
|
], Invoice.prototype, "freelancerInvoiceUrl", 2);
|
|
4565
4679
|
__decorateClass([
|
|
4566
|
-
|
|
4680
|
+
OneToOne7(() => EscrowWalletTransaction, (escrowWalletTransaction) => escrowWalletTransaction.invoice)
|
|
4567
4681
|
], Invoice.prototype, "escrowWalletTransaction", 2);
|
|
4568
4682
|
__decorateClass([
|
|
4569
|
-
|
|
4683
|
+
OneToOne7(() => Hiring, (hiring) => hiring.invoice)
|
|
4570
4684
|
], Invoice.prototype, "hiring", 2);
|
|
4571
4685
|
Invoice = __decorateClass([
|
|
4572
|
-
|
|
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
|
-
|
|
4590
|
-
|
|
4703
|
+
Column29({ name: "escrow_wallet_id", type: "integer", nullable: true }),
|
|
4704
|
+
Index21()
|
|
4591
4705
|
], EscrowWalletTransaction.prototype, "escrowWalletId", 2);
|
|
4592
4706
|
__decorateClass([
|
|
4593
|
-
|
|
4594
|
-
|
|
4707
|
+
ManyToOne27(() => EscrowWallet, (escrowWallet) => escrowWallet.escrowWalletTransactions),
|
|
4708
|
+
JoinColumn27({ name: "wallet_id" })
|
|
4595
4709
|
], EscrowWalletTransaction.prototype, "escrowWallet", 2);
|
|
4596
4710
|
__decorateClass([
|
|
4597
|
-
|
|
4598
|
-
|
|
4711
|
+
Column29({ name: "invoice_id", type: "integer", nullable: true }),
|
|
4712
|
+
Index21()
|
|
4599
4713
|
], EscrowWalletTransaction.prototype, "invoiceId", 2);
|
|
4600
4714
|
__decorateClass([
|
|
4601
|
-
|
|
4602
|
-
|
|
4715
|
+
OneToOne8(() => Invoice, (invoice) => invoice.escrowWalletTransaction),
|
|
4716
|
+
JoinColumn27({ name: "invoice_id" })
|
|
4603
4717
|
], EscrowWalletTransaction.prototype, "invoice", 2);
|
|
4604
4718
|
__decorateClass([
|
|
4605
|
-
|
|
4719
|
+
Column29({ name: "amount", type: "bigint", nullable: true })
|
|
4606
4720
|
], EscrowWalletTransaction.prototype, "amount", 2);
|
|
4607
4721
|
__decorateClass([
|
|
4608
|
-
|
|
4722
|
+
Column29({ name: "escrow_type", type: "enum", enum: EscrowWalletTransactionTypeEnum })
|
|
4609
4723
|
], EscrowWalletTransaction.prototype, "escrowType", 2);
|
|
4610
4724
|
__decorateClass([
|
|
4611
|
-
|
|
4725
|
+
Column29({ name: "description", type: "text", nullable: true })
|
|
4612
4726
|
], EscrowWalletTransaction.prototype, "description", 2);
|
|
4613
4727
|
__decorateClass([
|
|
4614
|
-
|
|
4728
|
+
Column29({ name: "completed_at", type: "timestamptz", nullable: true })
|
|
4615
4729
|
], EscrowWalletTransaction.prototype, "completedAt", 2);
|
|
4616
4730
|
__decorateClass([
|
|
4617
|
-
|
|
4731
|
+
Column29({ name: "escrow_transaction_for", type: "enum", enum: EscrowWalletTransactionForEnum })
|
|
4618
4732
|
], EscrowWalletTransaction.prototype, "escrowTransactionFor", 2);
|
|
4619
4733
|
__decorateClass([
|
|
4620
|
-
|
|
4734
|
+
Column29({ name: "meta_data", type: "varchar", nullable: true })
|
|
4621
4735
|
], EscrowWalletTransaction.prototype, "metaData", 2);
|
|
4622
4736
|
EscrowWalletTransaction = __decorateClass([
|
|
4623
|
-
|
|
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
|
-
|
|
4631
|
-
|
|
4744
|
+
Column30({ name: "job_id", type: "integer", nullable: true }),
|
|
4745
|
+
Index22()
|
|
4632
4746
|
], EscrowWallet.prototype, "jobId", 2);
|
|
4633
4747
|
__decorateClass([
|
|
4634
|
-
|
|
4635
|
-
|
|
4748
|
+
ManyToOne28(() => Job, (job) => job.escrowWallets),
|
|
4749
|
+
JoinColumn28({ name: "job_id" })
|
|
4636
4750
|
], EscrowWallet.prototype, "job", 2);
|
|
4637
4751
|
__decorateClass([
|
|
4638
|
-
|
|
4639
|
-
|
|
4752
|
+
Column30({ name: "client_id", type: "integer", nullable: true }),
|
|
4753
|
+
Index22()
|
|
4640
4754
|
], EscrowWallet.prototype, "clientId", 2);
|
|
4641
4755
|
__decorateClass([
|
|
4642
|
-
|
|
4643
|
-
|
|
4756
|
+
ManyToOne28(() => User, (user) => user.clientEscrowWallets),
|
|
4757
|
+
JoinColumn28({ name: "client_id" })
|
|
4644
4758
|
], EscrowWallet.prototype, "client", 2);
|
|
4645
4759
|
__decorateClass([
|
|
4646
|
-
|
|
4647
|
-
|
|
4760
|
+
Column30({ name: "freelancer_id", type: "integer", nullable: true }),
|
|
4761
|
+
Index22()
|
|
4648
4762
|
], EscrowWallet.prototype, "freelancerId", 2);
|
|
4649
4763
|
__decorateClass([
|
|
4650
|
-
|
|
4651
|
-
|
|
4764
|
+
ManyToOne28(() => User, (user) => user.freelancerEscrowWallets),
|
|
4765
|
+
JoinColumn28({ name: "freelancer_id" })
|
|
4652
4766
|
], EscrowWallet.prototype, "freelancer", 2);
|
|
4653
4767
|
__decorateClass([
|
|
4654
|
-
|
|
4655
|
-
|
|
4768
|
+
Column30({ name: "contract_id", type: "integer", nullable: true }),
|
|
4769
|
+
Index22()
|
|
4656
4770
|
], EscrowWallet.prototype, "contractId", 2);
|
|
4657
4771
|
__decorateClass([
|
|
4658
|
-
|
|
4659
|
-
|
|
4772
|
+
OneToOne9(() => Contract, (contract) => contract.escrowWallet),
|
|
4773
|
+
JoinColumn28({ name: "contract_id" })
|
|
4660
4774
|
], EscrowWallet.prototype, "contract", 2);
|
|
4661
4775
|
__decorateClass([
|
|
4662
|
-
|
|
4776
|
+
Column30({ name: "wallet_balance", type: "varchar", default: "0" })
|
|
4663
4777
|
], EscrowWallet.prototype, "escrowBalance", 2);
|
|
4664
4778
|
__decorateClass([
|
|
4665
|
-
|
|
4779
|
+
Column30({ name: "metadata", type: "jsonb", nullable: true })
|
|
4666
4780
|
], EscrowWallet.prototype, "metadata", 2);
|
|
4667
4781
|
__decorateClass([
|
|
4668
|
-
|
|
4782
|
+
OneToMany12(() => EscrowWalletTransaction, (escrowWalletTransaction) => escrowWalletTransaction.escrowWallet)
|
|
4669
4783
|
], EscrowWallet.prototype, "escrowWalletTransactions", 2);
|
|
4670
4784
|
EscrowWallet = __decorateClass([
|
|
4671
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
6955
|
+
OneToOne13(
|
|
6940
6956
|
() => FreelancerProfile,
|
|
6941
6957
|
(freelancerProfile) => freelancerProfile.user
|
|
6942
6958
|
)
|
|
6943
6959
|
], User.prototype, "freelancerProfile", 2);
|
|
6944
6960
|
__decorateClass([
|
|
6945
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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, "
|
|
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:
|
|
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
|
|
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:
|
|
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
|
|
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:
|
|
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
|
|
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:
|
|
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
|
|
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:
|
|
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
|
|
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:
|
|
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
|
|
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:
|
|
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
|
|
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:
|
|
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
|
|
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:
|
|
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
|
|
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:
|
|
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
|
};
|