chore: add domain logic
This commit is contained in:
+1
-1
@@ -25,7 +25,7 @@
|
|||||||
"orm:down": "mikro-orm migration:down --config=database/mikro-orm.config.ts",
|
"orm:down": "mikro-orm migration:down --config=database/mikro-orm.config.ts",
|
||||||
"orm:generate": "mikro-orm migration:create --blank --config=database/mikro-orm.config.ts",
|
"orm:generate": "mikro-orm migration:create --blank --config=database/mikro-orm.config.ts",
|
||||||
"orm:update": "mikro-orm schema:update --run --config=database/mikro-orm.config.ts",
|
"orm:update": "mikro-orm schema:update --run --config=database/mikro-orm.config.ts",
|
||||||
"orm:drop": "mikro-orm schema:drop --run --config=database/mikro-orm.config.ts",
|
"orm:drop": "mikro-orm schema:drop --run --drop-migrations-table --drop-db --config=database/mikro-orm.config.ts",
|
||||||
"orm:seed": "mikro-orm seeder:create --config=database/mikro-orm.config.ts",
|
"orm:seed": "mikro-orm seeder:create --config=database/mikro-orm.config.ts",
|
||||||
"seed:run": "mikro-orm seeder:run --config=database/mikro-orm.config.ts"
|
"seed:run": "mikro-orm seeder:run --config=database/mikro-orm.config.ts"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -612,4 +612,36 @@ export const enum BusinessMessage {
|
|||||||
BUSINESS_SETTINGS_UPDATED = "تنظیمات کسب و کار با موفقیت به روز رسانی شد",
|
BUSINESS_SETTINGS_UPDATED = "تنظیمات کسب و کار با موفقیت به روز رسانی شد",
|
||||||
NAME_REQUIRED = "نام کسب و کار مورد نیاز است",
|
NAME_REQUIRED = "نام کسب و کار مورد نیاز است",
|
||||||
NAME_STRING = "نام کسب و کار باید یک رشته باشد",
|
NAME_STRING = "نام کسب و کار باید یک رشته باشد",
|
||||||
|
DOMAIN_CONNECTED = "دامنه با موفقیت متصل شد",
|
||||||
|
DOMAIN_NOT_CONNECTED = "دامنه به سرور ما متصل نشد",
|
||||||
|
DOMAIN_NOT_FOUND = "دامنه یافت نشد",
|
||||||
|
DOMAIN_VERIFICATION_TOKEN_REQUIRED = "توکن تایید دامنه مورد نیاز است",
|
||||||
|
|
||||||
|
// Domain Connection Instructions in Farsi
|
||||||
|
DOMAIN_CONNECTION_TITLE = "دامنه خود را به سرورهای ما متصل کنید",
|
||||||
|
DOMAIN_CONNECTION_DESCRIPTION = "یکی از این گزینهها را برای اتصال دامنه خود انتخاب کنید:",
|
||||||
|
DOMAIN_CONNECTION_OPTION_A_TITLE = "گزینه A: استفاده از رکورد A (IP مستقیم)",
|
||||||
|
DOMAIN_CONNECTION_OPTION_A_STEP1 = "۱. یک رکورد A در تنظیمات DNS خود اضافه کنید",
|
||||||
|
DOMAIN_CONNECTION_OPTION_A_STEP2 = "۲. میزبان را روی @ یا زیردامنه خود (مثلاً www) تنظیم کنید - رکورد A",
|
||||||
|
DOMAIN_CONNECTION_OPTION_A_STEP3 = "۳. مقدار را به IP سرور ما تنظیم کنید: ",
|
||||||
|
DOMAIN_CONNECTION_OPTION_A_STEP4 = "۴. TTL را روی ۳۶۰۰ (۱ ساعت) یا کمترین مقدار موجود تنظیم کنید - رکورد A",
|
||||||
|
DOMAIN_CONNECTION_OPTION_B_TITLE = "گزینه B: استفاده از رکورد CNAME (توصیه شده)",
|
||||||
|
DOMAIN_CONNECTION_OPTION_B_STEP1 = "۱. یک رکورد CNAME در تنظیمات DNS خود اضافه کنید",
|
||||||
|
DOMAIN_CONNECTION_OPTION_B_STEP2 = "۲. میزبان را روی @ یا زیردامنه خود (مثلاً www) تنظیم کنید - رکورد CNAME",
|
||||||
|
DOMAIN_CONNECTION_OPTION_B_STEP3 = "۳. مقدار را تنظیم کنید به: ",
|
||||||
|
DOMAIN_CONNECTION_OPTION_B_STEP4 = "۴. TTL را روی ۳۶۰۰ (۱ ساعت) یا کمترین مقدار موجود تنظیم کنید - رکورد CNAME",
|
||||||
|
DOMAIN_CONNECTION_OPTION_B_NOTE = "توجه: برخی از ارائهدهندگان DNS اجازه CNAME برای دامنههای اصلی (@) را نمیدهند",
|
||||||
|
DOMAIN_CONNECTION_TROUBLESHOOTING_1 = "- مطمئن شوید که رکوردها را به دامنه صحیح اضافه کردهاید",
|
||||||
|
DOMAIN_CONNECTION_TROUBLESHOOTING_2 = "- برخی از ارائهدهندگان DNS برای دامنههای اصلی به نماد @ نیاز دارند",
|
||||||
|
DOMAIN_CONNECTION_TROUBLESHOOTING_3 = "- بررسی کنید که مقادیر را دقیقاً همانطور که نشان داده شده کپی کردهاید",
|
||||||
|
DOMAIN_CONNECTION_TROUBLESHOOTING_4 = "- تغییرات DNS میتواند برای انتشار زمان ببرد (معمولاً ۱۵ دقیقه تا ۴۸ ساعت)",
|
||||||
|
|
||||||
|
// Domain Setup Instructions in Farsi
|
||||||
|
DOMAIN_SETUP_VERIFICATION_TITLE = "مرحله ۱: تایید مالکیت دامنه",
|
||||||
|
DOMAIN_SETUP_VERIFICATION_DESC = "این رکورد TXT را اضافه کنید تا ثابت کنید مالک دامنه هستید:",
|
||||||
|
DOMAIN_SETUP_PROPAGATION_NOTE = "تغییرات DNS میتواند ۱۵ دقیقه تا ۴۸ ساعت در سراسر جهان منتشر شود",
|
||||||
|
DOMAIN_SETUP_TROUBLESHOOTING_1 = "- اطمینان حاصل کنید که رکوردها را به دامنه صحیح اضافه کردهاید",
|
||||||
|
DOMAIN_SETUP_TROUBLESHOOTING_2 = "- بعضی از ارائهدهندگان DNS برای دامنههای اصلی به نماد @ نیاز دارند",
|
||||||
|
DOMAIN_SETUP_TROUBLESHOOTING_3 = "- اطمینان حاصل کنید که مقادیر را دقیقاً همانطور که نشان داده شده کپی کردهاید",
|
||||||
|
DOMAIN_SETUP_TROUBLESHOOTING_4 = "- حداقل ۱۵ دقیقه قبل از بررسی وضعیت تأیید صبر کنید",
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Body, Controller, Get, Param, Patch, Post, UseInterceptors } from "@nestjs/common";
|
import { Body, Controller, Get, Param, Patch, Post, UseInterceptors } from "@nestjs/common";
|
||||||
import { ApiHeader, ApiOperation } from "@nestjs/swagger";
|
import { ApiHeader, ApiOperation, ApiTags } from "@nestjs/swagger";
|
||||||
|
|
||||||
import { UpdateBusinessDomainDto } from "./DTO/business-domain.dto";
|
import { UpdateBusinessDomainDto } from "./DTO/business-domain.dto";
|
||||||
import { BusinessSlugParamDto } from "./DTO/business-slug-param.dto";
|
import { BusinessSlugParamDto } from "./DTO/business-slug-param.dto";
|
||||||
@@ -13,6 +13,7 @@ import { BusinessInterceptor } from "../../core/interceptors/business.intercepto
|
|||||||
|
|
||||||
@Controller("business")
|
@Controller("business")
|
||||||
@ApiHeader({ name: "x-business-id", description: "Business ID" })
|
@ApiHeader({ name: "x-business-id", description: "Business ID" })
|
||||||
|
@ApiTags("Business")
|
||||||
@UseInterceptors(BusinessInterceptor)
|
@UseInterceptors(BusinessInterceptor)
|
||||||
export class BusinessesController {
|
export class BusinessesController {
|
||||||
constructor(
|
constructor(
|
||||||
@@ -22,86 +23,43 @@ export class BusinessesController {
|
|||||||
|
|
||||||
@Get("slug/:slug")
|
@Get("slug/:slug")
|
||||||
@ApiOperation({ summary: "Get business by slug" })
|
@ApiOperation({ summary: "Get business by slug" })
|
||||||
async getBusinessBySlug(@Param() params: BusinessSlugParamDto) {
|
getBusinessBySlug(@Param() params: BusinessSlugParamDto) {
|
||||||
return this.businessesService.getBusinessBySlug(params.slug);
|
return this.businessesService.getBusinessBySlug(params.slug);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Patch("settings")
|
@Patch("settings")
|
||||||
@ApiOperation({ summary: "Update business settings" })
|
@ApiOperation({ summary: "Update business settings" })
|
||||||
@AuthGuards()
|
@AuthGuards()
|
||||||
async updateBusinessSettings(@BusinessDec("id") businessId: string, @Body() settingsDto: UpdateBusinessSettingsDto) {
|
updateBusinessSettings(@BusinessDec("id") businessId: string, @Body() settingsDto: UpdateBusinessSettingsDto) {
|
||||||
return this.businessesService.updateBusinessSettings(businessId, settingsDto);
|
return this.businessesService.updateBusinessSettings(businessId, settingsDto);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Get("settings")
|
@Get("settings")
|
||||||
@ApiOperation({ summary: "Get business settings" })
|
@ApiOperation({ summary: "Get business settings" })
|
||||||
@AuthGuards()
|
@AuthGuards()
|
||||||
async getBusinessSettings(@BusinessDec("id") businessId: string) {
|
getBusinessSettings(@BusinessDec("id") businessId: string) {
|
||||||
return this.businessesService.getBusinessSettings(businessId);
|
return this.businessesService.getBusinessSettings(businessId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Domain management endpoints - simplified flow
|
||||||
@Post("domain")
|
@Post("domain")
|
||||||
@ApiOperation({ summary: "Update business domain" })
|
@ApiOperation({ summary: "Set/update business domain and get verification instructions" })
|
||||||
@AuthGuards()
|
@AuthGuards()
|
||||||
async updateBusinessDomain(@BusinessDec() business: Business, @Body() domainDto: UpdateBusinessDomainDto) {
|
setDomain(@BusinessDec() business: Business, @Body() domainDto: UpdateBusinessDomainDto) {
|
||||||
return this.domainVerificationService.updateBusinessDomain(business.id, domainDto);
|
return this.domainVerificationService.setDomain(business.id, domainDto);
|
||||||
}
|
|
||||||
|
|
||||||
@Get("domain/status")
|
|
||||||
@ApiOperation({ summary: "Get business domain status" })
|
|
||||||
@AuthGuards()
|
|
||||||
async getDomainStatus(@BusinessDec() business: Business) {
|
|
||||||
return this.domainVerificationService.getDomainVerificationStatus(business.id);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Post("domain/verify")
|
@Post("domain/verify")
|
||||||
@ApiOperation({ summary: "Initiate domain verification" })
|
@ApiOperation({ summary: "Verify domain ownership and connection" })
|
||||||
@AuthGuards()
|
@AuthGuards()
|
||||||
async verifyDomain(@BusinessDec() business: Business) {
|
verifyDomain(@BusinessDec() business: Business) {
|
||||||
return this.domainVerificationService.initiateVerification(business.id);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Post("domain/verify/check")
|
|
||||||
@ApiOperation({ summary: "Check domain verification status" })
|
|
||||||
@AuthGuards()
|
|
||||||
async checkVerification(@BusinessDec() business: Business) {
|
|
||||||
return this.domainVerificationService.verifyDomain(business.id);
|
return this.domainVerificationService.verifyDomain(business.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
// DNS-specific domain verification endpoints
|
@Get("domain/status")
|
||||||
@Post("domain/set-domain")
|
@ApiOperation({ summary: "Get current domain status" })
|
||||||
@ApiOperation({ summary: "Set domain and get verification instructions" })
|
|
||||||
@AuthGuards()
|
@AuthGuards()
|
||||||
async setDomainAndGetInstructions(@BusinessDec() business: Business, @Body() domainDto: UpdateBusinessDomainDto) {
|
getDomainStatus(@BusinessDec() business: Business) {
|
||||||
return this.domainVerificationService.setDomainAndGetVerificationInstructions(business.id, domainDto.domain);
|
return this.domainVerificationService.getDomainStatus(business.id);
|
||||||
}
|
|
||||||
|
|
||||||
@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) {
|
|
||||||
return this.domainVerificationService.initiateVerification(businessId);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Post("admin/:businessId/domain/verify/check")
|
|
||||||
@ApiOperation({ summary: "Admin check domain verification status" })
|
|
||||||
@AuthGuards()
|
|
||||||
async adminCheckVerification(@Param("businessId") businessId: string) {
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ export class DomainVerificationService {
|
|||||||
private readonly resolveA = promisify(dns.resolve4);
|
private readonly resolveA = promisify(dns.resolve4);
|
||||||
private readonly resolveCname = promisify(dns.resolveCname);
|
private readonly resolveCname = promisify(dns.resolveCname);
|
||||||
|
|
||||||
private readonly serverIP = "IP_OF_YOUR_SERVER";
|
private readonly serverIP = "194.5.192.20";
|
||||||
private readonly serverDomain = "app.dzone.danakcorp.com";
|
private readonly serverDomain = "app.dzone.danakcorp.com";
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@@ -26,21 +26,27 @@ export class DomainVerificationService {
|
|||||||
) {}
|
) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update business domain
|
* Set Domain - Core method for setting/updating the domain and returning instructions
|
||||||
|
*
|
||||||
|
* This is the main entry point for users to set their domain
|
||||||
*/
|
*/
|
||||||
async updateBusinessDomain(businessId: string, domainDto: UpdateBusinessDomainDto) {
|
async setDomain(businessId: string, domainDto: UpdateBusinessDomainDto) {
|
||||||
const business = await this.businessesService.getBusinessById(businessId);
|
const business = await this.businessesService.getBusinessById(businessId);
|
||||||
|
|
||||||
|
// If domain hasn't changed and is already verified, just return success
|
||||||
if (business.domain === domainDto.domain && business.isDomainVerified) {
|
if (business.domain === domainDto.domain && business.isDomainVerified) {
|
||||||
return {
|
return {
|
||||||
message: BusinessMessage.DOMAIN_ALREADY_VERIFIED,
|
message: BusinessMessage.DOMAIN_ALREADY_VERIFIED,
|
||||||
domain: business.domain,
|
domain: business.domain,
|
||||||
isVerified: business.isDomainVerified,
|
isVerified: business.isDomainVerified,
|
||||||
|
verifiedAt: business.domainVerifiedAt,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Generate verification token
|
||||||
const verificationToken = this.generateVerificationToken();
|
const verificationToken = this.generateVerificationToken();
|
||||||
|
|
||||||
|
// Update domain info
|
||||||
business.domain = domainDto.domain;
|
business.domain = domainDto.domain;
|
||||||
business.isDomainVerified = false;
|
business.isDomainVerified = false;
|
||||||
business.domainVerificationToken = verificationToken;
|
business.domainVerificationToken = verificationToken;
|
||||||
@@ -48,133 +54,33 @@ export class DomainVerificationService {
|
|||||||
|
|
||||||
await this.em.flush();
|
await this.em.flush();
|
||||||
|
|
||||||
|
// Return setup instructions
|
||||||
return {
|
return {
|
||||||
message: BusinessMessage.DOMAIN_UPDATED,
|
message: BusinessMessage.DOMAIN_VERIFICATION_INITIATED,
|
||||||
domain: business.domain,
|
domain: business.domain,
|
||||||
isVerified: business.isDomainVerified,
|
isVerified: false,
|
||||||
dnsInstructions: this.getDomainSetupInstructions(business.domain, verificationToken),
|
dnsInstructions: this.getDomainSetupInstructions(business.domain, verificationToken),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initiate domain verification with specific method
|
* Verify Domain - Check domain ownership and connection status
|
||||||
|
*
|
||||||
|
* This method checks if the domain is properly configured with TXT record and
|
||||||
|
* if it points to our servers
|
||||||
*/
|
*/
|
||||||
async initiateVerification(businessId: string) {
|
|
||||||
const business = await this.businessesService.getBusinessById(businessId);
|
|
||||||
|
|
||||||
if (!business.domain) throw new BadRequestException(BusinessMessage.DOMAIN_INVALID);
|
|
||||||
|
|
||||||
if (business.isDomainVerified) {
|
|
||||||
return {
|
|
||||||
message: BusinessMessage.DOMAIN_ALREADY_VERIFIED,
|
|
||||||
domain: business.domain,
|
|
||||||
isVerified: true,
|
|
||||||
verifiedAt: business.domainVerifiedAt,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!business.domainVerificationToken) {
|
|
||||||
business.domainVerificationToken = this.generateVerificationToken();
|
|
||||||
await this.em.flush();
|
|
||||||
}
|
|
||||||
|
|
||||||
await this.em.flush();
|
|
||||||
|
|
||||||
return {
|
|
||||||
message: BusinessMessage.DOMAIN_VERIFICATION_INITIATED,
|
|
||||||
domain: business.domain,
|
|
||||||
instructions: this.getDomainSetupInstructions(business.domain, business.domainVerificationToken),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
//************************************** */
|
|
||||||
async verifyDomain(businessId: string) {
|
async verifyDomain(businessId: string) {
|
||||||
const business = await this.businessesService.getBusinessById(businessId);
|
const business = await this.businessesService.getBusinessById(businessId);
|
||||||
|
|
||||||
if (!business.domain || !business.domainVerificationToken) throw new BadRequestException(BusinessMessage.DOMAIN_VERIFICATION_METHOD_INVALID);
|
// Validate required fields
|
||||||
|
if (!business.domain) {
|
||||||
if (business.isDomainVerified) {
|
|
||||||
return {
|
return {
|
||||||
message: BusinessMessage.DOMAIN_ALREADY_VERIFIED,
|
hasDomain: false,
|
||||||
domain: business.domain,
|
message: BusinessMessage.DOMAIN_NOT_FOUND,
|
||||||
isVerified: true,
|
|
||||||
verifiedAt: business.domainVerifiedAt,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
if (!business.domainVerificationToken) throw new BadRequestException(BusinessMessage.DOMAIN_VERIFICATION_TOKEN_REQUIRED);
|
||||||
let isVerified = false;
|
|
||||||
|
|
||||||
isVerified = await this.checkDnsTxtRecord(business.domain, business.domainVerificationToken);
|
|
||||||
|
|
||||||
if (isVerified) {
|
|
||||||
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,
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
return {
|
|
||||||
message: BusinessMessage.DOMAIN_VERIFICATION_FAILED,
|
|
||||||
domain: business.domain,
|
|
||||||
isVerified: false,
|
|
||||||
instructions: this.getDomainSetupInstructions(business.domain, business.domainVerificationToken),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
} catch (error: unknown) {
|
|
||||||
this.logger.error(`Domain verification failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
||||||
throw new BadRequestException(BusinessMessage.DOMAIN_VERIFICATION_FAILED);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//************************************** */
|
|
||||||
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) {
|
if (business.isDomainVerified) {
|
||||||
const domainPointsToServer = await this.checkDomainPointsToServer(business.domain);
|
const domainPointsToServer = await this.checkDomainPointsToServer(business.domain);
|
||||||
@@ -185,23 +91,20 @@ export class DomainVerificationService {
|
|||||||
isVerified: true,
|
isVerified: true,
|
||||||
verifiedAt: business.domainVerifiedAt,
|
verifiedAt: business.domainVerifiedAt,
|
||||||
domainPointsToServer,
|
domainPointsToServer,
|
||||||
serverConnectionStatus: domainPointsToServer
|
connectionStatus: domainPointsToServer ? BusinessMessage.DOMAIN_CONNECTED : BusinessMessage.DOMAIN_NOT_CONNECTED,
|
||||||
? "Your domain is properly set up and pointing to our servers"
|
connectionInstructions: !domainPointsToServer ? this.getDomainConnectionInstructions(business.domain) : null,
|
||||||
: "Your domain is verified, but not properly pointing to our servers",
|
|
||||||
setupInstructions: !domainPointsToServer ? this.getDomainConnectionInstructions(business.domain) : null,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// First check domain ownership verification via TXT record
|
// Check domain ownership verification via TXT record
|
||||||
const isVerified = await this.checkDnsTxtRecord(business.domain, business.domainVerificationToken);
|
const isVerified = await this.checkDnsTxtRecord(business.domain, business.domainVerificationToken);
|
||||||
|
|
||||||
// Then check if domain points to our server (A or CNAME record)
|
// Check domain points to our server
|
||||||
const domainPointsToServer = await this.checkDomainPointsToServer(business.domain);
|
const domainPointsToServer = await this.checkDomainPointsToServer(business.domain);
|
||||||
|
|
||||||
// If TXT verification passes, mark the domain as verified regardless of A/CNAME
|
// Update verification status if verified
|
||||||
if (isVerified) {
|
if (isVerified) {
|
||||||
// Update domain verification status
|
|
||||||
business.isDomainVerified = true;
|
business.isDomainVerified = true;
|
||||||
business.domainVerifiedAt = dayjs().toDate();
|
business.domainVerifiedAt = dayjs().toDate();
|
||||||
await this.em.flush();
|
await this.em.flush();
|
||||||
@@ -212,21 +115,18 @@ export class DomainVerificationService {
|
|||||||
isVerified: true,
|
isVerified: true,
|
||||||
verifiedAt: business.domainVerifiedAt,
|
verifiedAt: business.domainVerifiedAt,
|
||||||
domainPointsToServer,
|
domainPointsToServer,
|
||||||
serverConnectionStatus: domainPointsToServer
|
connectionStatus: domainPointsToServer ? BusinessMessage.DOMAIN_CONNECTED : BusinessMessage.DOMAIN_NOT_CONNECTED,
|
||||||
? "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,
|
connectionInstructions: !domainPointsToServer ? this.getDomainConnectionInstructions(business.domain) : null,
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
|
// Verification failed
|
||||||
return {
|
return {
|
||||||
message: BusinessMessage.DOMAIN_VERIFICATION_FAILED,
|
message: BusinessMessage.DOMAIN_VERIFICATION_FAILED,
|
||||||
domain: business.domain,
|
domain: business.domain,
|
||||||
isVerified: false,
|
isVerified: false,
|
||||||
domainPointsToServer,
|
domainPointsToServer,
|
||||||
serverConnectionStatus: domainPointsToServer
|
connectionStatus: domainPointsToServer ? BusinessMessage.DOMAIN_CONNECTED : BusinessMessage.DOMAIN_NOT_CONNECTED,
|
||||||
? "Your domain is pointing to our servers, but ownership verification failed"
|
setupInstructions: this.getDomainSetupInstructions(business.domain, business.domainVerificationToken),
|
||||||
: "Both ownership verification and server connection failed",
|
|
||||||
setupInstructions: this.getDomainSetupInstructions(business.domain, business.domainVerificationToken || ""),
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
} catch (error: unknown) {
|
} catch (error: unknown) {
|
||||||
@@ -237,38 +137,11 @@ export class DomainVerificationService {
|
|||||||
domain: business.domain,
|
domain: business.domain,
|
||||||
isVerified: false,
|
isVerified: false,
|
||||||
error: error instanceof Error ? error.message : String(error),
|
error: error instanceof Error ? error.message : String(error),
|
||||||
setupInstructions: this.getDomainSetupInstructions(business.domain, business.domainVerificationToken || ""),
|
setupInstructions: this.getDomainSetupInstructions(business.domain, business.domainVerificationToken),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//************************************** */
|
|
||||||
async getDomainVerificationStatus(businessId: string) {
|
|
||||||
const business = await this.businessesService.getBusinessById(businessId);
|
|
||||||
|
|
||||||
if (!business.domain) {
|
|
||||||
return {
|
|
||||||
hasDomain: false,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if domain points to our server (A or CNAME record)
|
|
||||||
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,
|
|
||||||
connectionInstructions: !domainPointsToServer ? this.getDomainConnectionInstructions(business.domain) : null,
|
|
||||||
setupInstructions: !business.isDomainVerified ? this.getDomainSetupInstructions(business.domain, business.domainVerificationToken || "") : null,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
//*********************************** */
|
//*********************************** */
|
||||||
async getDomainStatus(businessId: string) {
|
async getDomainStatus(businessId: string) {
|
||||||
const business = await this.businessesService.getBusinessById(businessId);
|
const business = await this.businessesService.getBusinessById(businessId);
|
||||||
@@ -276,7 +149,7 @@ export class DomainVerificationService {
|
|||||||
if (!business.domain) {
|
if (!business.domain) {
|
||||||
return {
|
return {
|
||||||
hasDomain: false,
|
hasDomain: false,
|
||||||
message: "No domain set for this business",
|
message: BusinessMessage.DOMAIN_NOT_FOUND,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -288,8 +161,8 @@ export class DomainVerificationService {
|
|||||||
isVerified: business.isDomainVerified,
|
isVerified: business.isDomainVerified,
|
||||||
verifiedAt: business.domainVerifiedAt,
|
verifiedAt: business.domainVerifiedAt,
|
||||||
domainPointsToServer,
|
domainPointsToServer,
|
||||||
verificationStatus: business.isDomainVerified ? "Verified" : "Not Verified",
|
verificationStatus: business.isDomainVerified ? BusinessMessage.DOMAIN_VERIFICATION_SUCCESS : BusinessMessage.DOMAIN_VERIFICATION_FAILED,
|
||||||
connectionStatus: domainPointsToServer ? "Connected" : "Not Connected",
|
connectionStatus: domainPointsToServer ? BusinessMessage.DOMAIN_CONNECTED : BusinessMessage.DOMAIN_NOT_CONNECTED,
|
||||||
fullSetupComplete: business.isDomainVerified && domainPointsToServer,
|
fullSetupComplete: business.isDomainVerified && domainPointsToServer,
|
||||||
setupInstructions:
|
setupInstructions:
|
||||||
!business.isDomainVerified || !domainPointsToServer
|
!business.isDomainVerified || !domainPointsToServer
|
||||||
@@ -298,60 +171,54 @@ export class DomainVerificationService {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
//*********************************** */
|
||||||
* Generate a random verification token
|
//private methods
|
||||||
*/
|
|
||||||
private generateVerificationToken(): string {
|
private generateVerificationToken(): string {
|
||||||
return `dzone-verify-${crypto.randomBytes(16).toString("hex")}`;
|
return `dzone-verify-${crypto.randomBytes(16).toString("hex")}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
// Get domain connection instructions
|
||||||
* Get domain connection instructions
|
|
||||||
*/
|
|
||||||
private getDomainConnectionInstructions(_domain: string) {
|
private getDomainConnectionInstructions(_domain: string) {
|
||||||
// The domain parameter is used for contextual purposes in some implementations
|
|
||||||
// but not directly in this template
|
|
||||||
return {
|
return {
|
||||||
title: "Point your domain to our servers",
|
title: BusinessMessage.DOMAIN_CONNECTION_TITLE,
|
||||||
description: "Choose ONE of these options to connect your domain:",
|
description: BusinessMessage.DOMAIN_CONNECTION_DESCRIPTION,
|
||||||
options: [
|
options: [
|
||||||
{
|
{
|
||||||
title: "Option A: Using A Record (Direct IP)",
|
title: BusinessMessage.DOMAIN_CONNECTION_OPTION_A_TITLE,
|
||||||
steps: [
|
steps: [
|
||||||
"1. Add an A record in your DNS settings",
|
BusinessMessage.DOMAIN_CONNECTION_OPTION_A_STEP1,
|
||||||
"2. Set the host to @ or your subdomain (e.g., www)",
|
BusinessMessage.DOMAIN_CONNECTION_OPTION_A_STEP2,
|
||||||
"3. Set the value to our server IP: " + this.serverIP,
|
BusinessMessage.DOMAIN_CONNECTION_OPTION_A_STEP3 + this.serverIP,
|
||||||
"4. Set TTL to 3600 (1 hour) or the lowest available",
|
BusinessMessage.DOMAIN_CONNECTION_OPTION_A_STEP4,
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Option B: Using CNAME Record (Recommended)",
|
title: BusinessMessage.DOMAIN_CONNECTION_OPTION_B_TITLE,
|
||||||
steps: [
|
steps: [
|
||||||
"1. Add a CNAME record in your DNS settings",
|
BusinessMessage.DOMAIN_CONNECTION_OPTION_B_STEP1,
|
||||||
"2. Set the host to @ or your subdomain (e.g., www)",
|
BusinessMessage.DOMAIN_CONNECTION_OPTION_B_STEP2,
|
||||||
"3. Set the value to: " + this.serverDomain,
|
BusinessMessage.DOMAIN_CONNECTION_OPTION_B_STEP3 + this.serverDomain,
|
||||||
"4. Set TTL to 3600 (1 hour) or the lowest available",
|
BusinessMessage.DOMAIN_CONNECTION_OPTION_B_STEP4,
|
||||||
"Note: Some DNS providers don't allow CNAME for root domains (@)",
|
BusinessMessage.DOMAIN_CONNECTION_OPTION_B_NOTE,
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
troubleshooting: [
|
troubleshooting: [
|
||||||
"- Make sure you've added the records to the correct domain",
|
BusinessMessage.DOMAIN_CONNECTION_TROUBLESHOOTING_1,
|
||||||
"- Some DNS providers require the @ symbol for root domains",
|
BusinessMessage.DOMAIN_CONNECTION_TROUBLESHOOTING_2,
|
||||||
"- Check that you've copied the values exactly as shown",
|
BusinessMessage.DOMAIN_CONNECTION_TROUBLESHOOTING_3,
|
||||||
"- DNS changes can take time to propagate (typically 15 minutes to 48 hours)",
|
BusinessMessage.DOMAIN_CONNECTION_TROUBLESHOOTING_4,
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
// Get comprehensive domain setup instructions
|
||||||
* Get comprehensive domain setup instructions
|
|
||||||
*/
|
|
||||||
private getDomainSetupInstructions(domain: string, token: string) {
|
private getDomainSetupInstructions(domain: string, token: string) {
|
||||||
return {
|
return {
|
||||||
verificationInstructions: {
|
verificationInstructions: {
|
||||||
title: "Step 1: Verify domain ownership",
|
title: BusinessMessage.DOMAIN_SETUP_VERIFICATION_TITLE,
|
||||||
description: "Add this TXT record to prove you own the domain:",
|
description: BusinessMessage.DOMAIN_SETUP_VERIFICATION_DESC,
|
||||||
host: "@", // Root domain
|
host: "@", // Root domain
|
||||||
type: "TXT", // Record type
|
type: "TXT", // Record type
|
||||||
value: token, // Token value
|
value: token, // Token value
|
||||||
@@ -360,20 +227,18 @@ export class DomainVerificationService {
|
|||||||
connectionInstructions: this.getDomainConnectionInstructions(domain),
|
connectionInstructions: this.getDomainConnectionInstructions(domain),
|
||||||
general: {
|
general: {
|
||||||
verificationEndpoint: `/business/domain/verify/check-dns`,
|
verificationEndpoint: `/business/domain/verify/check-dns`,
|
||||||
propagationNote: "DNS changes can take 15 minutes to 48 hours to propagate worldwide",
|
propagationNote: BusinessMessage.DOMAIN_SETUP_PROPAGATION_NOTE,
|
||||||
troubleshooting: [
|
troubleshooting: [
|
||||||
"- Make sure you've added the records to the correct domain",
|
BusinessMessage.DOMAIN_SETUP_TROUBLESHOOTING_1,
|
||||||
"- Some DNS providers require the @ symbol for root domains",
|
BusinessMessage.DOMAIN_SETUP_TROUBLESHOOTING_2,
|
||||||
"- Check that you've copied the values exactly as shown",
|
BusinessMessage.DOMAIN_SETUP_TROUBLESHOOTING_3,
|
||||||
"- Wait at least 15 minutes before checking verification status",
|
BusinessMessage.DOMAIN_SETUP_TROUBLESHOOTING_4,
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
// Verify domain using DNS TXT record
|
||||||
* Verify domain using DNS TXT record
|
|
||||||
*/
|
|
||||||
private async checkDnsTxtRecord(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}`);
|
this.logger.log(`Checking TXT records for domain: ${domain}`);
|
||||||
@@ -392,9 +257,7 @@ export class DomainVerificationService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
// Check if domain points to our server via A or CNAME record
|
||||||
* Check if domain points to our server via A or CNAME record
|
|
||||||
*/
|
|
||||||
private async checkDomainPointsToServer(domain: string): Promise<boolean> {
|
private async checkDomainPointsToServer(domain: string): Promise<boolean> {
|
||||||
try {
|
try {
|
||||||
// Try CNAME first
|
// Try CNAME first
|
||||||
|
|||||||
Reference in New Issue
Block a user