fix(core): Improve logging consistency, record update functionality, and API error handling in Cloudflare modules
This commit is contained in:
parent
4600749442
commit
ba49c42dd8
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';
|
||||||
|
Loading…
x
Reference in New Issue
Block a user