update: domain verfication service
This commit is contained in:
@@ -0,0 +1,76 @@
|
||||
# Caprover Integration Setup
|
||||
|
||||
This document explains how to set up the integration between the DZone API and Caprover for automatic domain configuration.
|
||||
|
||||
## Overview
|
||||
|
||||
When a user successfully verifies their domain, the system will automatically configure that domain in Caprover, enabling the domain to be properly routed to the frontend application.
|
||||
|
||||
## Configuration Steps
|
||||
|
||||
### 1. Set Environment Variables
|
||||
|
||||
Add the following environment variables to your `.env` file:
|
||||
|
||||
```
|
||||
CAPROVER_API_URL=https://captain.dev.danakcorp.com/api/v2
|
||||
CAPROVER_EMAIL=your_caprover_email@example.com
|
||||
CAPROVER_PASSWORD=your_caprover_password
|
||||
CAPROVER_APP_NAME=dzone-front
|
||||
```
|
||||
|
||||
The system will automatically handle authentication with Caprover using these credentials and refresh tokens as needed.
|
||||
|
||||
### 2. Verify Configuration
|
||||
|
||||
1. Restart the API service to apply the new environment variables
|
||||
2. Try verifying a domain through the API
|
||||
3. Check the logs to ensure the Caprover API calls are successful
|
||||
|
||||
## How Token Refresh Works
|
||||
|
||||
The system automatically handles Caprover authentication:
|
||||
|
||||
1. When a domain configuration request is made, the system checks if it has a valid token
|
||||
2. If no token exists or the token is about to expire (within 5 minutes), it requests a new one
|
||||
3. The system uses the email and password to authenticate with Caprover
|
||||
4. The new token is stored and used for subsequent requests
|
||||
5. Tokens are typically valid for 24 hours, but the system refreshes them after 23 hours to be safe
|
||||
|
||||
This approach ensures that the integration continues to work even if tokens expire.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Authentication Issues
|
||||
|
||||
If you see authentication errors in the logs:
|
||||
|
||||
1. Verify that the `CAPROVER_EMAIL` and `CAPROVER_PASSWORD` are correct
|
||||
2. Check that the user has sufficient permissions in Caprover
|
||||
3. Ensure the Caprover instance is accessible at the URL specified in `CAPROVER_API_URL`
|
||||
|
||||
### API Connection Issues
|
||||
|
||||
If the API cannot connect to Caprover, check:
|
||||
|
||||
1. That the Caprover instance is running and accessible
|
||||
2. That the `CAPROVER_API_URL` is correct
|
||||
3. That there are no network issues between the API server and Caprover
|
||||
|
||||
### Domain Configuration Issues
|
||||
|
||||
If domains are not being properly configured in Caprover:
|
||||
|
||||
1. Check that the domain has proper DNS records pointing to your server
|
||||
2. Verify that the app name specified in `CAPROVER_APP_NAME` exists in Caprover
|
||||
3. Ensure the Caprover user associated with the credentials has sufficient permissions
|
||||
|
||||
## Manual Configuration
|
||||
|
||||
If automatic configuration fails, you can manually configure domains using the admin endpoint:
|
||||
|
||||
```
|
||||
POST /business/domain/configure-caprover
|
||||
```
|
||||
|
||||
This endpoint requires authentication and a business with a verified domain.
|
||||
@@ -62,4 +62,124 @@ 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,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { MikroOrmModule } from "@mikro-orm/nestjs";
|
||||
import { HttpModule } from "@nestjs/axios";
|
||||
import { BullModule } from "@nestjs/bullmq";
|
||||
import { Module } from "@nestjs/common";
|
||||
|
||||
@@ -16,6 +17,10 @@ import { DomainVerificationService } from "./services/domain-verification.servic
|
||||
name: SUBSCRIPTIONS.PROVISIONING_QUEUE_NAME,
|
||||
prefix: SUBSCRIPTIONS.PROVISIONING_QUEUE_PREFIX,
|
||||
}),
|
||||
HttpModule.register({
|
||||
timeout: 10000,
|
||||
maxRedirects: 5,
|
||||
}),
|
||||
],
|
||||
providers: [BusinessesService, BusinessProvisioningProcessor, DomainVerificationService],
|
||||
exports: [BusinessesService],
|
||||
|
||||
@@ -3,8 +3,12 @@ import dns from "node:dns";
|
||||
import { promisify } from "node:util";
|
||||
|
||||
import { EntityManager } from "@mikro-orm/postgresql";
|
||||
import { BadRequestException, Injectable, Logger } from "@nestjs/common";
|
||||
import { HttpService } from "@nestjs/axios";
|
||||
import { BadRequestException, Injectable, InternalServerErrorException, Logger } from "@nestjs/common";
|
||||
import { ConfigService } from "@nestjs/config";
|
||||
import { AxiosError } from "axios";
|
||||
import dayjs from "dayjs";
|
||||
import { catchError, firstValueFrom } from "rxjs";
|
||||
|
||||
import { BusinessesService } from "./businesses.service";
|
||||
import { BusinessMessage } from "../../../common/enums/message.enum";
|
||||
@@ -18,12 +22,87 @@ export class DomainVerificationService {
|
||||
private readonly resolveCname = promisify(dns.resolveCname);
|
||||
|
||||
private readonly serverIP = "194.5.192.20";
|
||||
private readonly serverDomain = "app.dzone.danakcorp.com";
|
||||
private readonly serverDomain = "dzone.danakcorp.com";
|
||||
|
||||
// Caprover configuration
|
||||
private readonly caproverApiUrl: string;
|
||||
private readonly caproverAppName: string;
|
||||
|
||||
// Caprover authentication
|
||||
// private readonly caproverEmail: string;
|
||||
private readonly caproverPassword: string;
|
||||
private caproverAuthToken: string;
|
||||
private tokenExpiry: Date | null = null;
|
||||
private readonly TOKEN_REFRESH_BEFORE_EXPIRY_MS = 5 * 60 * 1000; // 5 minutes
|
||||
|
||||
constructor(
|
||||
private readonly businessesService: BusinessesService,
|
||||
private readonly em: EntityManager,
|
||||
) {}
|
||||
private readonly httpService: HttpService,
|
||||
private readonly configService: ConfigService,
|
||||
) {
|
||||
this.caproverApiUrl = this.configService.getOrThrow<string>("CAPROVER_API_URL");
|
||||
this.caproverAppName = this.configService.getOrThrow<string>("CAPROVER_APP_NAME");
|
||||
// this.caproverEmail = this.configService.getOrThrow<string>("CAPROVER_EMAIL");
|
||||
this.caproverPassword = this.configService.getOrThrow<string>("CAPROVER_PASSWORD");
|
||||
this.caproverAuthToken = this.configService.get<string>("CAPROVER_AUTH_TOKEN", "");
|
||||
}
|
||||
|
||||
//--------------------------------
|
||||
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
|
||||
@@ -84,6 +163,13 @@ export class DomainVerificationService {
|
||||
|
||||
if (business.isDomainVerified) {
|
||||
const domainPointsToServer = await this.checkDomainPointsToServer(business.domain);
|
||||
try {
|
||||
let caproverConfigured = false;
|
||||
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 {
|
||||
message: BusinessMessage.DOMAIN_ALREADY_VERIFIED,
|
||||
@@ -109,6 +195,17 @@ export class DomainVerificationService {
|
||||
business.domainVerifiedAt = dayjs().toDate();
|
||||
await this.em.flush();
|
||||
|
||||
// If domain is verified, try to configure it in Caprover
|
||||
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 {
|
||||
message: BusinessMessage.DOMAIN_VERIFICATION_SUCCESS,
|
||||
domain: business.domain,
|
||||
@@ -171,6 +268,109 @@ 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> {
|
||||
try {
|
||||
// Get a fresh token before making the request
|
||||
const token = await this.getCaproverToken();
|
||||
|
||||
this.logger.log(`Configuring domain ${domain} in Caprover for app ${this.caproverAppName}`);
|
||||
|
||||
const url = `${this.caproverApiUrl}/user/apps/appDefinitions/customdomain`;
|
||||
|
||||
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 configuring domain in caprover", err);
|
||||
throw new InternalServerErrorException("error in configuring domain in caprover");
|
||||
}),
|
||||
),
|
||||
);
|
||||
|
||||
if (response.status === 200 && response.data?.status === 100) {
|
||||
this.logger.log(`Successfully configured domain ${domain} in Caprover`);
|
||||
|
||||
// Enable HTTPS for the domain
|
||||
await this.enableCaproverHttps(domain);
|
||||
|
||||
return true;
|
||||
} else {
|
||||
this.logger.error(`Failed to configure domain in Caprover: ${JSON.stringify(response.data)}`);
|
||||
return false;
|
||||
}
|
||||
} catch (error) {
|
||||
this.logger.error(`Error configuring domain in Caprover: ${error instanceof Error ? error.message : String(error)}`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable HTTPS for a domain in Caprover
|
||||
*/
|
||||
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 methods
|
||||
|
||||
@@ -178,8 +378,8 @@ export class DomainVerificationService {
|
||||
return `dzone-verify-${crypto.randomBytes(16).toString("hex")}`;
|
||||
}
|
||||
|
||||
// Get domain connection instructions
|
||||
private getDomainConnectionInstructions(_domain: string) {
|
||||
// Get domain connection instructions with enhanced guidance for different DNS providers
|
||||
private getDomainConnectionInstructions(domain: string) {
|
||||
return {
|
||||
title: BusinessMessage.DOMAIN_CONNECTION_TITLE,
|
||||
description: BusinessMessage.DOMAIN_CONNECTION_DESCRIPTION,
|
||||
@@ -209,7 +409,12 @@ 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 سرور استفاده کنید",
|
||||
],
|
||||
domain: domain,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -233,6 +438,9 @@ export class DomainVerificationService {
|
||||
BusinessMessage.DOMAIN_SETUP_TROUBLESHOOTING_2,
|
||||
BusinessMessage.DOMAIN_SETUP_TROUBLESHOOTING_3,
|
||||
BusinessMessage.DOMAIN_SETUP_TROUBLESHOOTING_4,
|
||||
// Add new troubleshooting instructions
|
||||
"- برخی از ارائهدهندگان DNS ممکن است نیاز به تنظیمات خاص برای رکوردهای TXT داشته باشند",
|
||||
"- اگر از Cloudflare استفاده میکنید، مطمئن شوید که رکوردهای TXT را بدون پروکسی تنظیم کردهاید",
|
||||
],
|
||||
},
|
||||
};
|
||||
@@ -258,37 +466,75 @@ 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`);
|
||||
|
||||
// Try CNAME first
|
||||
try {
|
||||
const cnameRecords = await this.resolveCname(domain);
|
||||
|
||||
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)) {
|
||||
this.logger.log(`Domain ${domain} has valid CNAME record pointing to our server`);
|
||||
return true;
|
||||
}
|
||||
} catch (_error) {
|
||||
this.logger.debug(`No CNAME records found for ${domain}, trying A records`);
|
||||
} catch (error: unknown) {
|
||||
this.logger.debug(`No CNAME records found for ${domain}: ${error instanceof Error ? error.message : String(error)}`);
|
||||
}
|
||||
|
||||
// Try www subdomain CNAME if root domain check failed
|
||||
if (!domain.startsWith("www.")) {
|
||||
try {
|
||||
const wwwDomain = `www.${domain}`;
|
||||
const wwwCnameRecords = await this.resolveCname(wwwDomain);
|
||||
this.logger.log(`Found CNAME records for ${wwwDomain}: ${JSON.stringify(wwwCnameRecords)}`);
|
||||
|
||||
// Check if www subdomain CNAME points to our server domain
|
||||
if (wwwCnameRecords.some((record) => record === this.serverDomain)) {
|
||||
this.logger.log(`WWW subdomain ${wwwDomain} has valid CNAME record pointing to our server`);
|
||||
return true;
|
||||
}
|
||||
} catch (error: unknown) {
|
||||
this.logger.debug(`No CNAME records found for www.${domain}: ${error instanceof Error ? error.message : String(error)}`);
|
||||
}
|
||||
}
|
||||
|
||||
// Try A record
|
||||
try {
|
||||
const aRecords = await this.resolveA(domain);
|
||||
|
||||
this.logger.log(`Found A records for ${domain}: ${JSON.stringify(aRecords)}`);
|
||||
|
||||
// Check if any A record points to our server IP
|
||||
if (aRecords.some((record) => record === this.serverIP)) {
|
||||
this.logger.log(`Domain ${domain} has valid A record pointing to our server`);
|
||||
return true;
|
||||
}
|
||||
} catch (_error) {
|
||||
this.logger.debug(`No A records found for ${domain}`);
|
||||
} catch (error: unknown) {
|
||||
this.logger.debug(`No A records found for ${domain}: ${error instanceof Error ? error.message : String(error)}`);
|
||||
}
|
||||
|
||||
// Try www subdomain A record if root domain check failed
|
||||
if (!domain.startsWith("www.")) {
|
||||
try {
|
||||
const wwwDomain = `www.${domain}`;
|
||||
const wwwARecords = await this.resolveA(wwwDomain);
|
||||
this.logger.log(`Found A records for ${wwwDomain}: ${JSON.stringify(wwwARecords)}`);
|
||||
|
||||
// Check if www subdomain A record points to our server IP
|
||||
if (wwwARecords.some((record) => record === this.serverIP)) {
|
||||
this.logger.log(`WWW subdomain ${wwwDomain} has valid A record pointing to our server`);
|
||||
return true;
|
||||
}
|
||||
} catch (error: unknown) {
|
||||
this.logger.debug(`No A records found for www.${domain}: ${error instanceof Error ? error.message : String(error)}`);
|
||||
}
|
||||
}
|
||||
|
||||
// If we get here, neither CNAME nor A records point to our server
|
||||
this.logger.warn(`Domain ${domain} does not point to our servers`);
|
||||
return false;
|
||||
} catch (error: unknown) {
|
||||
this.logger.error(`Error checking domain DNS records: ${error instanceof Error ? error.message : String(error)}`);
|
||||
|
||||
Reference in New Issue
Block a user