update: fix

This commit is contained in:
mahyargdz
2025-05-23 21:39:34 +03:30
parent f03cf82954
commit b224dd4f68
3 changed files with 112 additions and 230 deletions
+5
View File
@@ -635,6 +635,9 @@ export const enum BusinessMessage {
DOMAIN_CONNECTION_TROUBLESHOOTING_2 = "- برخی از ارائه‌دهندگان DNS برای دامنه‌های اصلی به نماد @ نیاز دارند",
DOMAIN_CONNECTION_TROUBLESHOOTING_3 = "- بررسی کنید که مقادیر را دقیقاً همانطور که نشان داده شده کپی کرده‌اید",
DOMAIN_CONNECTION_TROUBLESHOOTING_4 = "- تغییرات DNS می‌تواند برای انتشار زمان ببرد (معمولاً ۱۵ دقیقه تا ۴۸ ساعت)",
DOMAIN_CONNECTION_TROUBLESHOOTING_5 = "- اگر از Cloudflare استفاده می‌کنید، حالت ابری (پروکسی) را خاموش کنید و فقط از DNS استفاده کنید",
DOMAIN_CONNECTION_TROUBLESHOOTING_6 = "- برای دامنه اصلی (@)، برخی ارائه دهندگان DNS به جای CNAME از رکورد ANAME یا ALIAS استفاده می‌کنند",
DOMAIN_CONNECTION_TROUBLESHOOTING_7 = "- اگر نمی‌توانید CNAME برای دامنه اصلی تنظیم کنید، از رکورد A با IP سرور استفاده کنید",
// Domain Setup Instructions in Farsi
DOMAIN_SETUP_VERIFICATION_TITLE = "مرحله ۱: تایید مالکیت دامنه",
@@ -644,4 +647,6 @@ export const enum BusinessMessage {
DOMAIN_SETUP_TROUBLESHOOTING_2 = "- بعضی از ارائه‌دهندگان DNS برای دامنه‌های اصلی به نماد @ نیاز دارند",
DOMAIN_SETUP_TROUBLESHOOTING_3 = "- اطمینان حاصل کنید که مقادیر را دقیقاً همانطور که نشان داده شده کپی کرده‌اید",
DOMAIN_SETUP_TROUBLESHOOTING_4 = "- حداقل ۱۵ دقیقه قبل از بررسی وضعیت تأیید صبر کنید",
DOMAIN_SETUP_TROUBLESHOOTING_5 = "- برخی از ارائه‌دهندگان DNS ممکن است نیاز به تنظیمات خاص برای رکوردهای TXT داشته باشند",
DOMAIN_SETUP_TROUBLESHOOTING_6 = "- اگر از Cloudflare استفاده می‌کنید، مطمئن شوید که رکوردهای TXT را بدون پروکسی تنظیم کرده‌اید",
}
@@ -62,124 +62,4 @@ export class BusinessesController {
getDomainStatus(@BusinessDec() business: Business) {
return this.domainVerificationService.getDomainStatus(business.id);
}
@Post("domain/update-dns-records")
@ApiOperation({ summary: "Get detailed instructions for updating DNS records" })
@AuthGuards()
getDetailedDnsInstructions(@BusinessDec() business: Business) {
// This endpoint provides detailed DNS setup instructions for various DNS providers
if (!business.domain) {
return {
success: false,
message: "No domain configured for this business",
};
}
return {
success: true,
domain: business.domain,
instructions: {
title: "راهنمای تنظیم رکوردهای DNS برای دامنه شما",
description: "برای اتصال دامنه خود به سرویس دی‌زون، لطفا یکی از روش‌های زیر را انتخاب کنید:",
methods: [
{
title: "روش A: استفاده از رکورد A (آدرس IP)",
description: "این روش برای همه ارائه‌دهندگان DNS کار می‌کند و برای دامنه اصلی مناسب است.",
steps: [
"۱. وارد پنل مدیریت DNS خود شوید",
"۲. یک رکورد A جدید ایجاد کنید",
"۳. در فیلد Host یا Name، @ را وارد کنید (برای دامنه اصلی)",
`۴. در فیلد Value یا Points to، آدرس IP سرور ما را وارد کنید: 194.5.192.20`,
"۵. TTL را روی 3600 (یک ساعت) یا کمترین مقدار ممکن تنظیم کنید",
"۶. تغییرات را ذخیره کنید",
],
},
{
title: "روش B: استفاده از رکورد CNAME (نام مستعار)",
description: "این روش برای زیردامنه‌ها مانند www توصیه می‌شود.",
steps: [
"۱. وارد پنل مدیریت DNS خود شوید",
"۲. یک رکورد CNAME جدید ایجاد کنید",
"۳. در فیلد Host یا Name، www را وارد کنید (یا زیردامنه دلخواه)",
`۴. در فیلد Value یا Points to، آدرس سرور ما را وارد کنید: app.dzone.danakcorp.com`,
"۵. TTL را روی 3600 (یک ساعت) یا کمترین مقدار ممکن تنظیم کنید",
"۶. تغییرات را ذخیره کنید",
],
note: "توجه: برخی ارائه‌دهندگان DNS اجازه استفاده از CNAME برای دامنه اصلی (@) را نمی‌دهند. در این صورت از روش A استفاده کنید.",
},
],
providerSpecific: [
{
provider: "Cloudflare",
instructions: [
"۱. حالت ابری (پروکسی) را خاموش کنید و فقط از DNS استفاده کنید",
"۲. برای دامنه اصلی (@) از رکورد A استفاده کنید",
"۳. برای زیردامنه www از رکورد CNAME استفاده کنید",
],
},
{
provider: "cPanel",
instructions: [
"۱. به بخش Zone Editor بروید",
"۲. روی دامنه خود کلیک کنید",
"۳. برای دامنه اصلی، رکورد A با مقدار 194.5.192.20 اضافه کنید",
"۴. برای www، رکورد CNAME با مقدار app.dzone.danakcorp.com اضافه کنید",
],
},
{
provider: "سایر ارائه‌دهندگان",
instructions: [
"اگر نمی‌توانید CNAME برای دامنه اصلی تنظیم کنید، از رکورد A استفاده کنید",
"برخی ارائه‌دهندگان از رکوردهای ANAME یا ALIAS برای دامنه اصلی پشتیبانی می‌کنند",
"اگر در تنظیم رکوردها مشکل دارید، با پشتیبانی ارائه‌دهنده DNS خود تماس بگیرید",
],
},
],
troubleshooting: [
"- تغییرات DNS ممکن است بین ۱۵ دقیقه تا ۴۸ ساعت طول بکشد تا در سراسر اینترنت منتشر شود",
"- اگر پس از ۴۸ ساعت هنوز مشکل دارید، مطمئن شوید که رکوردها را درست تنظیم کرده‌اید",
"- برای بررسی وضعیت رکوردهای DNS خود می‌توانید از ابزارهایی مانند dnschecker.org استفاده کنید",
"- اگر همچنان مشکل دارید، با پشتیبانی ما تماس بگیرید",
],
},
};
}
@Post("domain/configure-caprover")
@ApiOperation({ summary: "Manually configure domain in Caprover (Admin use only)" })
@AuthGuards()
async manualCaproverConfig(@BusinessDec() business: Business) {
if (!business.domain) {
return {
success: false,
message: "No domain configured for this business",
};
}
if (!business.isDomainVerified) {
return {
success: false,
message: "Domain must be verified before configuring in Caprover",
domain: business.domain,
};
}
try {
const result = await this.domainVerificationService.configureCaproverDomain(business.domain);
return {
success: result,
message: result
? `Domain ${business.domain} successfully configured in Caprover`
: `Failed to configure domain ${business.domain} in Caprover`,
domain: business.domain,
};
} catch (error: unknown) {
return {
success: false,
message: `Error configuring domain in Caprover: ${error instanceof Error ? error.message : String(error)}`,
domain: business.domain,
};
}
}
}
@@ -49,66 +49,6 @@ export class DomainVerificationService {
}
//--------------------------------
private async getCaproverToken(): Promise<string> {
try {
// Check if we already have a valid token
if (this.caproverAuthToken && this.tokenExpiry && new Date() < new Date(this.tokenExpiry.getTime() - this.TOKEN_REFRESH_BEFORE_EXPIRY_MS)) {
this.logger.debug("Using existing Caprover token");
return this.caproverAuthToken;
}
this.logger.log("Getting new Caprover authentication token");
const loginUrl = `${this.caproverApiUrl}/login`;
const response = await firstValueFrom(
this.httpService
.post(
loginUrl,
{
password: this.caproverPassword,
otpToken: "",
// email: this.caproverEmail,
},
{
headers: {
"x-namespace": "captain",
"Content-Type": "application/json",
},
},
)
.pipe(
catchError((err: AxiosError) => {
this.logger.error("Error in Caprover authentication", err);
throw new InternalServerErrorException("Failed to authenticate with Caprover");
}),
),
);
if (response.status === 200 && response.data?.data?.token) {
// Set the token and expiry (default to 24 hours if not provided by Caprover)
this.caproverAuthToken = response.data.data.token;
// Set expiry to 23 hours from now to be safe (Caprover tokens typically last 24 hours)
this.tokenExpiry = new Date(Date.now() + 1 * 60 * 60 * 1000);
this.logger.log("Successfully obtained new Caprover token");
return this.caproverAuthToken;
} else {
this.logger.error(`Failed to get Caprover token: ${JSON.stringify(response.data)}`);
throw new InternalServerErrorException("Failed to get Caprover authentication token");
}
} catch (error: unknown) {
this.logger.error(`Error getting Caprover token: ${error instanceof Error ? error.message : String(error)}`);
throw new InternalServerErrorException("Failed to authenticate with Caprover");
}
}
/**
* 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 setDomain(businessId: string, domainDto: UpdateBusinessDomainDto) {
const business = await this.businessesService.getBusinessById(businessId);
@@ -142,12 +82,7 @@ export class DomainVerificationService {
};
}
/**
* 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 verifyDomain(businessId: string) {
const business = await this.businessesService.getBusinessById(businessId);
@@ -239,7 +174,8 @@ export class DomainVerificationService {
}
}
//*********************************** */
//--------------------------------
async getDomainStatus(businessId: string) {
const business = await this.businessesService.getBusinessById(businessId);
@@ -252,6 +188,16 @@ export class DomainVerificationService {
const domainPointsToServer = await this.checkDomainPointsToServer(business.domain);
let caproverConfigured = false;
if (domainPointsToServer) {
try {
caproverConfigured = await this.configureCaproverDomain(business.domain);
this.logger.log(`Domain ${business.domain} configuration in Caprover: ${caproverConfigured ? "successful" : "failed"}`);
} catch (error: unknown) {
this.logger.error(`Failed to configure domain in Caprover: ${error instanceof Error ? error.message : String(error)}`);
}
}
return {
hasDomain: true,
domain: business.domain,
@@ -268,11 +214,58 @@ export class DomainVerificationService {
};
}
/**
* Configure a domain in Caprover
* This method sends a request to Caprover API to add a custom domain to an app
*/
async configureCaproverDomain(domain: string): Promise<boolean> {
//private methods
//--------------------------------
private async enableCaproverHttps(domain: string): Promise<boolean> {
try {
// Get a fresh token before making the request
const token = await this.getCaproverToken();
this.logger.log(`Enabling HTTPS for domain ${domain} in Caprover`);
const url = `${this.caproverApiUrl}/user/apps/appDefinitions/enablecustomdomainssl`;
const response = await firstValueFrom(
this.httpService
.post(
url,
{
appName: this.caproverAppName,
customDomain: domain,
},
{
headers: {
"X-Captain-Auth": token,
"x-namespace": "captain",
"Content-Type": "application/json",
},
},
)
.pipe(
catchError((err: AxiosError) => {
this.logger.error("error in enabling https in caprover", err);
throw new InternalServerErrorException("error in enabling https in caprover");
}),
),
);
if (response.status === 200 && response.data?.status === 100) {
this.logger.log(`Successfully enabled HTTPS for domain ${domain} in Caprover`);
return true;
} else {
this.logger.error(`Failed to enable HTTPS in Caprover: ${JSON.stringify(response.data)}`);
return false;
}
} catch (error: unknown) {
this.logger.error(`Error enabling HTTPS in Caprover: ${error instanceof Error ? error.message : String(error)}`);
return false;
}
}
//--------------------------------
private async configureCaproverDomain(domain: string): Promise<boolean> {
try {
// Get a fresh token before making the request
const token = await this.getCaproverToken();
@@ -321,30 +314,30 @@ export class DomainVerificationService {
return false;
}
}
/**
* Enable HTTPS for a domain in Caprover
*/
private async enableCaproverHttps(domain: string): Promise<boolean> {
//--------------------------------
private async getCaproverToken(): Promise<string> {
try {
// Get a fresh token before making the request
const token = await this.getCaproverToken();
// Check if we already have a valid token
if (this.caproverAuthToken && this.tokenExpiry && new Date() < new Date(this.tokenExpiry.getTime() - this.TOKEN_REFRESH_BEFORE_EXPIRY_MS)) {
this.logger.debug("Using existing Caprover token");
return this.caproverAuthToken;
}
this.logger.log(`Enabling HTTPS for domain ${domain} in Caprover`);
this.logger.log("Getting new Caprover authentication token");
const url = `${this.caproverApiUrl}/user/apps/appDefinitions/enablecustomdomainssl`;
const loginUrl = `${this.caproverApiUrl}/login`;
const response = await firstValueFrom(
this.httpService
.post(
url,
loginUrl,
{
appName: this.caproverAppName,
customDomain: domain,
password: this.caproverPassword,
otpToken: "",
// email: this.caproverEmail,
},
{
headers: {
"X-Captain-Auth": token,
"x-namespace": "captain",
"Content-Type": "application/json",
},
@@ -352,33 +345,38 @@ export class DomainVerificationService {
)
.pipe(
catchError((err: AxiosError) => {
this.logger.error("error in enabling https in caprover", err);
throw new InternalServerErrorException("error in enabling https in caprover");
this.logger.error("Error in Caprover authentication", err);
throw new InternalServerErrorException("Failed to authenticate with Caprover");
}),
),
);
if (response.status === 200 && response.data?.status === 100) {
this.logger.log(`Successfully enabled HTTPS for domain ${domain} in Caprover`);
return true;
if (response.status === 200 && response.data?.data?.token) {
// Set the token and expiry (default to 24 hours if not provided by Caprover)
this.caproverAuthToken = response.data.data.token;
// Set expiry to 23 hours from now to be safe (Caprover tokens typically last 24 hours)
this.tokenExpiry = new Date(Date.now() + 1 * 60 * 60 * 1000);
this.logger.log("Successfully obtained new Caprover token");
return this.caproverAuthToken;
} else {
this.logger.error(`Failed to enable HTTPS in Caprover: ${JSON.stringify(response.data)}`);
return false;
this.logger.error(`Failed to get Caprover token: ${JSON.stringify(response.data)}`);
throw new InternalServerErrorException("Failed to get Caprover authentication token");
}
} catch (error: unknown) {
this.logger.error(`Error enabling HTTPS in Caprover: ${error instanceof Error ? error.message : String(error)}`);
return false;
this.logger.error(`Error getting Caprover token: ${error instanceof Error ? error.message : String(error)}`);
throw new InternalServerErrorException("Failed to authenticate with Caprover");
}
}
//*********************************** */
//private methods
//--------------------------------
private generateVerificationToken(): string {
return `dzone-verify-${crypto.randomBytes(16).toString("hex")}`;
}
// Get domain connection instructions with enhanced guidance for different DNS providers
//--------------------------------
private getDomainConnectionInstructions(domain: string) {
return {
title: BusinessMessage.DOMAIN_CONNECTION_TITLE,
@@ -409,16 +407,16 @@ export class DomainVerificationService {
BusinessMessage.DOMAIN_CONNECTION_TROUBLESHOOTING_2,
BusinessMessage.DOMAIN_CONNECTION_TROUBLESHOOTING_3,
BusinessMessage.DOMAIN_CONNECTION_TROUBLESHOOTING_4,
// Add new troubleshooting instructions for common DNS providers
"- اگر از Cloudflare استفاده می‌کنید، حالت ابری (پروکسی) را خاموش کنید و فقط از DNS استفاده کنید",
"- برای دامنه اصلی (@)، برخی ارائه دهندگان DNS به جای CNAME از رکورد ANAME یا ALIAS استفاده می‌کنند",
"- اگر نمی‌توانید CNAME برای دامنه اصلی تنظیم کنید، از رکورد A با IP سرور استفاده کنید",
BusinessMessage.DOMAIN_CONNECTION_TROUBLESHOOTING_5,
BusinessMessage.DOMAIN_CONNECTION_TROUBLESHOOTING_6,
BusinessMessage.DOMAIN_CONNECTION_TROUBLESHOOTING_7,
],
domain: domain,
};
}
// Get comprehensive domain setup instructions
//--------------------------------
private getDomainSetupInstructions(domain: string, token: string) {
return {
verificationInstructions: {
@@ -431,22 +429,21 @@ export class DomainVerificationService {
},
connectionInstructions: this.getDomainConnectionInstructions(domain),
general: {
verificationEndpoint: `/business/domain/verify/check-dns`,
propagationNote: BusinessMessage.DOMAIN_SETUP_PROPAGATION_NOTE,
troubleshooting: [
BusinessMessage.DOMAIN_SETUP_TROUBLESHOOTING_1,
BusinessMessage.DOMAIN_SETUP_TROUBLESHOOTING_2,
BusinessMessage.DOMAIN_SETUP_TROUBLESHOOTING_3,
BusinessMessage.DOMAIN_SETUP_TROUBLESHOOTING_4,
// Add new troubleshooting instructions
"- برخی از ارائه‌دهندگان DNS ممکن است نیاز به تنظیمات خاص برای رکوردهای TXT داشته باشند",
"- اگر از Cloudflare استفاده می‌کنید، مطمئن شوید که رکوردهای TXT را بدون پروکسی تنظیم کرده‌اید",
BusinessMessage.DOMAIN_SETUP_TROUBLESHOOTING_5,
BusinessMessage.DOMAIN_SETUP_TROUBLESHOOTING_6,
],
},
};
}
// Verify domain using DNS TXT record
//--------------------------------
private async checkDnsTxtRecord(domain: string, token: string): Promise<boolean> {
try {
this.logger.log(`Checking TXT records for domain: ${domain}`);
@@ -465,8 +462,8 @@ export class DomainVerificationService {
}
}
// Check if domain points to our server via A or CNAME record
// Enhanced to handle both root domain and www subdomain
//--------------------------------
private async checkDomainPointsToServer(domain: string): Promise<boolean> {
try {
this.logger.log(`Checking if domain ${domain} points to our servers`);