Files
cloudly/ts/manager.domain/classes.domainmanager.ts
T

259 lines
8.0 KiB
TypeScript

import type { Cloudly } from '../classes.cloudly.js';
import * as plugins from '../plugins.js';
import { Domain } from './classes.domain.js';
interface IWorkHosterDomain {
name: string;
nameservers?: string[];
capabilities?: {
canCreateSubdomains: boolean;
canManageDnsRecords: boolean;
canIssueCertificates: boolean;
canHostEmail: boolean;
};
}
export class DomainManager {
public typedrouter = new plugins.typedrequest.TypedRouter();
public cloudlyRef: Cloudly;
get db() {
return this.cloudlyRef.mongodbConnector.smartdataDb;
}
public CDomain = plugins.smartdata.setDefaultManagerForDoc(this, Domain);
constructor(cloudlyRef: Cloudly) {
this.cloudlyRef = cloudlyRef;
this.cloudlyRef.typedrouter.addTypedRouter(this.typedrouter);
// Get all domains
this.typedrouter.addTypedHandler(
new plugins.typedrequest.TypedHandler<plugins.servezoneInterfaces.requests.domain.IRequest_Any_Cloudly_GetDomains>(
'getDomains',
async (reqArg) => {
await plugins.smartguard.passGuardsOrReject(reqArg, [
this.cloudlyRef.authManager.validIdentityGuard,
]);
const domains = await this.CDomain.getDomains();
return {
domains: await Promise.all(
domains.map((domain) => domain.createSavableObject())
),
};
}
)
);
// Get domain by ID
this.typedrouter.addTypedHandler(
new plugins.typedrequest.TypedHandler<plugins.servezoneInterfaces.requests.domain.IRequest_Any_Cloudly_GetDomainById>(
'getDomainById',
async (reqArg) => {
await plugins.smartguard.passGuardsOrReject(reqArg, [
this.cloudlyRef.authManager.validIdentityGuard,
]);
const domain = await this.CDomain.getDomainById(reqArg.domainId);
if (!domain) {
throw new Error(`Domain with id ${reqArg.domainId} not found`);
}
return {
domain: await domain.createSavableObject(),
};
}
)
);
// Create domain
this.typedrouter.addTypedHandler(
new plugins.typedrequest.TypedHandler<plugins.servezoneInterfaces.requests.domain.IRequest_Any_Cloudly_CreateDomain>(
'createDomain',
async (reqArg) => {
await plugins.smartguard.passGuardsOrReject(reqArg, [
this.cloudlyRef.authManager.validIdentityGuard,
]);
// Check if domain already exists
const existingDomain = await this.CDomain.getDomainByName(reqArg.domainData.name);
if (existingDomain) {
throw new Error(`Domain ${reqArg.domainData.name} already exists`);
}
const domain = await this.CDomain.createDomain(reqArg.domainData);
return {
domain: await domain.createSavableObject(),
};
}
)
);
// Update domain
this.typedrouter.addTypedHandler(
new plugins.typedrequest.TypedHandler<plugins.servezoneInterfaces.requests.domain.IRequest_Any_Cloudly_UpdateDomain>(
'updateDomain',
async (reqArg) => {
await plugins.smartguard.passGuardsOrReject(reqArg, [
this.cloudlyRef.authManager.validIdentityGuard,
]);
const domain = await this.CDomain.updateDomain(
reqArg.domainId,
reqArg.domainData
);
return {
domain: await domain.createSavableObject(),
};
}
)
);
// Delete domain
this.typedrouter.addTypedHandler(
new plugins.typedrequest.TypedHandler<plugins.servezoneInterfaces.requests.domain.IRequest_Any_Cloudly_DeleteDomain>(
'deleteDomain',
async (reqArg) => {
await plugins.smartguard.passGuardsOrReject(reqArg, [
this.cloudlyRef.authManager.validIdentityGuard,
]);
const success = await this.CDomain.deleteDomain(reqArg.domainId);
return {
success,
};
}
)
);
// Verify domain
this.typedrouter.addTypedHandler(
new plugins.typedrequest.TypedHandler<plugins.servezoneInterfaces.requests.domain.IRequest_Any_Cloudly_VerifyDomain>(
'verifyDomain',
async (reqArg) => {
await plugins.smartguard.passGuardsOrReject(reqArg, [
this.cloudlyRef.authManager.validIdentityGuard,
]);
const domain = await this.CDomain.getDomainById(reqArg.domainId);
if (!domain) {
throw new Error(`Domain with id ${reqArg.domainId} not found`);
}
const verificationResult = await domain.verifyDomain(reqArg.verificationMethod);
return {
domain: await domain.createSavableObject(),
verificationResult,
};
}
)
);
}
/**
* Initialize the domain manager
*/
public async init() {
await this.syncExternalGatewayDomains().catch((error) => {
console.log(`External gateway domain sync failed: ${(error as Error).message}`);
});
console.log('Domain Manager initialized');
}
public async syncExternalGatewayDomains(): Promise<number> {
const settings = await this.cloudlyRef.settingsManager.getSettings();
if (!settings.dcrouterGatewayUrl || !settings.dcrouterGatewayApiToken) {
return 0;
}
const typedRequest = new plugins.typedrequest.TypedRequest<any>(
`${settings.dcrouterGatewayUrl.replace(/\/+$/, '')}/typedrequest`,
'getWorkHosterDomains',
);
const response = await typedRequest.fire({
apiToken: settings.dcrouterGatewayApiToken,
}) as { domains: IWorkHosterDomain[] };
const activeDomainNames = new Set<string>();
for (const gatewayDomain of response.domains) {
const domainName = gatewayDomain.name.trim().toLowerCase();
if (!domainName) continue;
activeDomainNames.add(domainName);
const existingDomain = await this.CDomain.getDomainByName(domainName);
const tags = Array.from(new Set([...(existingDomain?.data.tags || []), 'dcrouter']));
const domainData: Partial<plugins.servezoneInterfaces.data.IDomain['data']> = {
name: domainName,
status: 'active',
verificationStatus: 'not_required',
nameservers: gatewayDomain.nameservers || existingDomain?.data.nameservers || [],
autoRenew: gatewayDomain.capabilities?.canIssueCertificates !== false,
activationState: 'available',
syncSource: 'manual',
lastSyncAt: Date.now(),
isExternal: true,
tags,
};
if (existingDomain) {
await this.CDomain.updateDomain(existingDomain.id, domainData);
} else {
await this.CDomain.createDomain(domainData as plugins.servezoneInterfaces.data.IDomain['data']);
}
}
const knownDomains = await this.CDomain.getDomains();
for (const domain of knownDomains) {
if (domain.data.tags?.includes('dcrouter') && !activeDomainNames.has(domain.data.name)) {
await this.CDomain.updateDomain(domain.id, {
activationState: 'ignored',
lastSyncAt: Date.now(),
});
}
}
console.log(`Synced ${activeDomainNames.size} domain(s) from external dcrouter gateway`);
return activeDomainNames.size;
}
/**
* Stop the domain manager
*/
public async stop() {
console.log('Domain Manager stopped');
}
/**
* Get all active domains
*/
public async getActiveDomains() {
const domains = await this.CDomain.getInstances({
'data.status': 'active',
});
return domains;
}
/**
* Get domains that are expiring soon
*/
public async getExpiringDomains(daysThreshold: number = 30) {
const domains = await this.CDomain.getDomains();
return domains.filter(domain => domain.isExpiringSoon(daysThreshold));
}
/**
* Check if a domain name is available (not in our system)
*/
public async isDomainAvailable(domainName: string): Promise<boolean> {
const existingDomain = await this.CDomain.getDomainByName(domainName);
return !existingDomain;
}
}