feat(core): Update dependencies, enhance documentation, and improve error handling with clearer API usage examples
This commit is contained in:
@ -3,6 +3,6 @@
|
||||
*/
|
||||
export const commitinfo = {
|
||||
name: '@apiclient.xyz/cloudflare',
|
||||
version: '6.0.6',
|
||||
version: '6.1.0',
|
||||
description: 'A TypeScript client for managing Cloudflare accounts, zones, DNS records, and workers with ease.'
|
||||
}
|
||||
|
@ -17,7 +17,7 @@ export class CloudflareAccount {
|
||||
|
||||
/**
|
||||
* constructor sets auth information on the CloudflareAccountInstance
|
||||
* @param optionsArg
|
||||
* @param authTokenArg Cloudflare API token
|
||||
*/
|
||||
constructor(authTokenArg: string) {
|
||||
this.authToken = authTokenArg;
|
||||
@ -27,21 +27,21 @@ export class CloudflareAccount {
|
||||
}
|
||||
|
||||
/**
|
||||
* Make a request to the Cloudflare API
|
||||
* Make a request to the Cloudflare API for endpoints not directly supported by the official client
|
||||
* Only use this for endpoints that don't have a direct method in the official client
|
||||
* @param method HTTP method (GET, POST, PUT, DELETE)
|
||||
* @param endpoint API endpoint path
|
||||
* @param data Optional request body data
|
||||
* @returns API response
|
||||
*/
|
||||
public async request<T = any>(
|
||||
method: 'GET' | 'POST' | 'PUT' | 'DELETE',
|
||||
method: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH',
|
||||
endpoint: string,
|
||||
data?: any
|
||||
): Promise<T> {
|
||||
try {
|
||||
const options: plugins.smartrequest.ISmartRequestOptions = {
|
||||
method,
|
||||
url: `https://api.cloudflare.com/client/v4${endpoint}`,
|
||||
headers: {
|
||||
'Authorization': `Bearer ${this.authToken}`,
|
||||
'Content-Type': 'application/json',
|
||||
@ -49,10 +49,10 @@ export class CloudflareAccount {
|
||||
};
|
||||
|
||||
if (data) {
|
||||
options.json = data;
|
||||
options.requestBody = JSON.stringify(data);
|
||||
}
|
||||
|
||||
const response = await plugins.smartrequest.request(options);
|
||||
const response = await plugins.smartrequest.request(`https://api.cloudflare.com/client/v4${endpoint}`, options);
|
||||
return JSON.parse(response.body);
|
||||
} catch (error) {
|
||||
logger.log('error', `Cloudflare API request failed: ${error.message}`);
|
||||
@ -108,13 +108,25 @@ export class CloudflareAccount {
|
||||
getRecord: async (
|
||||
domainNameArg: string,
|
||||
typeArg: plugins.tsclass.network.TDnsRecordType
|
||||
): Promise<plugins.ICloudflareTypes['Record']> => {
|
||||
const domain = new plugins.smartstring.Domain(domainNameArg);
|
||||
const recordArrayArg = await this.convenience.listRecords(domain.zoneName);
|
||||
const filteredResponse = recordArrayArg.filter((recordArg) => {
|
||||
return recordArg.type === typeArg && recordArg.name === domainNameArg;
|
||||
});
|
||||
return filteredResponse[0];
|
||||
): 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}`);
|
||||
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}`);
|
||||
return undefined;
|
||||
}
|
||||
},
|
||||
/**
|
||||
* creates a record
|
||||
@ -152,7 +164,10 @@ export class CloudflareAccount {
|
||||
return recordArg.name === domainNameArg && recordArg.type === typeArg;
|
||||
});
|
||||
if (recordToDelete) {
|
||||
await this.apiAccount.dns.records.delete(recordToDelete.id, {
|
||||
// The official client might have the id in a different location
|
||||
// Casting to any to access the id property
|
||||
const recordId = (recordToDelete as any).id;
|
||||
await this.apiAccount.dns.records.delete(recordId, {
|
||||
zone_id: zoneId,
|
||||
});
|
||||
} else {
|
||||
@ -164,15 +179,44 @@ export class CloudflareAccount {
|
||||
* cleanrecord allows the cleaning of any previous records to avoid unwanted sideeffects
|
||||
*/
|
||||
cleanRecord: async (domainNameArg: string, typeArg: plugins.tsclass.network.TDnsRecordType) => {
|
||||
logger.log('info', `Cleaning ${typeArg} records for ${domainNameArg}`);
|
||||
const records = await this.convenience.listRecords(domainNameArg);
|
||||
const recordsToDelete = records.filter((recordArg) => {
|
||||
return recordArg.type === typeArg;
|
||||
});
|
||||
for (const recordToDelete of recordsToDelete) {
|
||||
await this.apiAccount.dns.records.delete(recordToDelete.id, {
|
||||
zone_id: recordToDelete.zone_id,
|
||||
try {
|
||||
logger.log('info', `Cleaning ${typeArg} records for ${domainNameArg}`);
|
||||
const domain = new plugins.smartstring.Domain(domainNameArg);
|
||||
const zoneId = await this.convenience.getZoneId(domain.zoneName);
|
||||
|
||||
const records = await this.convenience.listRecords(domainNameArg);
|
||||
|
||||
if (!Array.isArray(records)) {
|
||||
logger.log('warn', `Expected records array for ${domainNameArg} but got ${typeof records}`);
|
||||
return;
|
||||
}
|
||||
|
||||
const recordsToDelete = records.filter((recordArg) => {
|
||||
return recordArg.type === typeArg;
|
||||
});
|
||||
|
||||
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
|
||||
// Casting to any to access properties safely
|
||||
const recordId = (recordToDelete as any).id;
|
||||
if (!recordId) {
|
||||
logger.log('warn', `Record ID not found for ${domainNameArg} record`);
|
||||
continue;
|
||||
}
|
||||
|
||||
await this.apiAccount.dns.records.delete(recordId, {
|
||||
zone_id: zoneId,
|
||||
});
|
||||
logger.log('info', `Deleted ${typeArg} record ${recordId} for ${domainNameArg}`);
|
||||
} catch (deleteError) {
|
||||
logger.log('error', `Failed to delete record: ${deleteError.message}`);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
logger.log('error', `Error cleaning ${typeArg} records for ${domainNameArg}: ${error.message}`);
|
||||
}
|
||||
},
|
||||
|
||||
@ -201,8 +245,9 @@ export class CloudflareAccount {
|
||||
return this.convenience.createRecord(domainNameArg, typeArg, contentArg, ttlArg);
|
||||
}
|
||||
|
||||
// Update the record
|
||||
const updatedRecord = await this.apiAccount.dns.records.edit(record.id, {
|
||||
// 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, {
|
||||
zone_id: zoneId,
|
||||
type: typeArg as any,
|
||||
name: domain.fullName,
|
||||
@ -220,23 +265,60 @@ export class CloudflareAccount {
|
||||
const domain = new plugins.smartstring.Domain(domainNameArg);
|
||||
const zoneId = await this.convenience.getZoneId(domain.zoneName);
|
||||
const records: plugins.ICloudflareTypes['Record'][] = [];
|
||||
for await (const record of this.apiAccount.dns.records.list({
|
||||
zone_id: zoneId,
|
||||
})) {
|
||||
records.push(record);
|
||||
|
||||
try {
|
||||
const result = await this.apiAccount.dns.records.list({
|
||||
zone_id: zoneId,
|
||||
});
|
||||
|
||||
// Check if the result has a 'result' property (API response format)
|
||||
if (result && result.result && Array.isArray(result.result)) {
|
||||
return result.result;
|
||||
}
|
||||
|
||||
// Otherwise iterate through async iterator (new client format)
|
||||
for await (const record of this.apiAccount.dns.records.list({
|
||||
zone_id: zoneId,
|
||||
})) {
|
||||
records.push(record);
|
||||
}
|
||||
|
||||
return records;
|
||||
} catch (error) {
|
||||
logger.log('error', `Failed to list records for ${domainNameArg}: ${error.message}`);
|
||||
return [];
|
||||
}
|
||||
return records;
|
||||
},
|
||||
/**
|
||||
* list all zones in the associated authenticated account
|
||||
* @param domainName
|
||||
*/
|
||||
listZones: async (domainName?: string) => {
|
||||
const zones: plugins.ICloudflareTypes['Zone'][] = [];
|
||||
for await (const zone of this.apiAccount.zones.list()) {
|
||||
zones.push(zone);
|
||||
const options: any = {};
|
||||
if (domainName) {
|
||||
options.name = domainName;
|
||||
}
|
||||
|
||||
const zones: plugins.ICloudflareTypes['Zone'][] = [];
|
||||
|
||||
try {
|
||||
const result = await this.apiAccount.zones.list(options);
|
||||
|
||||
// Check if the result has a 'result' property (API response format)
|
||||
if (result && result.result && Array.isArray(result.result)) {
|
||||
return result.result;
|
||||
}
|
||||
|
||||
// Otherwise iterate through async iterator (new client format)
|
||||
for await (const zone of this.apiAccount.zones.list(options)) {
|
||||
zones.push(zone);
|
||||
}
|
||||
|
||||
return zones;
|
||||
} catch (error) {
|
||||
logger.log('error', `Failed to list zones: ${error.message}`);
|
||||
return [];
|
||||
}
|
||||
return zones;
|
||||
},
|
||||
/**
|
||||
* purges a zone
|
||||
|
@ -45,17 +45,25 @@ export class CloudflareWorker {
|
||||
*/
|
||||
public async getRoutes() {
|
||||
const zones = await this.workerManager.cfAccount.convenience.listZones();
|
||||
|
||||
for (const zone of zones) {
|
||||
const requestRoute = `/zones/${zone.id}/workers/routes`;
|
||||
const response: {
|
||||
result: interfaces.ICflareWorkerRoute[];
|
||||
} = await this.workerManager.cfAccount.request('GET', requestRoute);
|
||||
for (const route of response.result) {
|
||||
logger.log('debug', `Processing route: ${route.pattern}`);
|
||||
logger.log('debug', `Comparing script: ${route.script} with worker ID: ${this.id}`);
|
||||
if (route.script === this.id) {
|
||||
this.routes.push({ ...route, zoneName: zone.name });
|
||||
try {
|
||||
// The official client doesn't have a direct method to list worker routes
|
||||
// We'll use the custom request method for this specific case
|
||||
const response: {
|
||||
result: interfaces.ICflareWorkerRoute[];
|
||||
} = await this.workerManager.cfAccount.request('GET', `/zones/${zone.id}/workers/routes`);
|
||||
|
||||
for (const route of response.result) {
|
||||
logger.log('debug', `Processing route: ${route.pattern}`);
|
||||
logger.log('debug', `Comparing script: ${route.script} with worker ID: ${this.id}`);
|
||||
|
||||
if (route.script === this.id) {
|
||||
this.routes.push({ ...route, zoneName: zone.name });
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
logger.log('error', `Failed to get worker routes for zone ${zone.name}: ${error.message}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -66,36 +74,101 @@ export class CloudflareWorker {
|
||||
*/
|
||||
public async setRoutes(routeArray: IWorkerRouteDefinition[]) {
|
||||
for (const newRoute of routeArray) {
|
||||
// lets determine wether a route is new, needs an update or already up to date.
|
||||
// Determine whether a route is new, needs an update, or is already up to date
|
||||
let routeStatus: 'new' | 'needsUpdate' | 'alreadyUpToDate' = 'new';
|
||||
let routeIdForUpdate: string;
|
||||
|
||||
for (const existingRoute of this.routes) {
|
||||
if (existingRoute.pattern === newRoute.pattern) {
|
||||
routeStatus = 'needsUpdate';
|
||||
routeIdForUpdate = existingRoute.id;
|
||||
|
||||
if (existingRoute.script === this.id) {
|
||||
routeStatus = 'alreadyUpToDate';
|
||||
logger.log('info', `route already exists, no update needed`);
|
||||
logger.log('info', `Route already exists, no update needed`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// lets care about actually setting routes
|
||||
if (routeStatus === 'new') {
|
||||
try {
|
||||
const zoneId = await this.workerManager.cfAccount.convenience.getZoneId(newRoute.zoneName);
|
||||
const requestRoute = `/zones/${zoneId}/workers/routes`;
|
||||
await this.workerManager.cfAccount.request('POST', requestRoute, {
|
||||
pattern: newRoute.pattern,
|
||||
script: this.id,
|
||||
});
|
||||
} else if (routeStatus === 'needsUpdate') {
|
||||
const zoneId = await this.workerManager.cfAccount.convenience.getZoneId(newRoute.zoneName);
|
||||
const requestRoute = `/zones/${zoneId}/workers/routes/${routeIdForUpdate}`;
|
||||
await this.workerManager.cfAccount.request('PUT', requestRoute, {
|
||||
pattern: newRoute.pattern,
|
||||
script: this.id,
|
||||
});
|
||||
|
||||
// Handle route creation or update
|
||||
if (routeStatus === 'new') {
|
||||
// The official client doesn't have a direct method to create worker routes
|
||||
// We'll use the custom request method for this specific case
|
||||
await this.workerManager.cfAccount.request('POST', `/zones/${zoneId}/workers/routes`, {
|
||||
pattern: newRoute.pattern,
|
||||
script: this.id,
|
||||
});
|
||||
|
||||
logger.log('info', `Created new route ${newRoute.pattern} for worker ${this.id}`);
|
||||
} else if (routeStatus === 'needsUpdate') {
|
||||
// The official client doesn't have a direct method to update worker routes
|
||||
// We'll use the custom request method for this specific case
|
||||
await this.workerManager.cfAccount.request('PUT', `/zones/${zoneId}/workers/routes/${routeIdForUpdate}`, {
|
||||
pattern: newRoute.pattern,
|
||||
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}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Upload or update worker script content
|
||||
* @param scriptContent The worker script content
|
||||
* @returns Updated worker object
|
||||
*/
|
||||
public async updateScript(scriptContent: string): Promise<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}`);
|
||||
|
||||
// The official client requires the metadata property
|
||||
const updatedWorker = await this.workerManager.cfAccount.apiAccount.workers.scripts.content.update(this.id, {
|
||||
account_id: this.workerManager.cfAccount.preselectedAccountId,
|
||||
"CF-WORKER-BODY-PART": scriptContent,
|
||||
metadata: {} // Required empty object
|
||||
});
|
||||
|
||||
// Update this instance with new data
|
||||
Object.assign(this, updatedWorker);
|
||||
|
||||
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
|
||||
*/
|
||||
public async delete(): Promise<boolean> {
|
||||
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}`);
|
||||
|
||||
await this.workerManager.cfAccount.apiAccount.workers.scripts.delete(this.id, {
|
||||
account_id: this.workerManager.cfAccount.preselectedAccountId
|
||||
});
|
||||
|
||||
return true;
|
||||
} catch (error) {
|
||||
logger.log('error', `Failed to delete worker: ${error.message}`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
@ -14,17 +14,33 @@ export class WorkerManager {
|
||||
* Creates a new worker or updates an existing one
|
||||
* @param workerName Name of the worker
|
||||
* @param workerScript JavaScript content of the worker
|
||||
* @returns The created or updated worker
|
||||
* @returns CloudflareWorker instance for the created/updated worker
|
||||
*/
|
||||
public async createWorker(workerName: string, workerScript: string): Promise<plugins.ICloudflareTypes['Script']> {
|
||||
public async createWorker(workerName: string, workerScript: string): Promise<CloudflareWorker> {
|
||||
if (!this.cfAccount.preselectedAccountId) {
|
||||
throw new Error('No account selected. Please select it first on the account.');
|
||||
}
|
||||
const worker = await this.cfAccount.apiAccount.workers.scripts.content.update(workerName, {
|
||||
account_id: this.cfAccount.preselectedAccountId,
|
||||
"CF-WORKER-BODY-PART": workerScript,
|
||||
});
|
||||
return worker;
|
||||
|
||||
try {
|
||||
// Create or update the worker script
|
||||
await this.cfAccount.apiAccount.workers.scripts.content.update(workerName, {
|
||||
account_id: this.cfAccount.preselectedAccountId,
|
||||
"CF-WORKER-BODY-PART": workerScript,
|
||||
metadata: {} // Required empty object
|
||||
});
|
||||
|
||||
// Create a new worker instance directly
|
||||
const worker = new CloudflareWorker(this);
|
||||
worker.id = workerName;
|
||||
|
||||
// Initialize the worker and get its routes
|
||||
await worker.getRoutes();
|
||||
|
||||
return worker;
|
||||
} catch (error) {
|
||||
logger.log('error', `Failed to create worker ${workerName}: ${error.message}`);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -38,11 +54,19 @@ export class WorkerManager {
|
||||
}
|
||||
|
||||
try {
|
||||
const script = await this.cfAccount.apiAccount.workers.scripts.get(workerName, {
|
||||
// Check if the worker exists
|
||||
await this.cfAccount.apiAccount.workers.scripts.get(workerName, {
|
||||
account_id: this.cfAccount.preselectedAccountId
|
||||
});
|
||||
|
||||
return CloudflareWorker.fromApiObject(this, script);
|
||||
// Create a new worker instance directly
|
||||
const worker = new CloudflareWorker(this);
|
||||
worker.id = workerName;
|
||||
|
||||
// Initialize the worker and get its routes
|
||||
await worker.getRoutes();
|
||||
|
||||
return worker;
|
||||
} catch (error) {
|
||||
logger.log('warn', `Worker '${workerName}' not found: ${error.message}`);
|
||||
return undefined;
|
||||
@ -57,13 +81,29 @@ export class WorkerManager {
|
||||
if (!this.cfAccount.preselectedAccountId) {
|
||||
throw new Error('No account selected. Please select it first on the account.');
|
||||
}
|
||||
const workerScripts: plugins.ICloudflareTypes['Script'][] = [];
|
||||
for await (const scriptArg of this.cfAccount.apiAccount.workers.scripts.list({
|
||||
account_id: this.cfAccount.preselectedAccountId,
|
||||
})) {
|
||||
workerScripts.push(scriptArg);
|
||||
|
||||
try {
|
||||
const result = await this.cfAccount.apiAccount.workers.scripts.list({
|
||||
account_id: this.cfAccount.preselectedAccountId,
|
||||
});
|
||||
|
||||
// Check if the result has a 'result' property (API response format)
|
||||
if (result && result.result && Array.isArray(result.result)) {
|
||||
return result.result;
|
||||
}
|
||||
|
||||
// Otherwise collect from async iterator (new client format)
|
||||
const workerScripts: plugins.ICloudflareTypes['Script'][] = [];
|
||||
for await (const scriptArg of this.cfAccount.apiAccount.workers.scripts.list({
|
||||
account_id: this.cfAccount.preselectedAccountId,
|
||||
})) {
|
||||
workerScripts.push(scriptArg);
|
||||
}
|
||||
return workerScripts;
|
||||
} catch (error) {
|
||||
logger.log('error', `Failed to list worker scripts: ${error.message}`);
|
||||
return [];
|
||||
}
|
||||
return workerScripts;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1,6 +1,7 @@
|
||||
import * as plugins from './cloudflare.plugins.js';
|
||||
import { logger } from './cloudflare.logger.js';
|
||||
import * as interfaces from './interfaces/index.js';
|
||||
import type { CloudflareAccount } from './cloudflare.classes.account.js';
|
||||
|
||||
export class CloudflareZone {
|
||||
// Zone properties
|
||||
@ -23,7 +24,7 @@ export class CloudflareZone {
|
||||
public permissions: string[];
|
||||
public plan: interfaces.ICflareZone['plan'];
|
||||
|
||||
private cfAccount: any; // Will be set when created through a manager
|
||||
private cfAccount?: CloudflareAccount; // Will be set when created through a manager
|
||||
|
||||
/**
|
||||
* Create a CloudflareZone instance from an API object
|
||||
@ -33,7 +34,7 @@ export class CloudflareZone {
|
||||
*/
|
||||
public static createFromApiObject(
|
||||
apiObject: plugins.ICloudflareTypes['Zone'],
|
||||
cfAccount?: any
|
||||
cfAccount?: CloudflareAccount
|
||||
): CloudflareZone {
|
||||
const cloudflareZone = new CloudflareZone();
|
||||
Object.assign(cloudflareZone, apiObject);
|
||||
@ -60,7 +61,7 @@ export class CloudflareZone {
|
||||
* @returns Updated zone
|
||||
*/
|
||||
public async enableDevelopmentMode(
|
||||
cfAccount?: any,
|
||||
cfAccount?: CloudflareAccount,
|
||||
duration: number = 10800
|
||||
): Promise<CloudflareZone> {
|
||||
const account = cfAccount || this.cfAccount;
|
||||
@ -70,13 +71,20 @@ export class CloudflareZone {
|
||||
|
||||
logger.log('info', `Enabling development mode for zone ${this.name}`);
|
||||
|
||||
const response = await account.request('PATCH', `/zones/${this.id}/settings/development_mode`, {
|
||||
value: 'on',
|
||||
time: duration
|
||||
});
|
||||
|
||||
this.development_mode = duration;
|
||||
return this;
|
||||
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
|
||||
});
|
||||
|
||||
this.development_mode = duration;
|
||||
return this;
|
||||
} catch (error) {
|
||||
logger.log('error', `Failed to enable development mode: ${error.message}`);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -84,7 +92,7 @@ export class CloudflareZone {
|
||||
* @param cfAccount Cloudflare account to use if not already set
|
||||
* @returns Updated zone
|
||||
*/
|
||||
public async disableDevelopmentMode(cfAccount?: any): Promise<CloudflareZone> {
|
||||
public async disableDevelopmentMode(cfAccount?: CloudflareAccount): Promise<CloudflareZone> {
|
||||
const account = cfAccount || this.cfAccount;
|
||||
if (!account) {
|
||||
throw new Error('CloudflareAccount is required to disable development mode');
|
||||
@ -92,12 +100,19 @@ export class CloudflareZone {
|
||||
|
||||
logger.log('info', `Disabling development mode for zone ${this.name}`);
|
||||
|
||||
const response = await account.request('PATCH', `/zones/${this.id}/settings/development_mode`, {
|
||||
value: 'off'
|
||||
});
|
||||
|
||||
this.development_mode = 0;
|
||||
return this;
|
||||
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'
|
||||
});
|
||||
|
||||
this.development_mode = 0;
|
||||
return this;
|
||||
} catch (error) {
|
||||
logger.log('error', `Failed to disable development mode: ${error.message}`);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -105,7 +120,7 @@ export class CloudflareZone {
|
||||
* @param cfAccount Cloudflare account to use if not already set
|
||||
* @returns True if successful
|
||||
*/
|
||||
public async purgeCache(cfAccount?: any): Promise<boolean> {
|
||||
public async purgeCache(cfAccount?: CloudflareAccount): Promise<boolean> {
|
||||
const account = cfAccount || this.cfAccount;
|
||||
if (!account) {
|
||||
throw new Error('CloudflareAccount is required to purge cache');
|
||||
@ -114,7 +129,8 @@ export class CloudflareZone {
|
||||
logger.log('info', `Purging all cache for zone ${this.name}`);
|
||||
|
||||
try {
|
||||
await account.request('POST', `/zones/${this.id}/purge_cache`, {
|
||||
await account.apiAccount.cache.purge({
|
||||
zone_id: this.id,
|
||||
purge_everything: true
|
||||
});
|
||||
return true;
|
||||
@ -130,7 +146,7 @@ export class CloudflareZone {
|
||||
* @param cfAccount Cloudflare account to use if not already set
|
||||
* @returns True if successful
|
||||
*/
|
||||
public async purgeUrls(urls: string[], cfAccount?: any): Promise<boolean> {
|
||||
public async purgeUrls(urls: string[], cfAccount?: CloudflareAccount): Promise<boolean> {
|
||||
const account = cfAccount || this.cfAccount;
|
||||
if (!account) {
|
||||
throw new Error('CloudflareAccount is required to purge URLs');
|
||||
@ -143,7 +159,8 @@ export class CloudflareZone {
|
||||
logger.log('info', `Purging ${urls.length} URLs from cache for zone ${this.name}`);
|
||||
|
||||
try {
|
||||
await account.request('POST', `/zones/${this.id}/purge_cache`, {
|
||||
await account.apiAccount.cache.purge({
|
||||
zone_id: this.id,
|
||||
files: urls
|
||||
});
|
||||
return true;
|
||||
@ -174,4 +191,42 @@ export class CloudflareZone {
|
||||
// If they're different, and current nameservers are Cloudflare's
|
||||
return this.name_servers.some(ns => ns.includes('cloudflare'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Update zone settings
|
||||
* @param settings Settings to update
|
||||
* @param cfAccount Cloudflare account to use if not already set
|
||||
* @returns Updated zone
|
||||
*/
|
||||
public async updateSettings(
|
||||
settings: Partial<{
|
||||
paused: boolean;
|
||||
plan: { id: string };
|
||||
vanity_name_servers: string[];
|
||||
type: 'full' | 'partial' | 'secondary';
|
||||
}>,
|
||||
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
|
||||
);
|
||||
|
||||
Object.assign(this, response.result);
|
||||
return this;
|
||||
} catch (error) {
|
||||
logger.log('error', `Failed to update zone settings: ${error.message}`);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
@ -17,19 +17,20 @@ export class ZoneManager {
|
||||
* @returns Array of CloudflareZone instances
|
||||
*/
|
||||
public async getZones(zoneName?: string): Promise<CloudflareZone[]> {
|
||||
let requestRoute = `/zones?per_page=50`;
|
||||
|
||||
// May be optionally filtered by domain name
|
||||
if (zoneName) {
|
||||
requestRoute = `${requestRoute}&name=${encodeURIComponent(zoneName)}`;
|
||||
}
|
||||
|
||||
try {
|
||||
const response: { result: interfaces.ICflareZone[] } = await this.cfAccount.request('GET', requestRoute);
|
||||
const options: any = { per_page: 50 };
|
||||
|
||||
return response.result.map(apiObject =>
|
||||
CloudflareZone.createFromApiObject(apiObject as any, this.cfAccount)
|
||||
);
|
||||
// May be optionally filtered by domain name
|
||||
if (zoneName) {
|
||||
options.name = zoneName;
|
||||
}
|
||||
|
||||
const zones: plugins.ICloudflareTypes['Zone'][] = [];
|
||||
for await (const zone of this.cfAccount.apiAccount.zones.list(options)) {
|
||||
zones.push(zone);
|
||||
}
|
||||
|
||||
return zones.map(zone => CloudflareZone.createFromApiObject(zone, this.cfAccount));
|
||||
} catch (error) {
|
||||
logger.log('error', `Failed to fetch zones: ${error.message}`);
|
||||
return [];
|
||||
@ -53,6 +54,7 @@ export class ZoneManager {
|
||||
*/
|
||||
public async getZoneById(zoneId: string): Promise<CloudflareZone | undefined> {
|
||||
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}`
|
||||
@ -86,6 +88,7 @@ export class ZoneManager {
|
||||
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',
|
||||
@ -112,6 +115,7 @@ export class ZoneManager {
|
||||
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;
|
||||
} catch (error) {
|
||||
@ -139,9 +143,13 @@ export class ZoneManager {
|
||||
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(
|
||||
'PUT',
|
||||
`/zones/${zoneId}/activation_check`
|
||||
'PATCH',
|
||||
`/zones/${zoneId}`,
|
||||
{
|
||||
status: 'active'
|
||||
}
|
||||
);
|
||||
|
||||
return CloudflareZone.createFromApiObject(response.result as any, this.cfAccount);
|
||||
@ -150,4 +158,26 @@ export class ZoneManager {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check the activation status of a zone
|
||||
* @param zoneId ID of the zone to check
|
||||
* @returns Updated zone or undefined if check failed
|
||||
*/
|
||||
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`
|
||||
);
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
@ -10,7 +10,8 @@ export class CloudflareUtils {
|
||||
public static isValidDomain(domainName: string): boolean {
|
||||
try {
|
||||
const domain = new plugins.smartstring.Domain(domainName);
|
||||
return domain.isValid();
|
||||
// Check if the domain has at least a TLD and a name
|
||||
return domain.fullName.includes('.') && domain.zoneName.length > 0;
|
||||
} catch (error) {
|
||||
return false;
|
||||
}
|
||||
@ -49,8 +50,9 @@ export class CloudflareUtils {
|
||||
public static isValidRecordType(type: string): boolean {
|
||||
const validTypes: plugins.tsclass.network.TDnsRecordType[] = [
|
||||
'A', 'AAAA', 'CNAME', 'TXT', 'SRV', 'LOC', 'MX',
|
||||
'NS', 'SPF', 'CERT', 'DNSKEY', 'DS', 'NAPTR', 'SMIMEA',
|
||||
'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);
|
||||
}
|
||||
|
Reference in New Issue
Block a user