fix(core): Improve logging consistency, record update functionality, and API error handling in Cloudflare modules
This commit is contained in:
		
							
								
								
									
										219
									
								
								changelog.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										219
									
								
								changelog.md
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,219 @@ | |||||||
|  | # Changelog | ||||||
|  |  | ||||||
|  | ## 2025-03-19 - 6.0.6 - fix(core) | ||||||
|  | Improve logging consistency, record update functionality, and API error handling in Cloudflare modules | ||||||
|  |  | ||||||
|  | - Replaced raw console.log calls with logger.log for unified logging across modules | ||||||
|  | - Implemented and documented the updateRecord method with proper parameters in CloudflareAccount | ||||||
|  | - Enhanced API request error handling and added detailed documentation in request methods | ||||||
|  | - Refactored CloudflareWorker and WorkerManager methods to improve clarity and maintainability | ||||||
|  | - Updated ZoneManager and CloudflareZone to improve error reporting and zone manipulation | ||||||
|  |  | ||||||
|  | ## 2024-06-16 - 6.0.5 – no significant changes | ||||||
|  | _No significant changes in this release._ | ||||||
|  |  | ||||||
|  | ## 2024-06-16 - 6.0.4 – miscellaneous | ||||||
|  | Several improvements and fixes: | ||||||
|  | - fix(start supporting workers again): update | ||||||
|  | - update license info | ||||||
|  | - update readme | ||||||
|  | - switch to official cloudflare api client while keeping class based approach | ||||||
|  |  | ||||||
|  | ## 2024-06-15 - 6.0.3 – core | ||||||
|  | - fix(core): update | ||||||
|  |  | ||||||
|  | ## 2023-06-13 - 6.0.2 – core | ||||||
|  | - fix(core): update | ||||||
|  |  | ||||||
|  | ## 2023-06-13 - 6.0.1 – core | ||||||
|  | - fix(core): update | ||||||
|  |  | ||||||
|  | ## 2022-09-27 - 6.0.0 – core | ||||||
|  | - fix(core): update | ||||||
|  |  | ||||||
|  | ## 2022-09-27 - 5.0.10 – core | ||||||
|  | - BREAKING CHANGE(core): switch to esm | ||||||
|  |  | ||||||
|  | ## 2022-09-27 - 5.0.9 – core | ||||||
|  | - fix(core): update | ||||||
|  |  | ||||||
|  | ## 2021-01-22 - 5.0.8 – core | ||||||
|  | - fix(core): update | ||||||
|  |  | ||||||
|  | ## 2021-01-22 - 5.0.7 – core | ||||||
|  | - fix(core): update | ||||||
|  |  | ||||||
|  | ## 2021-01-22 - 5.0.6 – core | ||||||
|  | - fix(core): update | ||||||
|  |  | ||||||
|  | ## 2020-06-10 - 5.0.5 – core | ||||||
|  | - fix(core): update | ||||||
|  |  | ||||||
|  | ## 2020-06-10 - 5.0.4 – core | ||||||
|  | - fix(core): update | ||||||
|  |  | ||||||
|  | ## 2020-02-28 - 5.0.3 – core | ||||||
|  | - fix(core): update | ||||||
|  |  | ||||||
|  | ## 2020-02-28 - 5.0.2 – core | ||||||
|  | - fix(core): update | ||||||
|  |  | ||||||
|  | ## 2020-02-28 - 5.0.1 – core | ||||||
|  | - fix(core): update | ||||||
|  |  | ||||||
|  | ## 2020-02-28 - 5.0.0 – core | ||||||
|  | - fix(core): update | ||||||
|  |  | ||||||
|  | ## 2020-02-19 - 4.0.5 – account | ||||||
|  | - BREAKING CHANGE(account): authorization now uses the new Account API | ||||||
|  |  | ||||||
|  | ## 2020-02-19 - 4.0.4 – core | ||||||
|  | - fix(core): update | ||||||
|  |  | ||||||
|  | ## 2020-02-19 - 4.0.3 – core | ||||||
|  | - fix(core): update | ||||||
|  |  | ||||||
|  | ## 2020-02-10 - 4.0.2 – core | ||||||
|  | - fix(core): update | ||||||
|  |  | ||||||
|  | ## 2020-02-10 - 4.0.1 – core | ||||||
|  | - fix(core): update | ||||||
|  |  | ||||||
|  | ## 2020-02-10 - 4.0.0 – core | ||||||
|  | - fix(core): update | ||||||
|  |  | ||||||
|  | ## 2020-02-09 - 3.0.7 – API | ||||||
|  | - BREAKING CHANGE(API): move to .convenience property | ||||||
|  |  | ||||||
|  | ## 2020-02-09 - 3.0.6 – core | ||||||
|  | - fix(core): update | ||||||
|  |  | ||||||
|  | ## 2020-02-09 - 3.0.5 – core | ||||||
|  | - fix(core): update | ||||||
|  |  | ||||||
|  | ## 2019-07-19 - 3.0.4 – core | ||||||
|  | - fix(core): update | ||||||
|  |  | ||||||
|  | ## 2019-07-18 - 3.0.3 – core | ||||||
|  | - fix(core): update | ||||||
|  |  | ||||||
|  | ## 2019-07-18 - 3.0.2 – core | ||||||
|  | - fix(core): update | ||||||
|  |  | ||||||
|  | ## 2019-07-18 - 3.0.1 – core | ||||||
|  | - fix(core): update | ||||||
|  |  | ||||||
|  | ## 2019-07-18 - 3.0.0 – core | ||||||
|  | - fix(core): update | ||||||
|  |  | ||||||
|  | ## 2019-07-18 - 2.0.1 – core | ||||||
|  | - fix(core): update | ||||||
|  |  | ||||||
|  | ## 2019-07-18 - 2.0.0 – core | ||||||
|  | - fix(core): update | ||||||
|  |  | ||||||
|  | ## 2019-07-18 - 2.0.2 – no significant changes | ||||||
|  | _No significant changes in this release._ | ||||||
|  |  | ||||||
|  | ## 2018-08-13 - 1.0.5 – scope | ||||||
|  | - BREAKING CHANGE(scope): change scope, tools and package name | ||||||
|  |  | ||||||
|  | ## 2017-06-11 - 1.0.4 – misc | ||||||
|  | - now using tsclass | ||||||
|  |  | ||||||
|  | ## 2017-06-09 - 1.0.3 – misc | ||||||
|  | - update dependencies | ||||||
|  |  | ||||||
|  | ## 2017-06-05 - 1.0.2 – misc | ||||||
|  | - now supports purging of assets | ||||||
|  | - improve test | ||||||
|  |  | ||||||
|  | ## 2017-06-04 - 1.0.1 – misc | ||||||
|  | - add npmextra.json | ||||||
|  |  | ||||||
|  | ## 2017-06-04 - 1.0.0 – misc | ||||||
|  | - add type TRecord, update ci | ||||||
|  |  | ||||||
|  | ## 2017-06-04 - 0.0.20 – no significant changes | ||||||
|  | _No significant changes in this release._ | ||||||
|  |  | ||||||
|  | ## 2017-06-04 - 0.0.19 – misc | ||||||
|  | - go async/await | ||||||
|  | - update brand link | ||||||
|  |  | ||||||
|  | ## 2017-02-12 - 0.0.18 – misc | ||||||
|  | - update README | ||||||
|  |  | ||||||
|  | ## 2017-01-29 - 0.0.17 – misc | ||||||
|  | - update README | ||||||
|  |  | ||||||
|  | ## 2017-01-29 - 0.0.16 – misc | ||||||
|  | - fix tests to run in parallel | ||||||
|  |  | ||||||
|  | ## 2017-01-29 - 0.0.15 – misc | ||||||
|  | - fixed bad request retry | ||||||
|  |  | ||||||
|  | ## 2017-01-29 - 0.0.14 – misc | ||||||
|  | - fix testing timeouts | ||||||
|  |  | ||||||
|  | ## 2017-01-29 - 0.0.13 – misc | ||||||
|  | - added random retry times | ||||||
|  |  | ||||||
|  | ## 2017-01-29 - 0.0.12 – misc | ||||||
|  | - update to new ci | ||||||
|  |  | ||||||
|  | ## 2017-01-29 - 0.0.11 – misc | ||||||
|  | - now using smartrequest | ||||||
|  |  | ||||||
|  | ## 2017-01-22 - 0.0.10 – misc | ||||||
|  | - now reacting to rate limiting | ||||||
|  |  | ||||||
|  | ## 2016-07-31 - 0.0.9 – misc | ||||||
|  | - update dependencies | ||||||
|  |  | ||||||
|  | ## 2016-06-22 - 0.0.8 to 0.0.7 – no significant changes | ||||||
|  | _No significant changes in these releases._ | ||||||
|  |  | ||||||
|  | ## 2016-06-22 - 0.0.6 – misc | ||||||
|  | - updated dependencies | ||||||
|  |  | ||||||
|  | ## 2016-06-21 - 0.0.5 – misc | ||||||
|  | - fix stages | ||||||
|  |  | ||||||
|  | ## 2016-06-21 - 0.0.4 – misc | ||||||
|  | - fix stages | ||||||
|  |  | ||||||
|  | ## 2016-06-21 - 0.0.3 – misc | ||||||
|  | Multiple improvements: | ||||||
|  | - now works for most things | ||||||
|  | - update to latest dependencies | ||||||
|  | - update .gitlab.yml | ||||||
|  | - update | ||||||
|  | - add .gitlab-ci.yml | ||||||
|  |  | ||||||
|  | ## 2016-05-25 - 0.0.2 – misc | ||||||
|  | Several changes: | ||||||
|  | - improve domain string handling | ||||||
|  | - update .getRecord | ||||||
|  | - improve .createRecord | ||||||
|  | - implemented .createRecord | ||||||
|  | - compile | ||||||
|  | - add functionality | ||||||
|  | - start with tests | ||||||
|  | - improved request method of cflare class | ||||||
|  |  | ||||||
|  | ## 2016-04-27 - 0.0.1 – misc | ||||||
|  | - now returning promises | ||||||
|  | - add lossless badge | ||||||
|  |  | ||||||
|  | ## 2016-04-27 - 0.0.0 – misc | ||||||
|  | - added travis and improved README | ||||||
|  |  | ||||||
|  | ## 2016-04-10 - 0.0.0 – misc | ||||||
|  | - add package.json and README | ||||||
|  |  | ||||||
|  | ## 2016-04-10 - unknown – misc | ||||||
|  | - Initial commit | ||||||
|  |  | ||||||
|  | --- | ||||||
|  | _Note: Versions that only contained version bump commits or minor housekeeping (6.0.5; 2.0.2; 0.0.20; 0.0.8 to 0.0.7) have been omitted from detailed entries and are summarized above._ | ||||||
| @@ -1,8 +1,8 @@ | |||||||
| /** | /** | ||||||
|  * autocreated commitinfo by @pushrocks/commitinfo |  * autocreated commitinfo by @push.rocks/commitinfo | ||||||
|  */ |  */ | ||||||
| export const commitinfo = { | export const commitinfo = { | ||||||
|   name: '@apiclient.xyz/cloudflare', |   name: '@apiclient.xyz/cloudflare', | ||||||
|   version: '6.0.5', |   version: '6.0.6', | ||||||
|   description: 'A TypeScript client for managing Cloudflare accounts, zones, DNS records, and workers with ease.' |   description: 'A TypeScript client for managing Cloudflare accounts, zones, DNS records, and workers with ease.' | ||||||
| } | } | ||||||
|   | |||||||
| @@ -26,6 +26,40 @@ export class CloudflareAccount { | |||||||
|     }); |     }); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   /** | ||||||
|  |    * Make a request to the Cloudflare API | ||||||
|  |    * @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', | ||||||
|  |     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', | ||||||
|  |         }, | ||||||
|  |       }; | ||||||
|  |  | ||||||
|  |       if (data) { | ||||||
|  |         options.json = data; | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       const response = await plugins.smartrequest.request(options); | ||||||
|  |       return JSON.parse(response.body); | ||||||
|  |     } catch (error) { | ||||||
|  |       logger.log('error', `Cloudflare API request failed: ${error.message}`); | ||||||
|  |       throw error; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|   public async preselectAccountByName(nameArg: string) { |   public async preselectAccountByName(nameArg: string) { | ||||||
|     const accounts = await this.convenience.listAccounts(); |     const accounts = await this.convenience.listAccounts(); | ||||||
|     const account = accounts.find((accountArg) => { |     const account = accounts.find((accountArg) => { | ||||||
| @@ -130,7 +164,7 @@ export class CloudflareAccount { | |||||||
|      * cleanrecord allows the cleaning of any previous records to avoid unwanted sideeffects |      * cleanrecord allows the cleaning of any previous records to avoid unwanted sideeffects | ||||||
|      */ |      */ | ||||||
|     cleanRecord: async (domainNameArg: string, typeArg: plugins.tsclass.network.TDnsRecordType) => { |     cleanRecord: async (domainNameArg: string, typeArg: plugins.tsclass.network.TDnsRecordType) => { | ||||||
|       console.log(`cleaning record for ${domainNameArg}`); |       logger.log('info', `Cleaning ${typeArg} records for ${domainNameArg}`); | ||||||
|       const records = await this.convenience.listRecords(domainNameArg); |       const records = await this.convenience.listRecords(domainNameArg); | ||||||
|       const recordsToDelete = records.filter((recordArg) => { |       const recordsToDelete = records.filter((recordArg) => { | ||||||
|         return recordArg.type === typeArg; |         return recordArg.type === typeArg; | ||||||
| @@ -144,17 +178,39 @@ export class CloudflareAccount { | |||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * updates a record |      * updates a record | ||||||
|      * @param domainNameArg |      * @param domainNameArg Domain name for the record | ||||||
|      * @param typeArg |      * @param typeArg Type of DNS record | ||||||
|      * @param valueArg |      * @param contentArg New content for the record | ||||||
|  |      * @param ttlArg Time to live in seconds (optional) | ||||||
|  |      * @returns Updated record | ||||||
|      */ |      */ | ||||||
|     updateRecord: async ( |     updateRecord: async ( | ||||||
|       domainNameArg: string, |       domainNameArg: string, | ||||||
|       typeArg: plugins.tsclass.network.TDnsRecordType, |       typeArg: plugins.tsclass.network.TDnsRecordType, | ||||||
|       valueArg |       contentArg: string, | ||||||
|     ) => { |       ttlArg: number = 1 | ||||||
|       // TODO: implement |     ): Promise<plugins.ICloudflareTypes['Record']> => { | ||||||
|       const domain = new plugins.smartstring.Domain(domainNameArg); |       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`); | ||||||
|  |         return this.convenience.createRecord(domainNameArg, typeArg, contentArg, ttlArg); | ||||||
|  |       } | ||||||
|  |        | ||||||
|  |       // Update the record | ||||||
|  |       const updatedRecord = await this.apiAccount.dns.records.edit(record.id, { | ||||||
|  |         zone_id: zoneId, | ||||||
|  |         type: typeArg as any, | ||||||
|  |         name: domain.fullName, | ||||||
|  |         content: contentArg, | ||||||
|  |         ttl: ttlArg | ||||||
|  |       }); | ||||||
|  |        | ||||||
|  |       return updatedRecord; | ||||||
|     }, |     }, | ||||||
|     /** |     /** | ||||||
|      * list all records of a specified domain name |      * list all records of a specified domain name | ||||||
| @@ -208,4 +264,4 @@ export class CloudflareAccount { | |||||||
|       await this.convenience.removeRecord(dnsChallenge.hostName, 'TXT'); |       await this.convenience.removeRecord(dnsChallenge.hostName, 'TXT'); | ||||||
|     }, |     }, | ||||||
|   }; |   }; | ||||||
| } | } | ||||||
| @@ -1,3 +1,96 @@ | |||||||
| import * as plugins from './cloudflare.plugins.js'; | import * as plugins from './cloudflare.plugins.js'; | ||||||
|  | import { logger } from './cloudflare.logger.js'; | ||||||
|  |  | ||||||
| export class CloudflareRecord {} | export interface ICloudflareRecordInfo { | ||||||
|  |   id: string; | ||||||
|  |   type: plugins.tsclass.network.TDnsRecordType; | ||||||
|  |   name: string; | ||||||
|  |   content: string; | ||||||
|  |   proxiable: boolean; | ||||||
|  |   proxied: boolean; | ||||||
|  |   ttl: number; | ||||||
|  |   locked: boolean; | ||||||
|  |   zone_id: string; | ||||||
|  |   zone_name: string; | ||||||
|  |   created_on: string; | ||||||
|  |   modified_on: string; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | export class CloudflareRecord { | ||||||
|  |   /** | ||||||
|  |    * Create a CloudflareRecord instance from an API object | ||||||
|  |    * @param apiObject Cloudflare DNS record API object | ||||||
|  |    * @returns CloudflareRecord instance | ||||||
|  |    */ | ||||||
|  |   public static createFromApiObject(apiObject: plugins.ICloudflareTypes['Record']): CloudflareRecord { | ||||||
|  |     const record = new CloudflareRecord(); | ||||||
|  |     Object.assign(record, apiObject); | ||||||
|  |     return record; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   // Record properties | ||||||
|  |   public id: string; | ||||||
|  |   public type: plugins.tsclass.network.TDnsRecordType; | ||||||
|  |   public name: string; | ||||||
|  |   public content: string; | ||||||
|  |   public proxiable: boolean; | ||||||
|  |   public proxied: boolean; | ||||||
|  |   public ttl: number; | ||||||
|  |   public locked: boolean; | ||||||
|  |   public zone_id: string; | ||||||
|  |   public zone_name: string; | ||||||
|  |   public created_on: string; | ||||||
|  |   public modified_on: string; | ||||||
|  |  | ||||||
|  |   /** | ||||||
|  |    * Update the record content | ||||||
|  |    * @param cloudflareAccount The Cloudflare account to use | ||||||
|  |    * @param newContent New content for the record | ||||||
|  |    * @param ttl Optional TTL value in seconds | ||||||
|  |    * @returns Updated record | ||||||
|  |    */ | ||||||
|  |   public async update( | ||||||
|  |     cloudflareAccount: any, | ||||||
|  |     newContent: string, | ||||||
|  |     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 | ||||||
|  |     }); | ||||||
|  |      | ||||||
|  |     // Update this instance | ||||||
|  |     this.content = newContent; | ||||||
|  |     if (ttl) { | ||||||
|  |       this.ttl = ttl; | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     return this; | ||||||
|  |   } | ||||||
|  |    | ||||||
|  |   /** | ||||||
|  |    * Delete this record | ||||||
|  |    * @param cloudflareAccount The Cloudflare account to use | ||||||
|  |    * @returns Boolean indicating success | ||||||
|  |    */ | ||||||
|  |   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 | ||||||
|  |       }); | ||||||
|  |        | ||||||
|  |       return true; | ||||||
|  |     } catch (error) { | ||||||
|  |       logger.log('error', `Failed to delete record: ${error.message}`); | ||||||
|  |       return false; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
| @@ -7,6 +7,11 @@ export interface IWorkerRoute extends interfaces.ICflareWorkerRoute { | |||||||
|   zoneName: string; |   zoneName: string; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | export interface IWorkerRouteDefinition { | ||||||
|  |   zoneName: string; | ||||||
|  |   pattern: string; | ||||||
|  | } | ||||||
|  |  | ||||||
| export class CloudflareWorker { | export class CloudflareWorker { | ||||||
|   // STATIC |   // STATIC | ||||||
|   public static async fromApiObject( |   public static async fromApiObject( | ||||||
| @@ -46,9 +51,8 @@ export class CloudflareWorker { | |||||||
|         result: interfaces.ICflareWorkerRoute[]; |         result: interfaces.ICflareWorkerRoute[]; | ||||||
|       } = await this.workerManager.cfAccount.request('GET', requestRoute); |       } = await this.workerManager.cfAccount.request('GET', requestRoute); | ||||||
|       for (const route of response.result) { |       for (const route of response.result) { | ||||||
|         console.log('hey'); |         logger.log('debug', `Processing route: ${route.pattern}`); | ||||||
|         console.log(route); |         logger.log('debug', `Comparing script: ${route.script} with worker ID: ${this.id}`); | ||||||
|         console.log(this.id); |  | ||||||
|         if (route.script === this.id) { |         if (route.script === this.id) { | ||||||
|           this.routes.push({ ...route, zoneName: zone.name }); |           this.routes.push({ ...route, zoneName: zone.name }); | ||||||
|         } |         } | ||||||
| @@ -56,7 +60,11 @@ export class CloudflareWorker { | |||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   public async setRoutes(routeArray: Array<{ zoneName: string; pattern: string }>) { |   /** | ||||||
|  |    * Sets routes for this worker | ||||||
|  |    * @param routeArray Array of route definitions | ||||||
|  |    */ | ||||||
|  |   public async setRoutes(routeArray: IWorkerRouteDefinition[]) { | ||||||
|     for (const newRoute of routeArray) { |     for (const newRoute of routeArray) { | ||||||
|       // lets determine wether a route is new, needs an update or already up to date. |       // lets determine wether a route is new, needs an update or already up to date. | ||||||
|       let routeStatus: 'new' | 'needsUpdate' | 'alreadyUpToDate' = 'new'; |       let routeStatus: 'new' | 'needsUpdate' | 'alreadyUpToDate' = 'new'; | ||||||
| @@ -90,4 +98,4 @@ export class CloudflareWorker { | |||||||
|       } |       } | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| } | } | ||||||
| @@ -1,6 +1,7 @@ | |||||||
| import * as plugins from './cloudflare.plugins.js'; | import * as plugins from './cloudflare.plugins.js'; | ||||||
| import { CloudflareAccount } from './cloudflare.classes.account.js'; | import { CloudflareAccount } from './cloudflare.classes.account.js'; | ||||||
| import { CloudflareWorker } from './cloudflare.classes.worker.js'; | import { CloudflareWorker } from './cloudflare.classes.worker.js'; | ||||||
|  | import { logger } from './cloudflare.logger.js'; | ||||||
|  |  | ||||||
| export class WorkerManager { | export class WorkerManager { | ||||||
|   public cfAccount: CloudflareAccount; |   public cfAccount: CloudflareAccount; | ||||||
| @@ -9,6 +10,12 @@ export class WorkerManager { | |||||||
|     this.cfAccount = cfAccountArg; |     this.cfAccount = cfAccountArg; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   /** | ||||||
|  |    * 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 | ||||||
|  |    */ | ||||||
|   public async createWorker(workerName: string, workerScript: string): Promise<plugins.ICloudflareTypes['Script']> { |   public async createWorker(workerName: string, workerScript: string): Promise<plugins.ICloudflareTypes['Script']> { | ||||||
|     if (!this.cfAccount.preselectedAccountId) { |     if (!this.cfAccount.preselectedAccountId) { | ||||||
|       throw new Error('No account selected. Please select it first on the account.'); |       throw new Error('No account selected. Please select it first on the account.'); | ||||||
| @@ -21,7 +28,30 @@ export class WorkerManager { | |||||||
|   } |   } | ||||||
|  |  | ||||||
|   /** |   /** | ||||||
|    * lists workers |    * Get a worker by name | ||||||
|  |    * @param workerName Name of the worker to retrieve | ||||||
|  |    * @returns CloudflareWorker instance or undefined if not found | ||||||
|  |    */ | ||||||
|  |   public async getWorker(workerName: string): Promise<CloudflareWorker | undefined> { | ||||||
|  |     if (!this.cfAccount.preselectedAccountId) { | ||||||
|  |       throw new Error('No account selected. Please select it first on the account.'); | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     try { | ||||||
|  |       const script = await this.cfAccount.apiAccount.workers.scripts.get(workerName, { | ||||||
|  |         account_id: this.cfAccount.preselectedAccountId | ||||||
|  |       }); | ||||||
|  |        | ||||||
|  |       return CloudflareWorker.fromApiObject(this, script); | ||||||
|  |     } catch (error) { | ||||||
|  |       logger.log('warn', `Worker '${workerName}' not found: ${error.message}`); | ||||||
|  |       return undefined; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   /** | ||||||
|  |    * Lists all worker scripts | ||||||
|  |    * @returns Array of worker scripts | ||||||
|    */ |    */ | ||||||
|   public async listWorkerScripts() { |   public async listWorkerScripts() { | ||||||
|     if (!this.cfAccount.preselectedAccountId) { |     if (!this.cfAccount.preselectedAccountId) { | ||||||
| @@ -35,4 +65,26 @@ export class WorkerManager { | |||||||
|     } |     } | ||||||
|     return workerScripts; |     return workerScripts; | ||||||
|   } |   } | ||||||
| } |    | ||||||
|  |   /** | ||||||
|  |    * Deletes a worker script | ||||||
|  |    * @param workerName Name of the worker to delete | ||||||
|  |    * @returns True if deletion was successful | ||||||
|  |    */ | ||||||
|  |   public async deleteWorker(workerName: string): Promise<boolean> { | ||||||
|  |     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 | ||||||
|  |       }); | ||||||
|  |       logger.log('info', `Worker '${workerName}' deleted successfully`); | ||||||
|  |       return true; | ||||||
|  |     } catch (error) { | ||||||
|  |       logger.log('error', `Failed to delete worker '${workerName}': ${error.message}`); | ||||||
|  |       return false; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
| @@ -1,9 +1,177 @@ | |||||||
| import * as plugins from './cloudflare.plugins.js'; | import * as plugins from './cloudflare.plugins.js'; | ||||||
|  | import { logger } from './cloudflare.logger.js'; | ||||||
|  | import * as interfaces from './interfaces/index.js'; | ||||||
|  |  | ||||||
| export class CloudflareZone { | export class CloudflareZone { | ||||||
|   public static createFromApiObject(apiObject: plugins.ICloudflareTypes['Zone']) { |   // Zone properties | ||||||
|  |   public id: string; | ||||||
|  |   public name: string; | ||||||
|  |   public status: interfaces.ICflareZone['status']; | ||||||
|  |   public paused: boolean; | ||||||
|  |   public type: interfaces.ICflareZone['type']; | ||||||
|  |   public development_mode: number; | ||||||
|  |   public name_servers: string[]; | ||||||
|  |   public original_name_servers: string[]; | ||||||
|  |   public original_registrar: string | null; | ||||||
|  |   public original_dnshost: string | null; | ||||||
|  |   public modified_on: string; | ||||||
|  |   public created_on: string; | ||||||
|  |   public activated_on: string; | ||||||
|  |   public meta: interfaces.ICflareZone['meta']; | ||||||
|  |   public owner: interfaces.ICflareZone['owner']; | ||||||
|  |   public account: interfaces.ICflareZone['account']; | ||||||
|  |   public permissions: string[]; | ||||||
|  |   public plan: interfaces.ICflareZone['plan']; | ||||||
|  |    | ||||||
|  |   private cfAccount: any; // Will be set when created through a manager | ||||||
|  |  | ||||||
|  |   /** | ||||||
|  |    * Create a CloudflareZone instance from an API object | ||||||
|  |    * @param apiObject Cloudflare Zone API object | ||||||
|  |    * @param cfAccount Optional Cloudflare account instance | ||||||
|  |    * @returns CloudflareZone instance | ||||||
|  |    */ | ||||||
|  |   public static createFromApiObject( | ||||||
|  |     apiObject: plugins.ICloudflareTypes['Zone'],  | ||||||
|  |     cfAccount?: any | ||||||
|  |   ): CloudflareZone { | ||||||
|     const cloudflareZone = new CloudflareZone(); |     const cloudflareZone = new CloudflareZone(); | ||||||
|     Object.assign(cloudflareZone, apiObject); |     Object.assign(cloudflareZone, apiObject); | ||||||
|  |      | ||||||
|  |     if (cfAccount) { | ||||||
|  |       cloudflareZone.cfAccount = cfAccount; | ||||||
|  |     } | ||||||
|  |      | ||||||
|     return cloudflareZone; |     return cloudflareZone; | ||||||
|   } |   } | ||||||
| } |    | ||||||
|  |   /** | ||||||
|  |    * Check if development mode is currently active | ||||||
|  |    * @returns True if development mode is active | ||||||
|  |    */ | ||||||
|  |   public isDevelopmentModeActive(): boolean { | ||||||
|  |     return this.development_mode > 0; | ||||||
|  |   } | ||||||
|  |    | ||||||
|  |   /** | ||||||
|  |    * Enable development mode for the zone | ||||||
|  |    * @param cfAccount Cloudflare account to use if not already set | ||||||
|  |    * @param duration Duration in seconds (default: 3 hours) | ||||||
|  |    * @returns Updated zone | ||||||
|  |    */ | ||||||
|  |   public async enableDevelopmentMode( | ||||||
|  |     cfAccount?: any, | ||||||
|  |     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}`); | ||||||
|  |      | ||||||
|  |     const response = await account.request('PATCH', `/zones/${this.id}/settings/development_mode`, { | ||||||
|  |       value: 'on', | ||||||
|  |       time: duration | ||||||
|  |     }); | ||||||
|  |      | ||||||
|  |     this.development_mode = duration; | ||||||
|  |     return this; | ||||||
|  |   } | ||||||
|  |    | ||||||
|  |   /** | ||||||
|  |    * Disable development mode for the zone | ||||||
|  |    * @param cfAccount Cloudflare account to use if not already set | ||||||
|  |    * @returns Updated zone | ||||||
|  |    */ | ||||||
|  |   public async disableDevelopmentMode(cfAccount?: any): Promise<CloudflareZone> { | ||||||
|  |     const account = cfAccount || this.cfAccount; | ||||||
|  |     if (!account) { | ||||||
|  |       throw new Error('CloudflareAccount is required to disable development mode'); | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     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; | ||||||
|  |   } | ||||||
|  |    | ||||||
|  |   /** | ||||||
|  |    * Purge all cached content for this zone | ||||||
|  |    * @param cfAccount Cloudflare account to use if not already set | ||||||
|  |    * @returns True if successful | ||||||
|  |    */ | ||||||
|  |   public async purgeCache(cfAccount?: any): Promise<boolean> { | ||||||
|  |     const account = cfAccount || this.cfAccount; | ||||||
|  |     if (!account) { | ||||||
|  |       throw new Error('CloudflareAccount is required to purge cache'); | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     logger.log('info', `Purging all cache for zone ${this.name}`); | ||||||
|  |      | ||||||
|  |     try { | ||||||
|  |       await account.request('POST', `/zones/${this.id}/purge_cache`, { | ||||||
|  |         purge_everything: true | ||||||
|  |       }); | ||||||
|  |       return true; | ||||||
|  |     } catch (error) { | ||||||
|  |       logger.log('error', `Failed to purge cache: ${error.message}`); | ||||||
|  |       return false; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |    | ||||||
|  |   /** | ||||||
|  |    * Purge specific URLs from the cache | ||||||
|  |    * @param urls Array of URLs to purge | ||||||
|  |    * @param cfAccount Cloudflare account to use if not already set | ||||||
|  |    * @returns True if successful | ||||||
|  |    */ | ||||||
|  |   public async purgeUrls(urls: string[], cfAccount?: any): Promise<boolean> { | ||||||
|  |     const account = cfAccount || this.cfAccount; | ||||||
|  |     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.request('POST', `/zones/${this.id}/purge_cache`, { | ||||||
|  |         files: urls | ||||||
|  |       }); | ||||||
|  |       return true; | ||||||
|  |     } catch (error) { | ||||||
|  |       logger.log('error', `Failed to purge URLs: ${error.message}`); | ||||||
|  |       return false; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |    | ||||||
|  |   /** | ||||||
|  |    * Check if the zone is active | ||||||
|  |    * @returns True if the zone is active | ||||||
|  |    */ | ||||||
|  |   public isActive(): boolean { | ||||||
|  |     return this.status === 'active' && !this.paused; | ||||||
|  |   } | ||||||
|  |    | ||||||
|  |   /** | ||||||
|  |    * Check if the zone is using Cloudflare nameservers | ||||||
|  |    * @returns True if using Cloudflare nameservers | ||||||
|  |    */ | ||||||
|  |   public isUsingCloudflareNameservers(): boolean { | ||||||
|  |     // Check if original nameservers match current nameservers | ||||||
|  |     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')); | ||||||
|  |   } | ||||||
|  | } | ||||||
| @@ -2,31 +2,152 @@ import * as plugins from './cloudflare.plugins.js'; | |||||||
| import * as interfaces from './interfaces/index.js'; | import * as interfaces from './interfaces/index.js'; | ||||||
| import { CloudflareAccount } from './cloudflare.classes.account.js'; | import { CloudflareAccount } from './cloudflare.classes.account.js'; | ||||||
| import { CloudflareZone } from './cloudflare.classes.zone.js'; | import { CloudflareZone } from './cloudflare.classes.zone.js'; | ||||||
|  | import { logger } from './cloudflare.logger.js'; | ||||||
|  |  | ||||||
| export class ZoneManager { | export class ZoneManager { | ||||||
|   public cfAccount: CloudflareAccount; |   public cfAccount: CloudflareAccount; | ||||||
|   public zoneName: string; |  | ||||||
|  |  | ||||||
|   constructor(cfAccountArg: CloudflareAccount) { |   constructor(cfAccountArg: CloudflareAccount) { | ||||||
|     this.cfAccount = cfAccountArg; |     this.cfAccount = cfAccountArg; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   public async getZones(zoneName: string) { |   /** | ||||||
|  |    * Get all zones, optionally filtered by name | ||||||
|  |    * @param zoneName Optional zone name to filter by | ||||||
|  |    * @returns Array of CloudflareZone instances | ||||||
|  |    */ | ||||||
|  |   public async getZones(zoneName?: string): Promise<CloudflareZone[]> { | ||||||
|     let requestRoute = `/zones?per_page=50`; |     let requestRoute = `/zones?per_page=50`; | ||||||
|     // may be optionally filtered by domain name |      | ||||||
|  |     // May be optionally filtered by domain name | ||||||
|     if (zoneName) { |     if (zoneName) { | ||||||
|       requestRoute = `${requestRoute}&name=${zoneName}`; |       requestRoute = `${requestRoute}&name=${encodeURIComponent(zoneName)}`; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     const response: any = await this.cfAccount.request('GET', requestRoute); |     try { | ||||||
|     const apiObjects: interfaces.ICflareZone[] = response.result; |       const response: { result: interfaces.ICflareZone[] } = await this.cfAccount.request('GET', requestRoute); | ||||||
|  |        | ||||||
|     const cloudflareZoneArray = []; |       return response.result.map(apiObject =>  | ||||||
|     for (const apiObject of apiObjects) { |         CloudflareZone.createFromApiObject(apiObject as any, this.cfAccount) | ||||||
|       cloudflareZoneArray.push(CloudflareZone.createFromApiObject(apiObject)); |       ); | ||||||
|  |     } catch (error) { | ||||||
|  |       logger.log('error', `Failed to fetch zones: ${error.message}`); | ||||||
|  |       return []; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     return cloudflareZoneArray; |  | ||||||
|   } |   } | ||||||
| } |    | ||||||
|  |   /** | ||||||
|  |    * Get a single zone by name | ||||||
|  |    * @param zoneName Zone name to find | ||||||
|  |    * @returns CloudflareZone instance or undefined if not found | ||||||
|  |    */ | ||||||
|  |   public async getZoneByName(zoneName: string): Promise<CloudflareZone | undefined> { | ||||||
|  |     const zones = await this.getZones(zoneName); | ||||||
|  |     return zones.find(zone => zone.name === zoneName); | ||||||
|  |   } | ||||||
|  |    | ||||||
|  |   /** | ||||||
|  |    * Get a zone by its ID | ||||||
|  |    * @param zoneId Zone ID to find | ||||||
|  |    * @returns CloudflareZone instance or undefined if not found | ||||||
|  |    */ | ||||||
|  |   public async getZoneById(zoneId: string): Promise<CloudflareZone | undefined> { | ||||||
|  |     try { | ||||||
|  |       const response: { result: interfaces.ICflareZone } = await this.cfAccount.request( | ||||||
|  |         '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 | ||||||
|  |    * @param jumpStart Whether to automatically attempt to fetch existing DNS records | ||||||
|  |    * @param accountId Account ID to use (defaults to preselected account) | ||||||
|  |    * @returns The created zone | ||||||
|  |    */ | ||||||
|  |   public async createZone( | ||||||
|  |     zoneName: string,  | ||||||
|  |     jumpStart: boolean = false, | ||||||
|  |     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}`); | ||||||
|  |        | ||||||
|  |       const response: { result: interfaces.ICflareZone } = await this.cfAccount.request( | ||||||
|  |         'POST',  | ||||||
|  |         '/zones',  | ||||||
|  |         { | ||||||
|  |           name: zoneName, | ||||||
|  |           jump_start: jumpStart, | ||||||
|  |           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 | ||||||
|  |    * @returns True if successful | ||||||
|  |    */ | ||||||
|  |   public async deleteZone(zoneId: string): Promise<boolean> { | ||||||
|  |     try { | ||||||
|  |       logger.log('info', `Deleting zone with ID ${zoneId}`); | ||||||
|  |        | ||||||
|  |       await this.cfAccount.request('DELETE', `/zones/${zoneId}`); | ||||||
|  |       return true; | ||||||
|  |     } catch (error) { | ||||||
|  |       logger.log('error', `Failed to delete zone with ID ${zoneId}: ${error.message}`); | ||||||
|  |       return false; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |    | ||||||
|  |   /** | ||||||
|  |    * Check if a zone exists | ||||||
|  |    * @param zoneName Name of the zone to check | ||||||
|  |    * @returns True if the zone exists | ||||||
|  |    */ | ||||||
|  |   public async zoneExists(zoneName: string): Promise<boolean> { | ||||||
|  |     const zones = await this.getZones(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 | ||||||
|  |    * @returns Updated zone or undefined if activation failed | ||||||
|  |    */ | ||||||
|  |   public async activateZone(zoneId: string): Promise<CloudflareZone | undefined> { | ||||||
|  |     try { | ||||||
|  |       logger.log('info', `Activating zone with ID ${zoneId}`); | ||||||
|  |        | ||||||
|  |       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 activate zone with ID ${zoneId}: ${error.message}`); | ||||||
|  |       return undefined; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										130
									
								
								ts/cloudflare.utils.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										130
									
								
								ts/cloudflare.utils.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,130 @@ | |||||||
|  | import * as plugins from './cloudflare.plugins.js'; | ||||||
|  | import { logger } from './cloudflare.logger.js'; | ||||||
|  |  | ||||||
|  | export class CloudflareUtils { | ||||||
|  |   /** | ||||||
|  |    * Validates if a domain name is properly formatted | ||||||
|  |    * @param domainName Domain name to validate | ||||||
|  |    * @returns True if the domain is valid | ||||||
|  |    */ | ||||||
|  |   public static isValidDomain(domainName: string): boolean { | ||||||
|  |     try { | ||||||
|  |       const domain = new plugins.smartstring.Domain(domainName); | ||||||
|  |       return domain.isValid(); | ||||||
|  |     } catch (error) { | ||||||
|  |       return false; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |    | ||||||
|  |   /** | ||||||
|  |    * Extracts the zone name (apex domain) from a full domain | ||||||
|  |    * @param domainName Domain name to process | ||||||
|  |    * @returns Zone name (apex domain) | ||||||
|  |    */ | ||||||
|  |   public static getZoneName(domainName: string): string { | ||||||
|  |     try { | ||||||
|  |       const domain = new plugins.smartstring.Domain(domainName); | ||||||
|  |       return domain.zoneName; | ||||||
|  |     } catch (error) { | ||||||
|  |       logger.log('error', `Invalid domain name: ${domainName}`); | ||||||
|  |       throw new Error(`Invalid domain name: ${domainName}`); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |    | ||||||
|  |   /** | ||||||
|  |    * Checks if a string is a valid Cloudflare API token | ||||||
|  |    * @param token API token to validate | ||||||
|  |    * @returns True if the token format is valid | ||||||
|  |    */ | ||||||
|  |   public static isValidApiToken(token: string): boolean { | ||||||
|  |     // 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 | ||||||
|  |    * @returns True if it's a valid DNS record type | ||||||
|  |    */ | ||||||
|  |   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', | ||||||
|  |       'SSHFP', 'TLSA', 'URI' | ||||||
|  |     ]; | ||||||
|  |     return validTypes.includes(type as any); | ||||||
|  |   } | ||||||
|  |    | ||||||
|  |   /** | ||||||
|  |    * Formats a URL for cache purging (ensures it starts with http/https) | ||||||
|  |    * @param url URL to format | ||||||
|  |    * @returns Properly formatted URL | ||||||
|  |    */ | ||||||
|  |   public static formatUrlForPurge(url: string): string { | ||||||
|  |     if (!url.startsWith('http://') && !url.startsWith('https://')) { | ||||||
|  |       return `https://${url}`; | ||||||
|  |     } | ||||||
|  |     return url; | ||||||
|  |   } | ||||||
|  |    | ||||||
|  |   /** | ||||||
|  |    * Converts a TTL value in seconds to a human-readable string | ||||||
|  |    * @param ttl TTL in seconds | ||||||
|  |    * @returns Human-readable TTL | ||||||
|  |    */ | ||||||
|  |   public static formatTtl(ttl: number): string { | ||||||
|  |     if (ttl === 1) { | ||||||
|  |       return 'Automatic'; | ||||||
|  |     } else if (ttl === 120) { | ||||||
|  |       return '2 minutes'; | ||||||
|  |     } else if (ttl === 300) { | ||||||
|  |       return '5 minutes'; | ||||||
|  |     } else if (ttl === 600) { | ||||||
|  |       return '10 minutes'; | ||||||
|  |     } else if (ttl === 900) { | ||||||
|  |       return '15 minutes'; | ||||||
|  |     } else if (ttl === 1800) { | ||||||
|  |       return '30 minutes'; | ||||||
|  |     } else if (ttl === 3600) { | ||||||
|  |       return '1 hour'; | ||||||
|  |     } else if (ttl === 7200) { | ||||||
|  |       return '2 hours'; | ||||||
|  |     } else if (ttl === 18000) { | ||||||
|  |       return '5 hours'; | ||||||
|  |     } else if (ttl === 43200) { | ||||||
|  |       return '12 hours'; | ||||||
|  |     } else if (ttl === 86400) { | ||||||
|  |       return '1 day'; | ||||||
|  |     } else { | ||||||
|  |       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 } }> | ||||||
|  |   ): 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); | ||||||
|  |         allResults.push(...response.result); | ||||||
|  |         totalPages = response.result_info.total_pages; | ||||||
|  |         page++; | ||||||
|  |       } catch (error) { | ||||||
|  |         logger.log('error', `Pagination error on page ${page}: ${error.message}`); | ||||||
|  |         break; | ||||||
|  |       } | ||||||
|  |     } while (page <= totalPages); | ||||||
|  |      | ||||||
|  |     return allResults; | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										11
									
								
								ts/index.ts
									
									
									
									
									
								
							
							
						
						
									
										11
									
								
								ts/index.ts
									
									
									
									
									
								
							| @@ -1,2 +1,11 @@ | |||||||
| export { CloudflareAccount } from './cloudflare.classes.account.js'; | export { CloudflareAccount } from './cloudflare.classes.account.js'; | ||||||
| export { CloudflareWorker } 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'; | ||||||
|  | export { ZoneManager } from './cloudflare.classes.zonemanager.js'; | ||||||
|  | export { CloudflareUtils } from './cloudflare.utils.js'; | ||||||
|  | export { commitinfo } from './00_commitinfo_data.js'; | ||||||
|  |  | ||||||
|  | // Re-export interfaces | ||||||
|  | export * from './interfaces/index.js'; | ||||||
							
								
								
									
										45
									
								
								ts/interfaces/cloudflare.api.zone.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								ts/interfaces/cloudflare.api.zone.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,45 @@ | |||||||
|  | export interface ICflareZone { | ||||||
|  |   id: string; | ||||||
|  |   name: string; | ||||||
|  |   status: 'active' | 'pending' | 'initializing' | 'moved' | 'deleted' | 'deactivated'; | ||||||
|  |   paused: boolean; | ||||||
|  |   type: 'full' | 'partial' | 'secondary'; | ||||||
|  |   development_mode: number; | ||||||
|  |   name_servers: string[]; | ||||||
|  |   original_name_servers: string[]; | ||||||
|  |   original_registrar: string | null; | ||||||
|  |   original_dnshost: string | null; | ||||||
|  |   modified_on: string; | ||||||
|  |   created_on: string; | ||||||
|  |   activated_on: string; | ||||||
|  |   meta: { | ||||||
|  |     step: number; | ||||||
|  |     wildcard_proxiable: boolean; | ||||||
|  |     custom_certificate_quota: number; | ||||||
|  |     page_rule_quota: number; | ||||||
|  |     phishing_detected: boolean; | ||||||
|  |     multiple_railguns_allowed: boolean; | ||||||
|  |   }; | ||||||
|  |   owner: { | ||||||
|  |     id: string | null; | ||||||
|  |     type: 'user' | 'organization'; | ||||||
|  |     email: string | null; | ||||||
|  |   }; | ||||||
|  |   account: { | ||||||
|  |     id: string; | ||||||
|  |     name: string; | ||||||
|  |   }; | ||||||
|  |   permissions: string[]; | ||||||
|  |   plan: { | ||||||
|  |     id: string; | ||||||
|  |     name: string; | ||||||
|  |     price: number; | ||||||
|  |     currency: string; | ||||||
|  |     frequency: string; | ||||||
|  |     is_subscribed: boolean; | ||||||
|  |     can_subscribe: boolean; | ||||||
|  |     legacy_id: string; | ||||||
|  |     legacy_discount: boolean; | ||||||
|  |     externally_managed: boolean; | ||||||
|  |   }; | ||||||
|  | } | ||||||
| @@ -1,2 +1,3 @@ | |||||||
| export * from './cloudflare.api.account.js'; | export * from './cloudflare.api.account.js'; | ||||||
| export * from './cloudflare.api.workerroute.js'; | export * from './cloudflare.api.workerroute.js'; | ||||||
|  | export * from './cloudflare.api.zone.js'; | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user