fix(ci): Update CI workflows, repository URL, and apply minor code formatting fixes

This commit is contained in:
2025-04-30 12:09:13 +00:00
parent 5d77214222
commit 36d9db4332
19 changed files with 531 additions and 264 deletions

View File

@ -3,6 +3,6 @@
*/
export const commitinfo = {
name: '@apiclient.xyz/cloudflare',
version: '6.4.0',
version: '6.4.1',
description: 'A TypeScript client for managing Cloudflare accounts, zones, DNS records, and workers with ease.'
}

View File

@ -39,13 +39,13 @@ export class CloudflareAccount implements plugins.tsclass.network.IConvenientDns
method: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH',
endpoint: string,
data?: any,
customHeaders?: Record<string, string>
customHeaders?: Record<string, string>,
): Promise<T> {
try {
const options: plugins.smartrequest.ISmartRequestOptions = {
method,
headers: {
'Authorization': `Bearer ${this.authToken}`,
Authorization: `Bearer ${this.authToken}`,
'Content-Type': 'application/json',
...customHeaders,
},
@ -62,13 +62,16 @@ export class CloudflareAccount implements plugins.tsclass.network.IConvenientDns
}
logger.log('debug', `Making ${method} request to ${endpoint}`);
const response = await plugins.smartrequest.request(`https://api.cloudflare.com/client/v4${endpoint}`, options);
const response = await plugins.smartrequest.request(
`https://api.cloudflare.com/client/v4${endpoint}`,
options,
);
// Check if response is already an object (might happen with newer smartrequest versions)
if (typeof response.body === 'object' && response.body !== null) {
return response.body;
}
// Otherwise try to parse as JSON
try {
if (typeof response.body === 'string' && response.body.trim()) {
@ -80,13 +83,15 @@ export class CloudflareAccount implements plugins.tsclass.network.IConvenientDns
}
} catch (parseError) {
logger.log('warn', `Failed to parse response as JSON: ${parseError.message}`);
// Create a fake response object to maintain expected structure
return {
result: [],
success: true,
errors: [],
messages: [`Failed to parse: ${typeof response.body === 'string' ? response.body?.substring(0, 50) : typeof response.body}...`]
messages: [
`Failed to parse: ${typeof response.body === 'string' ? response.body?.substring(0, 50) : typeof response.body}...`,
],
} as T;
}
} catch (error) {
@ -115,12 +120,12 @@ export class CloudflareAccount implements plugins.tsclass.network.IConvenientDns
listAccounts: async () => {
try {
const accounts: plugins.ICloudflareTypes['Account'][] = [];
// Collect all accounts using async iterator
for await (const account of this.apiAccount.accounts.list()) {
accounts.push(account as interfaces.ICloudflareApiAccountObject);
}
logger.log('info', `Found ${accounts.length} accounts`);
return accounts;
} catch (error) {
@ -152,21 +157,24 @@ export class CloudflareAccount implements plugins.tsclass.network.IConvenientDns
*/
getRecord: async (
domainNameArg: string,
typeArg: plugins.tsclass.network.TDnsRecordType
typeArg: plugins.tsclass.network.TDnsRecordType,
): Promise<plugins.ICloudflareTypes['Record'] | undefined> => {
try {
const domain = new plugins.smartstring.Domain(domainNameArg);
const recordArrayArg = await this.convenience.listRecords(domain.zoneName);
if (!Array.isArray(recordArrayArg)) {
logger.log('warn', `Expected records array for ${domainNameArg} but got ${typeof recordArrayArg}`);
logger.log(
'warn',
`Expected records array for ${domainNameArg} but got ${typeof recordArrayArg}`,
);
return undefined;
}
const filteredResponse = recordArrayArg.filter((recordArg) => {
return recordArg.type === typeArg && recordArg.name === domainNameArg;
});
return filteredResponse.length > 0 ? filteredResponse[0] : undefined;
} catch (error) {
logger.log('error', `Error getting record for ${domainNameArg}: ${error.message}`);
@ -180,7 +188,7 @@ export class CloudflareAccount implements plugins.tsclass.network.IConvenientDns
domainNameArg: string,
typeArg: plugins.tsclass.network.TDnsRecordType,
contentArg: string,
ttlArg = 1
ttlArg = 1,
): Promise<any> => {
const domain = new plugins.smartstring.Domain(domainNameArg);
const zoneId = await this.convenience.getZoneId(domain.zoneName);
@ -190,7 +198,7 @@ export class CloudflareAccount implements plugins.tsclass.network.IConvenientDns
name: domain.fullName,
content: contentArg,
ttl: ttlArg,
})
});
return response;
},
/**
@ -200,7 +208,7 @@ export class CloudflareAccount implements plugins.tsclass.network.IConvenientDns
*/
removeRecord: async (
domainNameArg: string,
typeArg: plugins.tsclass.network.TDnsRecordType
typeArg: plugins.tsclass.network.TDnsRecordType,
): Promise<any> => {
const domain = new plugins.smartstring.Domain(domainNameArg);
const zoneId = await this.convenience.getZoneId(domain.zoneName);
@ -228,22 +236,28 @@ export class CloudflareAccount implements plugins.tsclass.network.IConvenientDns
logger.log('info', `Cleaning ${typeArg} records for ${domainNameArg}`);
const domain = new plugins.smartstring.Domain(domainNameArg);
const zoneId = await this.convenience.getZoneId(domain.zoneName);
// List all records in the zone for this domain
const records = await this.convenience.listRecords(domain.zoneName);
if (!Array.isArray(records)) {
logger.log('warn', `Expected records array for ${domainNameArg} but got ${typeof records}`);
logger.log(
'warn',
`Expected records array for ${domainNameArg} but got ${typeof records}`,
);
return;
}
// Only delete records matching the specified name and type
const recordsToDelete = records.filter((recordArg) => {
return recordArg.type === typeArg && recordArg.name === domainNameArg;
});
logger.log('info', `Found ${recordsToDelete.length} ${typeArg} records to delete for ${domainNameArg}`);
logger.log(
'info',
`Found ${recordsToDelete.length} ${typeArg} records to delete for ${domainNameArg}`,
);
for (const recordToDelete of recordsToDelete) {
try {
// The official client might have different property locations
@ -253,7 +267,7 @@ export class CloudflareAccount implements plugins.tsclass.network.IConvenientDns
logger.log('warn', `Record ID not found for ${domainNameArg} record`);
continue;
}
await this.apiAccount.dns.records.delete(recordId, {
zone_id: zoneId,
});
@ -263,7 +277,10 @@ export class CloudflareAccount implements plugins.tsclass.network.IConvenientDns
}
}
} catch (error) {
logger.log('error', `Error cleaning ${typeArg} records for ${domainNameArg}: ${error.message}`);
logger.log(
'error',
`Error cleaning ${typeArg} records for ${domainNameArg}: ${error.message}`,
);
}
},
@ -279,19 +296,22 @@ export class CloudflareAccount implements plugins.tsclass.network.IConvenientDns
domainNameArg: string,
typeArg: plugins.tsclass.network.TDnsRecordType,
contentArg: string,
ttlArg: number = 1
ttlArg: number = 1,
): Promise<plugins.ICloudflareTypes['Record']> => {
const domain = new plugins.smartstring.Domain(domainNameArg);
const zoneId = await this.convenience.getZoneId(domain.zoneName);
// Find existing record
const record = await this.convenience.getRecord(domainNameArg, typeArg);
if (!record) {
logger.log('warn', `Record ${domainNameArg} of type ${typeArg} not found for update, creating instead`);
logger.log(
'warn',
`Record ${domainNameArg} of type ${typeArg} not found for update, creating instead`,
);
return this.convenience.createRecord(domainNameArg, typeArg, contentArg, ttlArg);
}
// Update the record - cast to any to access the id property
const recordId = (record as any).id;
const updatedRecord = await this.apiAccount.dns.records.edit(recordId, {
@ -299,9 +319,9 @@ export class CloudflareAccount implements plugins.tsclass.network.IConvenientDns
type: typeArg as any,
name: domain.fullName,
content: contentArg,
ttl: ttlArg
ttl: ttlArg,
});
return updatedRecord;
},
/**
@ -313,14 +333,14 @@ export class CloudflareAccount implements plugins.tsclass.network.IConvenientDns
const domain = new plugins.smartstring.Domain(domainNameArg);
const zoneId = await this.convenience.getZoneId(domain.zoneName);
const records: plugins.ICloudflareTypes['Record'][] = [];
// Collect all records using async iterator
for await (const record of this.apiAccount.dns.records.list({
zone_id: zoneId,
})) {
records.push(record);
}
logger.log('info', `Found ${records.length} DNS records for ${domainNameArg}`);
return records;
} catch (error) {
@ -338,15 +358,18 @@ export class CloudflareAccount implements plugins.tsclass.network.IConvenientDns
if (domainName) {
options.name = domainName;
}
const zones: plugins.ICloudflareTypes['Zone'][] = [];
// Collect all zones using async iterator
for await (const zone of this.apiAccount.zones.list(options)) {
zones.push(zone);
}
logger.log('info', `Found ${zones.length} zones${domainName ? ` matching ${domainName}` : ''}`);
logger.log(
'info',
`Found ${zones.length} zones${domainName ? ` matching ${domainName}` : ''}`,
);
return zones;
} catch (error) {
logger.log('error', `Failed to list zones: ${error.message}`);
@ -390,11 +413,11 @@ export class CloudflareAccount implements plugins.tsclass.network.IConvenientDns
dnsChallenge.hostName,
'TXT',
dnsChallenge.challenge,
120
120,
);
},
acmeRemoveDnsChallenge: async (dnsChallenge: plugins.tsclass.network.IDnsChallenge) => {
await this.convenience.removeRecord(dnsChallenge.hostName, 'TXT');
},
};
}
}

View File

@ -22,7 +22,9 @@ export class CloudflareRecord {
* @param apiObject Cloudflare DNS record API object
* @returns CloudflareRecord instance
*/
public static createFromApiObject(apiObject: plugins.ICloudflareTypes['Record']): CloudflareRecord {
public static createFromApiObject(
apiObject: plugins.ICloudflareTypes['Record'],
): CloudflareRecord {
const record = new CloudflareRecord();
Object.assign(record, apiObject);
return record;
@ -52,28 +54,28 @@ export class CloudflareRecord {
public async update(
cloudflareAccount: any,
newContent: string,
ttl?: number
ttl?: number,
): Promise<CloudflareRecord> {
logger.log('info', `Updating record ${this.name} (${this.type}) with new content`);
const updatedRecord = await cloudflareAccount.apiAccount.dns.records.edit(this.id, {
zone_id: this.zone_id,
type: this.type as any,
name: this.name,
content: newContent,
ttl: ttl || this.ttl,
proxied: this.proxied
proxied: this.proxied,
});
// Update this instance
this.content = newContent;
if (ttl) {
this.ttl = ttl;
}
return this;
}
/**
* Delete this record
* @param cloudflareAccount The Cloudflare account to use
@ -82,15 +84,15 @@ export class CloudflareRecord {
public async delete(cloudflareAccount: any): Promise<boolean> {
try {
logger.log('info', `Deleting record ${this.name} (${this.type})`);
await cloudflareAccount.apiAccount.dns.records.delete(this.id, {
zone_id: this.zone_id
zone_id: this.zone_id,
});
return true;
} catch (error) {
logger.log('error', `Failed to delete record: ${error.message}`);
return false;
}
}
}
}

View File

@ -16,7 +16,7 @@ export class CloudflareWorker {
// STATIC
public static async fromApiObject(
workerManager: WorkerManager,
apiObject
apiObject,
): Promise<CloudflareWorker> {
const newWorker = new CloudflareWorker(workerManager);
Object.assign(newWorker, apiObject);
@ -46,33 +46,33 @@ export class CloudflareWorker {
public async getRoutes() {
try {
this.routes = []; // Reset routes before fetching
// Get all zones using the async iterator
const zones: plugins.ICloudflareTypes['Zone'][] = [];
for await (const zone of this.workerManager.cfAccount.apiAccount.zones.list()) {
zones.push(zone);
}
if (zones.length === 0) {
logger.log('warn', 'No zones found for the account');
return;
}
for (const zone of zones) {
try {
if (!zone || !zone.id) {
logger.log('warn', 'Zone is missing ID property');
continue;
}
// Get worker routes for this zone
const apiRoutes = [];
for await (const route of this.workerManager.cfAccount.apiAccount.workers.routes.list({
zone_id: zone.id
zone_id: zone.id,
})) {
apiRoutes.push(route);
}
// Filter for routes that match this worker's ID
for (const route of apiRoutes) {
if (route.script === this.id) {
@ -81,10 +81,13 @@ export class CloudflareWorker {
}
}
} catch (error) {
logger.log('error', `Failed to get worker routes for zone ${zone.name || zone.id}: ${error.message}`);
logger.log(
'error',
`Failed to get worker routes for zone ${zone.name || zone.id}: ${error.message}`,
);
}
}
logger.log('info', `Found ${this.routes.length} routes for worker ${this.id}`);
} catch (error) {
logger.log('error', `Failed to get routes for worker ${this.id}: ${error.message}`);
@ -100,60 +103,62 @@ export class CloudflareWorker {
public async setRoutes(routeArray: IWorkerRouteDefinition[]) {
// First get all existing routes to determine what we need to create/update
await this.getRoutes();
for (const newRoute of routeArray) {
// Determine whether a route is new, needs an update, or is already up to date
let routeStatus: 'new' | 'needsUpdate' | 'alreadyUpToDate' = 'new';
let existingRouteId: string;
for (const existingRoute of this.routes) {
if (existingRoute.pattern === newRoute.pattern) {
routeStatus = 'needsUpdate';
existingRouteId = existingRoute.id;
if (existingRoute.script === this.id) {
routeStatus = 'alreadyUpToDate';
logger.log('info', `Route ${newRoute.pattern} already exists, no update needed`);
}
}
}
try {
// Get the zone ID
const zone = await this.workerManager.cfAccount.zoneManager.getZoneByName(newRoute.zoneName);
const zone = await this.workerManager.cfAccount.zoneManager.getZoneByName(
newRoute.zoneName,
);
if (!zone) {
logger.log('error', `Zone ${newRoute.zoneName} not found`);
continue;
}
// Handle route creation, update, or skip if already up to date
if (routeStatus === 'new') {
await this.workerManager.cfAccount.apiAccount.workers.routes.create({
zone_id: zone.id,
pattern: newRoute.pattern,
script: this.id
script: this.id,
});
logger.log('info', `Created new route ${newRoute.pattern} for worker ${this.id}`);
} else if (routeStatus === 'needsUpdate') {
await this.workerManager.cfAccount.apiAccount.workers.routes.update(existingRouteId, {
zone_id: zone.id,
pattern: newRoute.pattern,
script: this.id
script: this.id,
});
logger.log('info', `Updated route ${newRoute.pattern} for worker ${this.id}`);
}
} catch (error) {
logger.log('error', `Failed to set route ${newRoute.pattern}: ${error.message}`);
}
}
// Refresh routes after all changes
await this.getRoutes();
}
/**
* Upload or update worker script content
* @param scriptContent The worker script content
@ -163,10 +168,10 @@ export class CloudflareWorker {
if (!this.workerManager.cfAccount.preselectedAccountId) {
throw new Error('No account selected. Please select it first on the account.');
}
try {
logger.log('info', `Updating script for worker ${this.id}`);
// Use the official client to update the script (upload new content)
// Build params as any to include the script form part without TS errors
const updateParams: any = {
@ -175,23 +180,27 @@ export class CloudflareWorker {
};
updateParams['CF-WORKER-BODY-PART'] = 'script';
updateParams['script'] = scriptContent;
const updatedWorker = await this.workerManager.cfAccount.apiAccount.workers.scripts.content.update(this.id, updateParams);
const updatedWorker =
await this.workerManager.cfAccount.apiAccount.workers.scripts.content.update(
this.id,
updateParams,
);
// Update this instance with new data
if (updatedWorker && typeof updatedWorker === 'object') {
Object.assign(this, updatedWorker);
}
// Always ensure the script property is updated
this.script = scriptContent;
return this;
} catch (error) {
logger.log('error', `Failed to update worker script: ${error.message}`);
throw error;
}
}
/**
* Delete this worker script
* @returns True if deletion was successful
@ -200,19 +209,19 @@ export class CloudflareWorker {
if (!this.workerManager.cfAccount.preselectedAccountId) {
throw new Error('No account selected. Please select it first on the account.');
}
try {
logger.log('info', `Deleting worker ${this.id}`);
// Use the official client to delete the worker
await this.workerManager.cfAccount.apiAccount.workers.scripts.delete(this.id, {
account_id: this.workerManager.cfAccount.preselectedAccountId
account_id: this.workerManager.cfAccount.preselectedAccountId,
});
return true;
} catch (error) {
logger.log('error', `Failed to delete worker: ${error.message}`);
return false;
}
}
}
}

View File

@ -20,7 +20,7 @@ export class WorkerManager {
if (!this.cfAccount.preselectedAccountId) {
throw new Error('No account selected. Please select it first on the account.');
}
try {
// Use the official client to create/update the worker (upload script content)
// Build params as any to include the script form part without TS errors
@ -31,12 +31,12 @@ export class WorkerManager {
contentParams['CF-WORKER-BODY-PART'] = 'script';
contentParams['script'] = workerScript;
await this.cfAccount.apiAccount.workers.scripts.content.update(workerName, contentParams);
// Create a new worker instance
const worker = new CloudflareWorker(this);
worker.id = workerName;
worker.script = workerScript;
// Initialize the worker and get its routes
try {
await worker.getRoutes();
@ -44,7 +44,7 @@ export class WorkerManager {
logger.log('warn', `Failed to get routes for worker ${workerName}: ${routeError.message}`);
// Continue anyway since the worker was created
}
return worker;
} catch (error) {
logger.log('error', `Failed to create worker ${workerName}: ${error.message}`);
@ -61,22 +61,22 @@ export class WorkerManager {
if (!this.cfAccount.preselectedAccountId) {
throw new Error('No account selected. Please select it first on the account.');
}
try {
// Get the worker script using the official client
const workerScript = await this.cfAccount.apiAccount.workers.scripts.get(workerName, {
account_id: this.cfAccount.preselectedAccountId
account_id: this.cfAccount.preselectedAccountId,
});
// Create a new worker instance
const worker = new CloudflareWorker(this);
worker.id = workerName;
// Save script content if available
if (workerScript && typeof workerScript === 'object') {
Object.assign(worker, workerScript);
}
// Initialize the worker and get its routes
try {
await worker.getRoutes();
@ -84,7 +84,7 @@ export class WorkerManager {
logger.log('warn', `Failed to get routes for worker ${workerName}: ${routeError.message}`);
// Continue anyway since we found the worker
}
return worker;
} catch (error) {
logger.log('warn', `Worker '${workerName}' not found: ${error.message}`);
@ -100,35 +100,35 @@ export class WorkerManager {
if (!this.cfAccount.preselectedAccountId) {
throw new Error('No account selected. Please select it first on the account.');
}
try {
// Collect all scripts using the new client's async iterator
const workerScripts: plugins.ICloudflareTypes['Script'][] = [];
try {
for await (const script of this.cfAccount.apiAccount.workers.scripts.list({
account_id: this.cfAccount.preselectedAccountId,
})) {
workerScripts.push(script);
}
logger.log('info', `Found ${workerScripts.length} worker scripts`);
return workerScripts;
} catch (error) {
logger.log('warn', `Error while listing workers with async iterator: ${error.message}`);
// Try alternative approach if the async iterator fails
const result = await this.cfAccount.apiAccount.workers.scripts.list({
const result = (await this.cfAccount.apiAccount.workers.scripts.list({
account_id: this.cfAccount.preselectedAccountId,
}) as any;
})) as any;
// Check if the result has a 'result' property (older API response format)
if (result && result.result && Array.isArray(result.result)) {
logger.log('info', `Found ${result.result.length} worker scripts using direct result`);
return result.result;
}
}
logger.log('warn', 'Could not retrieve worker scripts');
return [];
} catch (error) {
@ -136,7 +136,7 @@ export class WorkerManager {
return [];
}
}
/**
* Deletes a worker script
* @param workerName Name of the worker to delete
@ -146,10 +146,10 @@ export class WorkerManager {
if (!this.cfAccount.preselectedAccountId) {
throw new Error('No account selected. Please select it first on the account.');
}
try {
await this.cfAccount.apiAccount.workers.scripts.delete(workerName, {
account_id: this.cfAccount.preselectedAccountId
account_id: this.cfAccount.preselectedAccountId,
});
logger.log('info', `Worker '${workerName}' deleted successfully`);
return true;
@ -158,4 +158,4 @@ export class WorkerManager {
return false;
}
}
}
}

View File

@ -23,7 +23,7 @@ export class CloudflareZone {
public account: interfaces.ICflareZone['account'];
public permissions: string[];
public plan: interfaces.ICflareZone['plan'];
private cfAccount?: CloudflareAccount; // Will be set when created through a manager
/**
@ -33,19 +33,19 @@ export class CloudflareZone {
* @returns CloudflareZone instance
*/
public static createFromApiObject(
apiObject: plugins.ICloudflareTypes['Zone'],
cfAccount?: CloudflareAccount
apiObject: plugins.ICloudflareTypes['Zone'],
cfAccount?: CloudflareAccount,
): CloudflareZone {
const cloudflareZone = new CloudflareZone();
Object.assign(cloudflareZone, apiObject);
if (cfAccount) {
cloudflareZone.cfAccount = cfAccount;
}
return cloudflareZone;
}
/**
* Check if development mode is currently active
* @returns True if development mode is active
@ -53,7 +53,7 @@ export class CloudflareZone {
public isDevelopmentModeActive(): boolean {
return this.development_mode > 0;
}
/**
* Enable development mode for the zone
* @param cfAccount Cloudflare account to use if not already set
@ -62,23 +62,23 @@ export class CloudflareZone {
*/
public async enableDevelopmentMode(
cfAccount?: CloudflareAccount,
duration: number = 10800
duration: number = 10800,
): Promise<CloudflareZone> {
const account = cfAccount || this.cfAccount;
if (!account) {
throw new Error('CloudflareAccount is required to enable development mode');
}
logger.log('info', `Enabling development mode for zone ${this.name}`);
try {
// The official client doesn't have a direct method for development mode
// We'll use the request method for this specific case
await account.request('PATCH', `/zones/${this.id}/settings/development_mode`, {
value: 'on',
time: duration
time: duration,
});
this.development_mode = duration;
return this;
} catch (error) {
@ -86,7 +86,7 @@ export class CloudflareZone {
throw error;
}
}
/**
* Disable development mode for the zone
* @param cfAccount Cloudflare account to use if not already set
@ -97,16 +97,16 @@ export class CloudflareZone {
if (!account) {
throw new Error('CloudflareAccount is required to disable development mode');
}
logger.log('info', `Disabling development mode for zone ${this.name}`);
try {
// The official client doesn't have a direct method for development mode
// We'll use the request method for this specific case
await account.request('PATCH', `/zones/${this.id}/settings/development_mode`, {
value: 'off'
value: 'off',
});
this.development_mode = 0;
return this;
} catch (error) {
@ -114,7 +114,7 @@ export class CloudflareZone {
throw error;
}
}
/**
* Purge all cached content for this zone
* @param cfAccount Cloudflare account to use if not already set
@ -125,13 +125,13 @@ export class CloudflareZone {
if (!account) {
throw new Error('CloudflareAccount is required to purge cache');
}
logger.log('info', `Purging all cache for zone ${this.name}`);
try {
await account.apiAccount.cache.purge({
zone_id: this.id,
purge_everything: true
purge_everything: true,
});
return true;
} catch (error) {
@ -139,7 +139,7 @@ export class CloudflareZone {
return false;
}
}
/**
* Purge specific URLs from the cache
* @param urls Array of URLs to purge
@ -151,17 +151,17 @@ export class CloudflareZone {
if (!account) {
throw new Error('CloudflareAccount is required to purge URLs');
}
if (!urls.length) {
return true;
}
logger.log('info', `Purging ${urls.length} URLs from cache for zone ${this.name}`);
try {
await account.apiAccount.cache.purge({
zone_id: this.id,
files: urls
files: urls,
});
return true;
} catch (error) {
@ -169,7 +169,7 @@ export class CloudflareZone {
return false;
}
}
/**
* Check if the zone is active
* @returns True if the zone is active
@ -177,7 +177,7 @@ export class CloudflareZone {
public isActive(): boolean {
return this.status === 'active' && !this.paused;
}
/**
* Check if the zone is using Cloudflare nameservers
* @returns True if using Cloudflare nameservers
@ -187,11 +187,11 @@ export class CloudflareZone {
if (!this.original_name_servers || !this.name_servers) {
return false;
}
// If they're different, and current nameservers are Cloudflare's
return this.name_servers.some(ns => ns.includes('cloudflare'));
return this.name_servers.some((ns) => ns.includes('cloudflare'));
}
/**
* Update zone settings
* @param settings Settings to update
@ -205,23 +205,23 @@ export class CloudflareZone {
vanity_name_servers: string[];
type: 'full' | 'partial' | 'secondary';
}>,
cfAccount?: CloudflareAccount
cfAccount?: CloudflareAccount,
): Promise<CloudflareZone> {
const account = cfAccount || this.cfAccount;
if (!account) {
throw new Error('CloudflareAccount is required to update zone settings');
}
logger.log('info', `Updating settings for zone ${this.name}`);
try {
// Use the request method instead of zones.edit to avoid type issues
const response: { result: interfaces.ICflareZone } = await account.request(
'PATCH',
`/zones/${this.id}`,
settings
settings,
);
Object.assign(this, response.result);
return this;
} catch (error) {
@ -229,4 +229,4 @@ export class CloudflareZone {
throw error;
}
}
}
}

View File

@ -19,7 +19,7 @@ export class ZoneManager {
public async getZones(zoneName?: string): Promise<CloudflareZone[]> {
try {
const options: any = { per_page: 50 };
// May be optionally filtered by domain name
if (zoneName) {
options.name = zoneName;
@ -29,14 +29,14 @@ export class ZoneManager {
for await (const zone of this.cfAccount.apiAccount.zones.list(options)) {
zones.push(zone);
}
return zones.map(zone => CloudflareZone.createFromApiObject(zone, this.cfAccount));
return zones.map((zone) => CloudflareZone.createFromApiObject(zone, this.cfAccount));
} catch (error) {
logger.log('error', `Failed to fetch zones: ${error.message}`);
return [];
}
}
/**
* Get a single zone by name
* @param zoneName Zone name to find
@ -44,9 +44,9 @@ export class ZoneManager {
*/
public async getZoneByName(zoneName: string): Promise<CloudflareZone | undefined> {
const zones = await this.getZones(zoneName);
return zones.find(zone => zone.name === zoneName);
return zones.find((zone) => zone.name === zoneName);
}
/**
* Get a zone by its ID
* @param zoneId Zone ID to find
@ -56,17 +56,17 @@ export class ZoneManager {
try {
// Use the request method instead of the zones.get method to avoid type issues
const response: { result: interfaces.ICflareZone } = await this.cfAccount.request(
'GET',
`/zones/${zoneId}`
'GET',
`/zones/${zoneId}`,
);
return CloudflareZone.createFromApiObject(response.result as any, this.cfAccount);
} catch (error) {
logger.log('error', `Failed to fetch zone with ID ${zoneId}: ${error.message}`);
return undefined;
}
}
/**
* Create a new zone
* @param zoneName Name of the zone to create
@ -75,37 +75,37 @@ export class ZoneManager {
* @returns The created zone
*/
public async createZone(
zoneName: string,
zoneName: string,
jumpStart: boolean = false,
accountId?: string
accountId?: string,
): Promise<CloudflareZone | undefined> {
const useAccountId = accountId || this.cfAccount.preselectedAccountId;
if (!useAccountId) {
throw new Error('No account selected. Please select it first on the account.');
}
try {
logger.log('info', `Creating zone ${zoneName}`);
// Use the request method for more direct control over the parameters
const response: { result: interfaces.ICflareZone } = await this.cfAccount.request(
'POST',
'/zones',
'POST',
'/zones',
{
name: zoneName,
jump_start: jumpStart,
account: { id: useAccountId }
}
account: { id: useAccountId },
},
);
return CloudflareZone.createFromApiObject(response.result as any, this.cfAccount);
} catch (error) {
logger.log('error', `Failed to create zone ${zoneName}: ${error.message}`);
return undefined;
}
}
/**
* Delete a zone
* @param zoneId ID of the zone to delete
@ -114,7 +114,7 @@ export class ZoneManager {
public async deleteZone(zoneId: string): Promise<boolean> {
try {
logger.log('info', `Deleting zone with ID ${zoneId}`);
// Use the request method to avoid type issues
await this.cfAccount.request('DELETE', `/zones/${zoneId}`);
return true;
@ -123,7 +123,7 @@ export class ZoneManager {
return false;
}
}
/**
* Check if a zone exists
* @param zoneName Name of the zone to check
@ -131,9 +131,9 @@ export class ZoneManager {
*/
public async zoneExists(zoneName: string): Promise<boolean> {
const zones = await this.getZones(zoneName);
return zones.some(zone => zone.name === zoneName);
return zones.some((zone) => zone.name === zoneName);
}
/**
* Activate a zone (if it's in pending status)
* @param zoneId ID of the zone to activate
@ -142,23 +142,23 @@ export class ZoneManager {
public async activateZone(zoneId: string): Promise<CloudflareZone | undefined> {
try {
logger.log('info', `Activating zone with ID ${zoneId}`);
// Use the request method for better control
const response: { result: interfaces.ICflareZone } = await this.cfAccount.request(
'PATCH',
'PATCH',
`/zones/${zoneId}`,
{
status: 'active'
}
status: 'active',
},
);
return CloudflareZone.createFromApiObject(response.result as any, this.cfAccount);
} catch (error) {
logger.log('error', `Failed to activate zone with ID ${zoneId}: ${error.message}`);
return undefined;
}
}
/**
* Check the activation status of a zone
* @param zoneId ID of the zone to check
@ -167,17 +167,17 @@ export class ZoneManager {
public async checkZoneActivation(zoneId: string): Promise<CloudflareZone | undefined> {
try {
logger.log('info', `Checking activation for zone with ID ${zoneId}`);
// For this specific endpoint, we'll use the request method
const response: { result: interfaces.ICflareZone } = await this.cfAccount.request(
'PUT',
`/zones/${zoneId}/activation_check`
`/zones/${zoneId}/activation_check`,
);
return CloudflareZone.createFromApiObject(response.result as any, this.cfAccount);
} catch (error) {
logger.log('error', `Failed to check zone activation with ID ${zoneId}: ${error.message}`);
return undefined;
}
}
}
}

View File

@ -16,7 +16,7 @@ export class CloudflareUtils {
return false;
}
}
/**
* Extracts the zone name (apex domain) from a full domain
* @param domainName Domain name to process
@ -31,7 +31,7 @@ export class CloudflareUtils {
throw new Error(`Invalid domain name: ${domainName}`);
}
}
/**
* Checks if a string is a valid Cloudflare API token
* @param token API token to validate
@ -41,7 +41,7 @@ export class CloudflareUtils {
// Cloudflare API tokens are typically 40+ characters long and start with specific patterns
return /^[A-Za-z0-9_-]{40,}$/.test(token);
}
/**
* Validates a DNS record type
* @param type DNS record type to validate
@ -49,14 +49,28 @@ export class CloudflareUtils {
*/
public static isValidRecordType(type: string): boolean {
const validTypes: plugins.tsclass.network.TDnsRecordType[] = [
'A', 'AAAA', 'CNAME', 'TXT', 'SRV', 'LOC', 'MX',
'NS', 'CAA', 'CERT', 'DNSKEY', 'DS', 'NAPTR', 'SMIMEA',
'SSHFP', 'TLSA', 'URI'
'A',
'AAAA',
'CNAME',
'TXT',
'SRV',
'LOC',
'MX',
'NS',
'CAA',
'CERT',
'DNSKEY',
'DS',
'NAPTR',
'SMIMEA',
'SSHFP',
'TLSA',
'URI',
// Note: SPF has been removed as it's not in TDnsRecordType
];
return validTypes.includes(type as any);
}
/**
* Formats a URL for cache purging (ensures it starts with http/https)
* @param url URL to format
@ -68,7 +82,7 @@ export class CloudflareUtils {
}
return url;
}
/**
* Converts a TTL value in seconds to a human-readable string
* @param ttl TTL in seconds
@ -101,20 +115,23 @@ export class CloudflareUtils {
return `${ttl} seconds`;
}
}
/**
* Safely handles API pagination for Cloudflare requests
* @param makeRequest Function that makes the API request with page parameters
* @returns Combined results from all pages
*/
public static async paginateResults<T>(
makeRequest: (page: number, perPage: number) => Promise<{ result: T[], result_info: { total_pages: number } }>
makeRequest: (
page: number,
perPage: number,
) => Promise<{ result: T[]; result_info: { total_pages: number } }>,
): Promise<T[]> {
const perPage = 50; // Cloudflare's maximum
let page = 1;
let totalPages = 1;
const allResults: T[] = [];
do {
try {
const response = await makeRequest(page, perPage);
@ -126,7 +143,7 @@ export class CloudflareUtils {
break;
}
} while (page <= totalPages);
return allResults;
}
}
}

View File

@ -1,5 +1,9 @@
export { CloudflareAccount } from './cloudflare.classes.account.js';
export { CloudflareWorker, type IWorkerRoute, type IWorkerRouteDefinition } from './cloudflare.classes.worker.js';
export {
CloudflareWorker,
type IWorkerRoute,
type IWorkerRouteDefinition,
} from './cloudflare.classes.worker.js';
export { WorkerManager } from './cloudflare.classes.workermanager.js';
export { CloudflareRecord, type ICloudflareRecordInfo } from './cloudflare.classes.record.js';
export { CloudflareZone } from './cloudflare.classes.zone.js';
@ -8,4 +12,4 @@ export { CloudflareUtils } from './cloudflare.utils.js';
export { commitinfo } from './00_commitinfo_data.js';
// Re-export interfaces
export * from './interfaces/index.js';
export * from './interfaces/index.js';

View File

@ -17,4 +17,4 @@ export interface ICloudflareApiAccountObject {
};
};
created_on: string; // Assuming ISO date string
}
}

View File

@ -42,4 +42,4 @@ export interface ICflareZone {
legacy_discount: boolean;
externally_managed: boolean;
};
}
}