fix: the bug in the payment service
This commit is contained in:
@@ -2,6 +2,7 @@ import { EntityManager } from "@mikro-orm/postgresql";
|
|||||||
import { Seeder } from "@mikro-orm/seeder";
|
import { Seeder } from "@mikro-orm/seeder";
|
||||||
import { Logger } from "@nestjs/common";
|
import { Logger } from "@nestjs/common";
|
||||||
|
|
||||||
|
import { PaymentGatewaySeeder } from "./payment-gateway.seeder";
|
||||||
import { RoleSeeder } from "./role.seeder";
|
import { RoleSeeder } from "./role.seeder";
|
||||||
|
|
||||||
export class IndexSeeder extends Seeder {
|
export class IndexSeeder extends Seeder {
|
||||||
@@ -10,6 +11,7 @@ export class IndexSeeder extends Seeder {
|
|||||||
async run(em: EntityManager): Promise<void> {
|
async run(em: EntityManager): Promise<void> {
|
||||||
this.logger.log("Starting seeding process...");
|
this.logger.log("Starting seeding process...");
|
||||||
await new RoleSeeder().run(em);
|
await new RoleSeeder().run(em);
|
||||||
|
await new PaymentGatewaySeeder().run(em);
|
||||||
this.logger.log("Seeding process completed successfully.");
|
this.logger.log("Seeding process completed successfully.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,34 @@
|
|||||||
|
import { EntityManager, RequiredEntityData } from "@mikro-orm/postgresql";
|
||||||
|
import { Seeder } from "@mikro-orm/seeder";
|
||||||
|
import { Logger } from "@nestjs/common";
|
||||||
|
|
||||||
|
import { PaymentGateway } from "../../src/modules/payments/entities/payment-gateway.entity";
|
||||||
|
import { GatewayEnum } from "../../src/modules/payments/enums/gateway.enum";
|
||||||
|
|
||||||
|
export class PaymentGatewaySeeder extends Seeder {
|
||||||
|
private readonly logger = new Logger(PaymentGatewaySeeder.name);
|
||||||
|
|
||||||
|
async run(em: EntityManager): Promise<void> {
|
||||||
|
const paymentGateways: RequiredEntityData<PaymentGateway>[] = [
|
||||||
|
{
|
||||||
|
name: GatewayEnum.ZARINPAL,
|
||||||
|
nameFa: "زرین پال",
|
||||||
|
logoUrl: "https://storage.danakcorp.com/logo/fa-dark.png",
|
||||||
|
description: "درگاه پرداخت زرین پال",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
for (const paymentGateway of paymentGateways) {
|
||||||
|
const existingPaymentGateway = await em.findOne(PaymentGateway, { name: paymentGateway.name });
|
||||||
|
|
||||||
|
if (!existingPaymentGateway) {
|
||||||
|
const createdPaymentGateway = em.create(PaymentGateway, paymentGateway);
|
||||||
|
|
||||||
|
await em.persistAndFlush(createdPaymentGateway);
|
||||||
|
this.logger.log(`[PaymentGatewaySeeder] Created payment gateway: ${createdPaymentGateway.name}`);
|
||||||
|
} else {
|
||||||
|
this.logger.log(`[PaymentGatewaySeeder] Payment gateway already exists: ${paymentGateway.name}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -9,9 +9,7 @@ export enum DomainVerificationMethod {
|
|||||||
export class UpdateBusinessDomainDto {
|
export class UpdateBusinessDomainDto {
|
||||||
@IsNotEmpty()
|
@IsNotEmpty()
|
||||||
@IsString()
|
@IsString()
|
||||||
@Matches(/^([a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,}$/, {
|
@Matches(/^([a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,}$/, { message: "Domain must be a valid domain name (e.g., example.com)" })
|
||||||
message: "Domain must be a valid domain name (e.g., example.com)",
|
|
||||||
})
|
|
||||||
domain: string;
|
domain: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,12 @@
|
|||||||
import { Body, Controller, Get, Param, Post } from "@nestjs/common";
|
import { Body, Controller, Get, Param, Post } from "@nestjs/common";
|
||||||
|
import { ApiOperation } from "@nestjs/swagger";
|
||||||
|
|
||||||
import { UpdateBusinessDomainDto, VerifyDomainDto } from "./DTO/business-domain.dto";
|
import { UpdateBusinessDomainDto, VerifyDomainDto } from "./DTO/business-domain.dto";
|
||||||
import { BusinessSlugParamDto } from "./DTO/business-slug-param.dto";
|
import { BusinessSlugParamDto } from "./DTO/business-slug-param.dto";
|
||||||
import { Business } from "./entities/business.entity";
|
import { Business } from "./entities/business.entity";
|
||||||
import { BusinessesService } from "./services/businesses.service";
|
import { BusinessesService } from "./services/businesses.service";
|
||||||
import { DomainVerificationService } from "./services/domain-verification.service";
|
import { DomainVerificationService } from "./services/domain-verification.service";
|
||||||
|
import { AuthGuards } from "../../common/decorators/auth-guard.decorator";
|
||||||
import { BusinessDec } from "../../common/decorators/business.decorator";
|
import { BusinessDec } from "../../common/decorators/business.decorator";
|
||||||
|
|
||||||
@Controller("business")
|
@Controller("business")
|
||||||
@@ -15,37 +17,74 @@ export class BusinessesController {
|
|||||||
) {}
|
) {}
|
||||||
|
|
||||||
@Get("slug/:slug")
|
@Get("slug/:slug")
|
||||||
|
@ApiOperation({ summary: "Get business by slug" })
|
||||||
async getBusinessBySlug(@Param() params: BusinessSlugParamDto) {
|
async getBusinessBySlug(@Param() params: BusinessSlugParamDto) {
|
||||||
return this.businessesService.getBusinessBySlug(params.slug);
|
return this.businessesService.getBusinessBySlug(params.slug);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Post()
|
// Domain management endpoints
|
||||||
|
@Post("domain")
|
||||||
|
@ApiOperation({ summary: "Update business domain" })
|
||||||
|
@AuthGuards()
|
||||||
async updateBusinessDomain(@BusinessDec() business: Business, @Body() domainDto: UpdateBusinessDomainDto) {
|
async updateBusinessDomain(@BusinessDec() business: Business, @Body() domainDto: UpdateBusinessDomainDto) {
|
||||||
return this.domainVerificationService.updateBusinessDomain(business.id, domainDto);
|
return this.domainVerificationService.updateBusinessDomain(business.id, domainDto);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Get("status")
|
@Get("domain/status")
|
||||||
|
@ApiOperation({ summary: "Get business domain status" })
|
||||||
|
@AuthGuards()
|
||||||
async getDomainStatus(@BusinessDec() business: Business) {
|
async getDomainStatus(@BusinessDec() business: Business) {
|
||||||
return this.domainVerificationService.getDomainVerificationStatus(business.id);
|
return this.domainVerificationService.getDomainVerificationStatus(business.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Post("verify")
|
@Post("domain/verify")
|
||||||
|
@ApiOperation({ summary: "Initiate domain verification" })
|
||||||
|
@AuthGuards()
|
||||||
async verifyDomain(@BusinessDec() business: Business, @Body() verifyDto: VerifyDomainDto) {
|
async verifyDomain(@BusinessDec() business: Business, @Body() verifyDto: VerifyDomainDto) {
|
||||||
return this.domainVerificationService.initiateVerification(business.id, verifyDto);
|
return this.domainVerificationService.initiateVerification(business.id, verifyDto);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Post("verify/check")
|
@Post("domain/verify/check")
|
||||||
|
@ApiOperation({ summary: "Check domain verification status" })
|
||||||
|
@AuthGuards()
|
||||||
async checkVerification(@BusinessDec() business: Business) {
|
async checkVerification(@BusinessDec() business: Business) {
|
||||||
return this.domainVerificationService.verifyDomain(business.id);
|
return this.domainVerificationService.verifyDomain(business.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Post("admin/:businessId/verify")
|
// DNS-specific domain verification endpoints
|
||||||
|
@Post("domain/set-domain")
|
||||||
|
@ApiOperation({ summary: "Set domain and get verification instructions" })
|
||||||
|
@AuthGuards()
|
||||||
|
async setDomainAndGetInstructions(@BusinessDec() business: Business, @Body() domainDto: UpdateBusinessDomainDto) {
|
||||||
|
return this.domainVerificationService.setDomainAndGetVerificationInstructions(business.id, domainDto.domain);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Get("domain/check-dns")
|
||||||
|
@ApiOperation({ summary: "Check DNS verification" })
|
||||||
|
@AuthGuards()
|
||||||
|
async checkDnsVerification(@BusinessDec() business: Business) {
|
||||||
|
return this.domainVerificationService.verifyDnsTxtRecord(business.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Admin domain management endpoints
|
||||||
|
@Post("admin/:businessId/domain/verify")
|
||||||
|
@ApiOperation({ summary: "Admin initiate domain verification" })
|
||||||
|
@AuthGuards()
|
||||||
async adminVerifyDomain(@Param("businessId") businessId: string, @Body() verifyDto: VerifyDomainDto) {
|
async adminVerifyDomain(@Param("businessId") businessId: string, @Body() verifyDto: VerifyDomainDto) {
|
||||||
return this.domainVerificationService.initiateVerification(businessId, verifyDto);
|
return this.domainVerificationService.initiateVerification(businessId, verifyDto);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Post("admin/:businessId/verify/check")
|
@Post("admin/:businessId/domain/verify/check")
|
||||||
|
@ApiOperation({ summary: "Admin check domain verification status" })
|
||||||
|
@AuthGuards()
|
||||||
async adminCheckVerification(@Param("businessId") businessId: string) {
|
async adminCheckVerification(@Param("businessId") businessId: string) {
|
||||||
return this.domainVerificationService.verifyDomain(businessId);
|
return this.domainVerificationService.verifyDomain(businessId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Post("admin/:businessId/domain/check-dns")
|
||||||
|
@ApiOperation({ summary: "Admin check DNS verification" })
|
||||||
|
@AuthGuards()
|
||||||
|
async adminCheckDnsVerification(@Param("businessId") businessId: string) {
|
||||||
|
return this.domainVerificationService.verifyDnsTxtRecord(businessId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,8 +27,4 @@ export class BusinessesService {
|
|||||||
|
|
||||||
return business;
|
return business;
|
||||||
}
|
}
|
||||||
|
|
||||||
async flushChanges() {
|
|
||||||
await this.businessRepository.getEntityManager().flush();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
import * as crypto from "node:crypto";
|
import crypto from "node:crypto";
|
||||||
import * as dns from "node:dns";
|
import dns from "node:dns";
|
||||||
import { promisify } from "node:util";
|
import { promisify } from "node:util";
|
||||||
|
|
||||||
|
import { EntityManager } from "@mikro-orm/postgresql";
|
||||||
import { BadRequestException, Injectable, Logger } from "@nestjs/common";
|
import { BadRequestException, Injectable, Logger } from "@nestjs/common";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
|
import dayjs from "dayjs";
|
||||||
|
|
||||||
import { BusinessesService } from "./businesses.service";
|
import { BusinessesService } from "./businesses.service";
|
||||||
import { BusinessMessage } from "../../../common/enums/message.enum";
|
import { BusinessMessage } from "../../../common/enums/message.enum";
|
||||||
@@ -13,8 +15,17 @@ import { DomainVerificationMethod, UpdateBusinessDomainDto, VerifyDomainDto } fr
|
|||||||
export class DomainVerificationService {
|
export class DomainVerificationService {
|
||||||
private readonly logger = new Logger(DomainVerificationService.name);
|
private readonly logger = new Logger(DomainVerificationService.name);
|
||||||
private readonly resolveTxt = promisify(dns.resolveTxt);
|
private readonly resolveTxt = promisify(dns.resolveTxt);
|
||||||
|
private readonly resolveA = promisify(dns.resolve4);
|
||||||
|
private readonly resolveCname = promisify(dns.resolveCname);
|
||||||
|
|
||||||
constructor(private readonly businessesService: BusinessesService) {}
|
// The server IP or domain users should point their domain to
|
||||||
|
private readonly serverIP = "IP_OF_YOUR_SERVER"; // Replace with actual IP
|
||||||
|
private readonly serverDomain = "app.dzone.danakcorp.com"; // Replace with actual domain
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private readonly businessesService: BusinessesService,
|
||||||
|
private readonly em: EntityManager,
|
||||||
|
) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update business domain
|
* Update business domain
|
||||||
@@ -41,13 +52,13 @@ export class DomainVerificationService {
|
|||||||
business.domainVerifiedAt = undefined;
|
business.domainVerifiedAt = undefined;
|
||||||
business.domainVerificationMethod = undefined;
|
business.domainVerificationMethod = undefined;
|
||||||
|
|
||||||
await this.businessesService.flushChanges();
|
await this.em.flush();
|
||||||
|
|
||||||
return {
|
return {
|
||||||
message: BusinessMessage.DOMAIN_UPDATED,
|
message: BusinessMessage.DOMAIN_UPDATED,
|
||||||
domain: business.domain,
|
domain: business.domain,
|
||||||
isVerified: business.isDomainVerified,
|
isVerified: business.isDomainVerified,
|
||||||
verificationMethods: this.getVerificationInstructions(business.domain, verificationToken),
|
dnsInstructions: this.getDomainSetupInstructions(business.domain, verificationToken),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -73,12 +84,12 @@ export class DomainVerificationService {
|
|||||||
// If no token exists, generate one
|
// If no token exists, generate one
|
||||||
if (!business.domainVerificationToken) {
|
if (!business.domainVerificationToken) {
|
||||||
business.domainVerificationToken = this.generateVerificationToken();
|
business.domainVerificationToken = this.generateVerificationToken();
|
||||||
await this.businessesService.flushChanges();
|
await this.em.flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update verification method
|
// Update verification method
|
||||||
business.domainVerificationMethod = verifyDto.method;
|
business.domainVerificationMethod = verifyDto.method;
|
||||||
await this.businessesService.flushChanges();
|
await this.em.flush();
|
||||||
|
|
||||||
return {
|
return {
|
||||||
message: BusinessMessage.DOMAIN_VERIFICATION_INITIATED,
|
message: BusinessMessage.DOMAIN_VERIFICATION_INITIATED,
|
||||||
@@ -88,6 +99,44 @@ export class DomainVerificationService {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set domain and get verification and connection instructions
|
||||||
|
*/
|
||||||
|
async setDomainAndGetVerificationInstructions(businessId: string, domain: string) {
|
||||||
|
const business = await this.businessesService.getBusinessById(businessId);
|
||||||
|
|
||||||
|
// Domain hasn't changed, check if it's already verified
|
||||||
|
if (business.domain === domain && business.isDomainVerified) {
|
||||||
|
return {
|
||||||
|
message: BusinessMessage.DOMAIN_ALREADY_VERIFIED,
|
||||||
|
domain: business.domain,
|
||||||
|
isVerified: business.isDomainVerified,
|
||||||
|
dnsInstructions: business.domainVerificationToken
|
||||||
|
? this.getDomainSetupInstructions(domain, business.domainVerificationToken)
|
||||||
|
: this.getDomainSetupInstructions(domain, ""),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate verification token
|
||||||
|
const verificationToken = this.generateVerificationToken();
|
||||||
|
|
||||||
|
// Update the domain and verification data
|
||||||
|
business.domain = domain;
|
||||||
|
business.isDomainVerified = false;
|
||||||
|
business.domainVerificationToken = verificationToken;
|
||||||
|
business.domainVerifiedAt = undefined;
|
||||||
|
|
||||||
|
await this.em.flush();
|
||||||
|
|
||||||
|
// Return DNS verification and setup instructions
|
||||||
|
return {
|
||||||
|
message: BusinessMessage.DOMAIN_VERIFICATION_INITIATED,
|
||||||
|
domain: business.domain,
|
||||||
|
isVerified: false,
|
||||||
|
dnsInstructions: this.getDomainSetupInstructions(domain, verificationToken),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check domain verification status
|
* Check domain verification status
|
||||||
*/
|
*/
|
||||||
@@ -112,7 +161,7 @@ export class DomainVerificationService {
|
|||||||
|
|
||||||
switch (business.domainVerificationMethod) {
|
switch (business.domainVerificationMethod) {
|
||||||
case DomainVerificationMethod.DNS_TXT:
|
case DomainVerificationMethod.DNS_TXT:
|
||||||
isVerified = await this.verifyDnsTxtRecord(business.domain, business.domainVerificationToken);
|
isVerified = await this.checkDnsTxtRecord(business.domain, business.domainVerificationToken);
|
||||||
break;
|
break;
|
||||||
case DomainVerificationMethod.FILE:
|
case DomainVerificationMethod.FILE:
|
||||||
isVerified = await this.verifyFileMethod(business.domain, business.domainVerificationToken);
|
isVerified = await this.verifyFileMethod(business.domain, business.domainVerificationToken);
|
||||||
@@ -126,8 +175,8 @@ export class DomainVerificationService {
|
|||||||
|
|
||||||
if (isVerified) {
|
if (isVerified) {
|
||||||
business.isDomainVerified = true;
|
business.isDomainVerified = true;
|
||||||
business.domainVerifiedAt = new Date();
|
business.domainVerifiedAt = dayjs().toDate();
|
||||||
await this.businessesService.flushChanges();
|
await this.em.flush();
|
||||||
|
|
||||||
return {
|
return {
|
||||||
message: BusinessMessage.DOMAIN_VERIFICATION_SUCCESS,
|
message: BusinessMessage.DOMAIN_VERIFICATION_SUCCESS,
|
||||||
@@ -150,6 +199,82 @@ export class DomainVerificationService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verify DNS TXT record and domain pointing
|
||||||
|
*/
|
||||||
|
async verifyDnsTxtRecord(businessId: string) {
|
||||||
|
const business = await this.businessesService.getBusinessById(businessId);
|
||||||
|
|
||||||
|
if (!business.domain || !business.domainVerificationToken) {
|
||||||
|
throw new BadRequestException(BusinessMessage.DOMAIN_INVALID);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (business.isDomainVerified) {
|
||||||
|
const domainPointsToServer = await this.checkDomainPointsToServer(business.domain);
|
||||||
|
|
||||||
|
return {
|
||||||
|
message: BusinessMessage.DOMAIN_ALREADY_VERIFIED,
|
||||||
|
domain: business.domain,
|
||||||
|
isVerified: true,
|
||||||
|
verifiedAt: business.domainVerifiedAt,
|
||||||
|
domainPointsToServer,
|
||||||
|
serverConnectionStatus: domainPointsToServer
|
||||||
|
? "Your domain is properly set up and pointing to our servers"
|
||||||
|
: "Your domain is verified, but not properly pointing to our servers",
|
||||||
|
setupInstructions: !domainPointsToServer ? this.getDomainConnectionInstructions(business.domain) : null,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// First check domain ownership verification via TXT record
|
||||||
|
const isVerified = await this.checkDnsTxtRecord(business.domain, business.domainVerificationToken);
|
||||||
|
|
||||||
|
// Then check if domain points to our server (A or CNAME record)
|
||||||
|
const domainPointsToServer = await this.checkDomainPointsToServer(business.domain);
|
||||||
|
|
||||||
|
// If TXT verification passes, mark the domain as verified regardless of A/CNAME
|
||||||
|
if (isVerified) {
|
||||||
|
// Update domain verification status
|
||||||
|
business.isDomainVerified = true;
|
||||||
|
business.domainVerifiedAt = dayjs().toDate();
|
||||||
|
await this.em.flush();
|
||||||
|
|
||||||
|
return {
|
||||||
|
message: BusinessMessage.DOMAIN_VERIFICATION_SUCCESS,
|
||||||
|
domain: business.domain,
|
||||||
|
isVerified: true,
|
||||||
|
verifiedAt: business.domainVerifiedAt,
|
||||||
|
domainPointsToServer,
|
||||||
|
serverConnectionStatus: domainPointsToServer
|
||||||
|
? "Your domain is properly set up and pointing to our servers"
|
||||||
|
: "Your domain is verified, but not properly pointing to our servers",
|
||||||
|
connectionInstructions: !domainPointsToServer ? this.getDomainConnectionInstructions(business.domain) : null,
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
message: BusinessMessage.DOMAIN_VERIFICATION_FAILED,
|
||||||
|
domain: business.domain,
|
||||||
|
isVerified: false,
|
||||||
|
domainPointsToServer,
|
||||||
|
serverConnectionStatus: domainPointsToServer
|
||||||
|
? "Your domain is pointing to our servers, but ownership verification failed"
|
||||||
|
: "Both ownership verification and server connection failed",
|
||||||
|
setupInstructions: this.getDomainSetupInstructions(business.domain, business.domainVerificationToken || ""),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
} catch (error: unknown) {
|
||||||
|
this.logger.error(`Domain verification failed: ${error instanceof Error ? error.message : String(error)}`);
|
||||||
|
|
||||||
|
return {
|
||||||
|
message: BusinessMessage.DOMAIN_VERIFICATION_FAILED,
|
||||||
|
domain: business.domain,
|
||||||
|
isVerified: false,
|
||||||
|
error: error instanceof Error ? error.message : String(error),
|
||||||
|
setupInstructions: this.getDomainSetupInstructions(business.domain, business.domainVerificationToken || ""),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get domain verification status
|
* Get domain verification status
|
||||||
*/
|
*/
|
||||||
@@ -162,13 +287,139 @@ export class DomainVerificationService {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check if domain points to our server (A or CNAME record)
|
||||||
|
const domainPointsToServer = await this.checkDomainPointsToServer(business.domain);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
hasDomain: true,
|
hasDomain: true,
|
||||||
domain: business.domain,
|
domain: business.domain,
|
||||||
isVerified: business.isDomainVerified,
|
isVerified: business.isDomainVerified,
|
||||||
verificationMethod: business.domainVerificationMethod,
|
|
||||||
verifiedAt: business.domainVerifiedAt,
|
verifiedAt: business.domainVerifiedAt,
|
||||||
instructions: business.domainVerificationToken ? this.getVerificationInstructions(business.domain, business.domainVerificationToken) : null,
|
domainPointsToServer,
|
||||||
|
verificationStatus: business.isDomainVerified ? "Verified" : "Not Verified",
|
||||||
|
connectionStatus: domainPointsToServer ? "Connected" : "Not Connected",
|
||||||
|
fullSetupComplete: business.isDomainVerified && domainPointsToServer,
|
||||||
|
connectionInstructions: !domainPointsToServer ? this.getDomainConnectionInstructions(business.domain) : null,
|
||||||
|
setupInstructions: !business.isDomainVerified ? this.getDomainSetupInstructions(business.domain, business.domainVerificationToken || "") : null,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verify domain using file method
|
||||||
|
*/
|
||||||
|
private async verifyFileMethod(domain: string, token: string): Promise<boolean> {
|
||||||
|
try {
|
||||||
|
// Try HTTPS first
|
||||||
|
try {
|
||||||
|
const response = await axios.get(`https://${domain}/.well-known/${token}.html`, {
|
||||||
|
timeout: 5000, // 5 seconds timeout
|
||||||
|
headers: {
|
||||||
|
"User-Agent": "Mozilla/5.0 (compatible; DzoneVerification/1.0; +https://dzone.danakcorp.com/domain-verification)",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (response.status === 200 && response.data.toString().trim() === token.trim()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
this.logger.debug(`HTTPS file verification failed, trying HTTP: ${error instanceof Error ? error.message : String(error)}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try HTTP if HTTPS fails
|
||||||
|
const httpResponse = await axios.get(`http://${domain}/.well-known/${token}.html`, {
|
||||||
|
timeout: 5000,
|
||||||
|
headers: {
|
||||||
|
"User-Agent": "Mozilla/5.0 (compatible; DzoneVerification/1.0; +https://dzone.danakcorp.com/domain-verification)",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (httpResponse.status === 200 && httpResponse.data.toString().trim() === token.trim()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
} catch (error: unknown) {
|
||||||
|
this.logger.error(`Error verifying file method: ${error instanceof Error ? error.message : String(error)}`);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verify domain using meta tag method
|
||||||
|
*/
|
||||||
|
private async verifyMetaTagMethod(domain: string, token: string): Promise<boolean> {
|
||||||
|
try {
|
||||||
|
// Try HTTPS first
|
||||||
|
try {
|
||||||
|
const response = await axios.get(`https://${domain}`, {
|
||||||
|
timeout: 5000, // 5 seconds timeout
|
||||||
|
headers: {
|
||||||
|
"User-Agent": "Mozilla/5.0 (compatible; DzoneVerification/1.0; +https://dzone.danakcorp.com/domain-verification)",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (response.status === 200) {
|
||||||
|
// Check for meta tag in the HTML
|
||||||
|
const content = response.data.toString();
|
||||||
|
const metaTagRegex = new RegExp(`<meta[^>]*name=["']dzone-verification["'][^>]*content=["']${token}["']`, "i");
|
||||||
|
if (metaTagRegex.test(content)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
this.logger.debug(`HTTPS meta tag verification failed, trying HTTP: ${error instanceof Error ? error.message : String(error)}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try HTTP if HTTPS fails
|
||||||
|
const httpResponse = await axios.get(`http://${domain}`, {
|
||||||
|
timeout: 5000,
|
||||||
|
headers: {
|
||||||
|
"User-Agent": "Mozilla/5.0 (compatible; DzoneVerification/1.0; +https://dzone.danakcorp.com/domain-verification)",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (httpResponse.status === 200) {
|
||||||
|
// Check for meta tag in the HTML
|
||||||
|
const content = httpResponse.data.toString();
|
||||||
|
const metaTagRegex = new RegExp(`<meta[^>]*name=["']dzone-verification["'][^>]*content=["']${token}["']`, "i");
|
||||||
|
return metaTagRegex.test(content);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
} catch (error: unknown) {
|
||||||
|
this.logger.error(`Error verifying meta tag method: ${error instanceof Error ? error.message : String(error)}`);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get domain verification and connection status
|
||||||
|
*/
|
||||||
|
async getDomainStatus(businessId: string) {
|
||||||
|
const business = await this.businessesService.getBusinessById(businessId);
|
||||||
|
|
||||||
|
if (!business.domain) {
|
||||||
|
return {
|
||||||
|
hasDomain: false,
|
||||||
|
message: "No domain set for this business",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const domainPointsToServer = await this.checkDomainPointsToServer(business.domain);
|
||||||
|
|
||||||
|
return {
|
||||||
|
hasDomain: true,
|
||||||
|
domain: business.domain,
|
||||||
|
isVerified: business.isDomainVerified,
|
||||||
|
verifiedAt: business.domainVerifiedAt,
|
||||||
|
domainPointsToServer,
|
||||||
|
verificationStatus: business.isDomainVerified ? "Verified" : "Not Verified",
|
||||||
|
connectionStatus: domainPointsToServer ? "Connected" : "Not Connected",
|
||||||
|
fullSetupComplete: business.isDomainVerified && domainPointsToServer,
|
||||||
|
setupInstructions:
|
||||||
|
!business.isDomainVerified || !domainPointsToServer
|
||||||
|
? this.getDomainSetupInstructions(business.domain, business.domainVerificationToken || "")
|
||||||
|
: null,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -206,14 +457,86 @@ export class DomainVerificationService {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get domain connection instructions
|
||||||
|
*/
|
||||||
|
private getDomainConnectionInstructions(_domain: string) {
|
||||||
|
// The domain parameter is used for contextual purposes in some implementations
|
||||||
|
// but not directly in this template
|
||||||
|
return {
|
||||||
|
title: "Point your domain to our servers",
|
||||||
|
description: "Choose ONE of these options to connect your domain:",
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
title: "Option A: Using A Record (Direct IP)",
|
||||||
|
steps: [
|
||||||
|
"1. Add an A record in your DNS settings",
|
||||||
|
"2. Set the host to @ or your subdomain (e.g., www)",
|
||||||
|
"3. Set the value to our server IP: " + this.serverIP,
|
||||||
|
"4. Set TTL to 3600 (1 hour) or the lowest available",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Option B: Using CNAME Record (Recommended)",
|
||||||
|
steps: [
|
||||||
|
"1. Add a CNAME record in your DNS settings",
|
||||||
|
"2. Set the host to @ or your subdomain (e.g., www)",
|
||||||
|
"3. Set the value to: " + this.serverDomain,
|
||||||
|
"4. Set TTL to 3600 (1 hour) or the lowest available",
|
||||||
|
"Note: Some DNS providers don't allow CNAME for root domains (@)",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
troubleshooting: [
|
||||||
|
"- Make sure you've added the records to the correct domain",
|
||||||
|
"- Some DNS providers require the @ symbol for root domains",
|
||||||
|
"- Check that you've copied the values exactly as shown",
|
||||||
|
"- DNS changes can take time to propagate (typically 15 minutes to 48 hours)",
|
||||||
|
],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get comprehensive domain setup instructions
|
||||||
|
*/
|
||||||
|
private getDomainSetupInstructions(domain: string, token: string) {
|
||||||
|
return {
|
||||||
|
verificationInstructions: {
|
||||||
|
title: "Step 1: Verify domain ownership",
|
||||||
|
description: "Add this TXT record to prove you own the domain:",
|
||||||
|
host: "@", // Root domain
|
||||||
|
type: "TXT", // Record type
|
||||||
|
value: token, // Token value
|
||||||
|
ttl: "3600", // Time to live (1 hour)
|
||||||
|
},
|
||||||
|
connectionInstructions: this.getDomainConnectionInstructions(domain),
|
||||||
|
general: {
|
||||||
|
verificationEndpoint: `/business/domain/verify/check-dns`,
|
||||||
|
propagationNote: "DNS changes can take 15 minutes to 48 hours to propagate worldwide",
|
||||||
|
troubleshooting: [
|
||||||
|
"- Make sure you've added the records to the correct domain",
|
||||||
|
"- Some DNS providers require the @ symbol for root domains",
|
||||||
|
"- Check that you've copied the values exactly as shown",
|
||||||
|
"- Wait at least 15 minutes before checking verification status",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Verify domain using DNS TXT record
|
* Verify domain using DNS TXT record
|
||||||
*/
|
*/
|
||||||
private async verifyDnsTxtRecord(domain: string, token: string): Promise<boolean> {
|
private async checkDnsTxtRecord(domain: string, token: string): Promise<boolean> {
|
||||||
try {
|
try {
|
||||||
|
this.logger.log(`Checking TXT records for domain: ${domain}`);
|
||||||
|
|
||||||
|
// Try to resolve TXT records
|
||||||
const txtRecords = await this.resolveTxt(domain);
|
const txtRecords = await this.resolveTxt(domain);
|
||||||
|
|
||||||
// Flatten the results and check if any match our token
|
// Flatten the results and check if any match our token
|
||||||
const records = txtRecords.flat();
|
const records = txtRecords.flat();
|
||||||
|
this.logger.log(`Found TXT records: ${JSON.stringify(records)}`);
|
||||||
|
|
||||||
return records.some((record) => record === token);
|
return records.some((record) => record === token);
|
||||||
} catch (error: unknown) {
|
} catch (error: unknown) {
|
||||||
this.logger.error(`Error verifying DNS TXT record: ${error instanceof Error ? error.message : String(error)}`);
|
this.logger.error(`Error verifying DNS TXT record: ${error instanceof Error ? error.message : String(error)}`);
|
||||||
@@ -222,89 +545,42 @@ export class DomainVerificationService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Verify domain using file method
|
* Check if domain points to our server via A or CNAME record
|
||||||
*/
|
*/
|
||||||
private async verifyFileMethod(domain: string, token: string): Promise<boolean> {
|
private async checkDomainPointsToServer(domain: string): Promise<boolean> {
|
||||||
try {
|
try {
|
||||||
// Try HTTPS first
|
// Try CNAME first
|
||||||
try {
|
try {
|
||||||
const response = await axios.get(`https://${domain}/.well-known/${token}.html`, {
|
const cnameRecords = await this.resolveCname(domain);
|
||||||
timeout: 5000, // 5 seconds timeout
|
|
||||||
headers: {
|
|
||||||
"User-Agent": "Mozilla/5.0 (compatible; DzoneVerification/1.0; +https://dzone.com/domain-verification)",
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
if (response.status === 200 && response.data.toString().trim() === token.trim()) {
|
this.logger.log(`Found CNAME records for ${domain}: ${JSON.stringify(cnameRecords)}`);
|
||||||
|
|
||||||
|
// Check if any CNAME points to our server domain
|
||||||
|
if (cnameRecords.some((record) => record === this.serverDomain)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (_error) {
|
||||||
this.logger.debug(`HTTPS file verification failed, trying HTTP: ${error instanceof Error ? error.message : String(error)}`);
|
this.logger.debug(`No CNAME records found for ${domain}, trying A records`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try HTTP if HTTPS fails
|
// Try A record
|
||||||
const httpResponse = await axios.get(`http://${domain}/.well-known/${token}.html`, {
|
|
||||||
timeout: 5000,
|
|
||||||
headers: {
|
|
||||||
"User-Agent": "Mozilla/5.0 (compatible; DzoneVerification/1.0; +https://dzone.com/domain-verification)",
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
if (httpResponse.status === 200 && httpResponse.data.toString().trim() === token.trim()) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
} catch (error: unknown) {
|
|
||||||
this.logger.error(`Error verifying file method: ${error instanceof Error ? error.message : String(error)}`);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Verify domain using meta tag method
|
|
||||||
*/
|
|
||||||
private async verifyMetaTagMethod(domain: string, token: string): Promise<boolean> {
|
|
||||||
try {
|
|
||||||
// Try HTTPS first
|
|
||||||
try {
|
try {
|
||||||
const response = await axios.get(`https://${domain}`, {
|
const aRecords = await this.resolveA(domain);
|
||||||
timeout: 5000, // 5 seconds timeout
|
|
||||||
headers: {
|
|
||||||
"User-Agent": "Mozilla/5.0 (compatible; DzoneVerification/1.0; +https://dzone.com/domain-verification)",
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
if (response.status === 200) {
|
this.logger.log(`Found A records for ${domain}: ${JSON.stringify(aRecords)}`);
|
||||||
// Check for meta tag in the HTML
|
|
||||||
const content = response.data.toString();
|
// Check if any A record points to our server IP
|
||||||
const metaTagRegex = new RegExp(`<meta[^>]*name=["']dzone-verification["'][^>]*content=["']${token}["']`, "i");
|
if (aRecords.some((record) => record === this.serverIP)) {
|
||||||
if (metaTagRegex.test(content)) {
|
return true;
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (_error) {
|
||||||
this.logger.debug(`HTTPS meta tag verification failed, trying HTTP: ${error instanceof Error ? error.message : String(error)}`);
|
this.logger.debug(`No A records found for ${domain}`);
|
||||||
}
|
|
||||||
|
|
||||||
// Try HTTP if HTTPS fails
|
|
||||||
const httpResponse = await axios.get(`http://${domain}`, {
|
|
||||||
timeout: 5000,
|
|
||||||
headers: {
|
|
||||||
"User-Agent": "Mozilla/5.0 (compatible; DzoneVerification/1.0; +https://dzone.com/domain-verification)",
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
if (httpResponse.status === 200) {
|
|
||||||
// Check for meta tag in the HTML
|
|
||||||
const content = httpResponse.data.toString();
|
|
||||||
const metaTagRegex = new RegExp(`<meta[^>]*name=["']dzone-verification["'][^>]*content=["']${token}["']`, "i");
|
|
||||||
return metaTagRegex.test(content);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If we get here, neither CNAME nor A records point to our server
|
||||||
return false;
|
return false;
|
||||||
} catch (error: unknown) {
|
} catch (error: unknown) {
|
||||||
this.logger.error(`Error verifying meta tag method: ${error instanceof Error ? error.message : String(error)}`);
|
this.logger.error(`Error checking domain DNS records: ${error instanceof Error ? error.message : String(error)}`);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,12 +5,12 @@ import { PaymentMessage } from "../../../common/enums/message.enum";
|
|||||||
|
|
||||||
export class CheckoutPaymentDto {
|
export class CheckoutPaymentDto {
|
||||||
@IsNotEmpty({ message: PaymentMessage.GATEWAY_REQUIRED })
|
@IsNotEmpty({ message: PaymentMessage.GATEWAY_REQUIRED })
|
||||||
@IsUUID("4", { message: PaymentMessage.GATEWAY_ID_SHOULD_BE_UUID })
|
@IsUUID("7", { message: PaymentMessage.GATEWAY_ID_SHOULD_BE_UUID })
|
||||||
@ApiProperty({ description: "Gateway id to pay", example: "e6fdce2a-9f91-47c4-8561-48368fc275b5" })
|
@ApiProperty({ description: "Gateway id to pay", example: "e6fdce2a-9f91-47c4-8561-48368fc275b5" })
|
||||||
gatewayId: string;
|
gatewayId: string;
|
||||||
|
|
||||||
@IsNotEmpty({ message: PaymentMessage.INVOICE_ID_REQUIRED })
|
@IsNotEmpty({ message: PaymentMessage.INVOICE_ID_REQUIRED })
|
||||||
@IsUUID("4", { message: PaymentMessage.INVOICE_ID_SHOULD_BE_UUID })
|
@IsUUID("7", { message: PaymentMessage.INVOICE_ID_SHOULD_BE_UUID })
|
||||||
@ApiProperty({ description: "Invoice id to pay", example: "e6fdce2a-9f91-47c4-8561-48368fc275b5" })
|
@ApiProperty({ description: "Invoice id to pay", example: "e6fdce2a-9f91-47c4-8561-48368fc275b5" })
|
||||||
invoiceId: string;
|
invoiceId: string;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -205,7 +205,7 @@ export class PaymentProcessor extends WorkerProcessor {
|
|||||||
const verifyData = await gateway.verifyPayment({
|
const verifyData = await gateway.verifyPayment({
|
||||||
reference: payment.reference,
|
reference: payment.reference,
|
||||||
amount: payment.amount,
|
amount: payment.amount,
|
||||||
merchantId: payment.business.zarinpalMerchantId || this.zarinpalMerchantId,
|
merchantId: payment.business.zarinpalMerchantId ?? this.zarinpalMerchantId,
|
||||||
});
|
});
|
||||||
|
|
||||||
const result = await this.paymentsService.processVerificationResult(verifyData, payment, em);
|
const result = await this.paymentsService.processVerificationResult(verifyData, payment, em);
|
||||||
|
|||||||
@@ -58,17 +58,17 @@ export class PaymentsService {
|
|||||||
if (invoice.company.user.id !== user.id) throw new BadRequestException(InvoiceMessage.NOT_FOUND_BY_ID_OR_NOT_BELONG_TO_USER);
|
if (invoice.company.user.id !== user.id) throw new BadRequestException(InvoiceMessage.NOT_FOUND_BY_ID_OR_NOT_BELONG_TO_USER);
|
||||||
|
|
||||||
const gatewayData = await this.processPayment(paymentGateway.name, {
|
const gatewayData = await this.processPayment(paymentGateway.name, {
|
||||||
amount: invoice.totalPrice.toNumber(),
|
amount: new Decimal(invoice.totalPrice).toNumber(),
|
||||||
description: PaymentMessage.CHECKOUT_INVOICE_MESSAGE.replace("[invoiceNumber]", invoice.numericId.toString()),
|
description: PaymentMessage.CHECKOUT_INVOICE_MESSAGE.replace("[invoiceNumber]", invoice.numericId.toString()),
|
||||||
email: user.email,
|
email: user.email,
|
||||||
mobile: user.phone,
|
mobile: user.phone,
|
||||||
callBackPath: invoice.business.slug,
|
callBackPath: invoice.business.slug,
|
||||||
merchantId: invoice.business.zarinpalMerchantId || this.zarinpalMerchantId,
|
merchantId: invoice.business.zarinpalMerchantId ?? this.zarinpalMerchantId,
|
||||||
});
|
});
|
||||||
|
|
||||||
const payment = await this.createGatewayPaymentForUser(
|
const payment = await this.createGatewayPaymentForUser(
|
||||||
user,
|
user,
|
||||||
invoice.totalPrice.toNumber(),
|
new Decimal(invoice.totalPrice).toNumber(),
|
||||||
gatewayData.reference,
|
gatewayData.reference,
|
||||||
paymentGateway.id,
|
paymentGateway.id,
|
||||||
invoice.business.id,
|
invoice.business.id,
|
||||||
@@ -84,6 +84,7 @@ export class PaymentsService {
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
await em.rollback();
|
await em.rollback();
|
||||||
if (error instanceof HttpException) throw error;
|
if (error instanceof HttpException) throw error;
|
||||||
|
this.logger.error("Payment checkout failed:", error);
|
||||||
throw new InternalServerErrorException(PaymentMessage.ERROR_IN_PAYMENT);
|
throw new InternalServerErrorException(PaymentMessage.ERROR_IN_PAYMENT);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -113,7 +114,7 @@ export class PaymentsService {
|
|||||||
const verifyData = await paymentGateway.verifyPayment({
|
const verifyData = await paymentGateway.verifyPayment({
|
||||||
reference: queryDto.Authority,
|
reference: queryDto.Authority,
|
||||||
amount: payment.amount,
|
amount: payment.amount,
|
||||||
merchantId: payment.business.zarinpalMerchantId || this.zarinpalMerchantId,
|
merchantId: payment.business.zarinpalMerchantId ?? this.zarinpalMerchantId,
|
||||||
});
|
});
|
||||||
|
|
||||||
return await this.handlePaymentVerificationWithResponse(verifyData, payment, rep, frontUrl, em);
|
return await this.handlePaymentVerificationWithResponse(verifyData, payment, rep, frontUrl, em);
|
||||||
@@ -289,8 +290,12 @@ export class PaymentsService {
|
|||||||
//===============================================
|
//===============================================
|
||||||
|
|
||||||
private async getPaymentByReference(reference: string, em: EntityManager): Promise<Payment> {
|
private async getPaymentByReference(reference: string, em: EntityManager): Promise<Payment> {
|
||||||
const payment = await em.findOne(Payment, { reference }, { populate: ["user", "invoice", "business"], lockMode: LockMode.PESSIMISTIC_WRITE });
|
// First get the payment with a lock, but without joins
|
||||||
|
const payment = await em.findOne(Payment, { reference }, { lockMode: LockMode.PESSIMISTIC_WRITE });
|
||||||
if (!payment) throw new BadRequestException(PaymentMessage.PAYMENT_NOT_FOUND_WITH_REF);
|
if (!payment) throw new BadRequestException(PaymentMessage.PAYMENT_NOT_FOUND_WITH_REF);
|
||||||
|
|
||||||
|
// Then populate relations after acquiring the lock
|
||||||
|
await em.populate(payment, ["user", "invoice", "business"]);
|
||||||
return payment;
|
return payment;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,11 +4,12 @@ import { ApiBody, ApiConsumes, ApiOperation, ApiTags } from "@nestjs/swagger";
|
|||||||
|
|
||||||
import { UploadMultipleFileDto, UploadSingleFileDto } from "./DTO/upload-file.dto";
|
import { UploadMultipleFileDto, UploadSingleFileDto } from "./DTO/upload-file.dto";
|
||||||
import { UploaderService } from "./uploader.service";
|
import { UploaderService } from "./uploader.service";
|
||||||
|
import { AuthGuards } from "../../common/decorators/auth-guard.decorator";
|
||||||
import { IFile } from "../utils/interfaces/IFile";
|
import { IFile } from "../utils/interfaces/IFile";
|
||||||
|
|
||||||
@ApiTags("Uploader")
|
@ApiTags("Uploader")
|
||||||
@Controller("uploader")
|
@Controller("uploader")
|
||||||
// @AuthGuards()
|
@AuthGuards()
|
||||||
export class UploaderController {
|
export class UploaderController {
|
||||||
constructor(private readonly uploaderService: UploaderService) {}
|
constructor(private readonly uploaderService: UploaderService) {}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user