BREAKING CHANGE(core): Introduce RecordManager and ConvenientDnsProvider; rename list/get methods for consistent API and deprecate convenience namespace
This commit is contained in:
12
changelog.md
12
changelog.md
@@ -1,5 +1,17 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## 2025-11-18 - 7.0.0 - BREAKING CHANGE(core)
|
||||||
|
Introduce RecordManager and ConvenientDnsProvider; rename list/get methods for consistent API and deprecate convenience namespace
|
||||||
|
|
||||||
|
- Add RecordManager with listRecords, getRecord, createRecord, updateRecord, deleteRecord and cleanRecords to centralize DNS record operations
|
||||||
|
- Add ConvenientDnsProvider adapter and CloudflareAccount.getConvenientDnsProvider() to provide IConvenientDnsProvider compatibility for third-party modules
|
||||||
|
- Rename methods to consistent list* naming: worker.getRoutes -> worker.listRoutes, WorkerManager.listWorkerScripts -> WorkerManager.listWorkers, ZoneManager.getZones -> ZoneManager.listZones, convenience.listRecords -> recordManager.listRecords
|
||||||
|
- Add ZoneManager.getZoneId() and ZoneManager.purgeZone() (zone cache purge helper)
|
||||||
|
- Deprecate the legacy convenience.* methods (getZoneId, getRecord, createRecord, removeRecord, cleanRecord, updateRecord, listRecords, listZones, isDomainSupported, purgeZone, acmeSetDnsChallenge, acmeRemoveDnsChallenge) — kept for backward compatibility but marked deprecated
|
||||||
|
- Export RecordManager and ConvenientDnsProvider from ts/index.ts and expose cfAccount.recordManager on CloudflareAccount
|
||||||
|
- Update tests to use new method names (listWorkers) and extend test runner timeout; package.json test script updated
|
||||||
|
- Documentation (readme) updated to describe the new manager-based API and migration guide; prepares project for major version 7.0.0
|
||||||
|
|
||||||
## 2025-11-17 - 6.4.3 - fix(cloudflare.plugins)
|
## 2025-11-17 - 6.4.3 - fix(cloudflare.plugins)
|
||||||
Switch to smartrequest namespace export and improve request typing and JSON parsing
|
Switch to smartrequest namespace export and improve request typing and JSON parsing
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
"typings": "dist_ts/index.d.ts",
|
"typings": "dist_ts/index.d.ts",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "(tstest test/)",
|
"test": "(tstest test/ --verbose --timeout 600)",
|
||||||
"build": "(tsbuild --web --allowimplicitany)",
|
"build": "(tsbuild --web --allowimplicitany)",
|
||||||
"buildDocs": "tsdoc",
|
"buildDocs": "tsdoc",
|
||||||
"updateOpenapi": "openapi-typescript https://raw.githubusercontent.com/cloudflare/api-schemas/main/openapi.yaml --output ts/openapi.spec.ts"
|
"updateOpenapi": "openapi-typescript https://raw.githubusercontent.com/cloudflare/api-schemas/main/openapi.yaml --output ts/openapi.spec.ts"
|
||||||
|
|||||||
213
readme.md
213
readme.md
@@ -8,10 +8,10 @@ An elegant, class-based TypeScript client for the Cloudflare API that makes mana
|
|||||||
## Features
|
## Features
|
||||||
|
|
||||||
- **Comprehensive coverage** of the Cloudflare API including zones, DNS records, and Workers
|
- **Comprehensive coverage** of the Cloudflare API including zones, DNS records, and Workers
|
||||||
- **Class-based design** with intuitive methods for all Cloudflare operations
|
- **Clean manager-based architecture** with intuitive methods for all Cloudflare operations
|
||||||
- **Strong TypeScript typing** for excellent IDE autocompletion and type safety
|
- **Strong TypeScript typing** for excellent IDE autocompletion and type safety
|
||||||
- **Fully integrated with the official Cloudflare client** using modern async iterators
|
- **Fully integrated with the official Cloudflare client** using modern async iterators
|
||||||
- **Convenience methods** for common operations to reduce boilerplate code
|
- **IConvenientDnsProvider compatibility** for seamless integration with third-party modules
|
||||||
- **Promise-based API** for easy async/await usage
|
- **Promise-based API** for easy async/await usage
|
||||||
- **ESM compatible** for modern JavaScript projects
|
- **ESM compatible** for modern JavaScript projects
|
||||||
- **Comprehensive error handling** for robust applications
|
- **Comprehensive error handling** for robust applications
|
||||||
@@ -37,12 +37,13 @@ import * as cflare from '@apiclient.xyz/cloudflare';
|
|||||||
// Initialize with your API token
|
// Initialize with your API token
|
||||||
const cfAccount = new cflare.CloudflareAccount('your-cloudflare-api-token');
|
const cfAccount = new cflare.CloudflareAccount('your-cloudflare-api-token');
|
||||||
|
|
||||||
// Use convenience methods for quick operations
|
// Use the clean manager-based API
|
||||||
await cfAccount.convenience.createRecord('subdomain.example.com', 'A', '192.0.2.1', 3600);
|
await cfAccount.recordManager.createRecord('subdomain.example.com', 'A', '192.0.2.1', 3600);
|
||||||
|
await cfAccount.zoneManager.purgeZone('example.com');
|
||||||
|
|
||||||
// Or work with the powerful class-based API
|
// Or use the IConvenientDnsProvider interface for third-party modules
|
||||||
const zone = await cfAccount.zoneManager.getZoneByName('example.com');
|
const dnsProvider = cfAccount.getConvenientDnsProvider();
|
||||||
await zone.purgeCache();
|
await dnsProvider.createRecord('subdomain.example.com', 'A', '192.0.2.1');
|
||||||
```
|
```
|
||||||
|
|
||||||
## Usage Guide
|
## Usage Guide
|
||||||
@@ -68,20 +69,20 @@ const myAccounts = await cfAccount.listAccounts();
|
|||||||
Zones represent your domains in Cloudflare.
|
Zones represent your domains in Cloudflare.
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// Get all zones in your account
|
// List all zones in your account
|
||||||
const allZones = await cfAccount.convenience.listZones();
|
const allZones = await cfAccount.zoneManager.listZones();
|
||||||
|
|
||||||
// Get a specific zone by domain name
|
// Get a specific zone by domain name
|
||||||
const myZone = await cfAccount.zoneManager.getZoneByName('example.com');
|
const myZone = await cfAccount.zoneManager.getZoneByName('example.com');
|
||||||
|
|
||||||
// Get zone ID directly
|
// Get zone ID directly
|
||||||
const zoneId = await cfAccount.convenience.getZoneId('example.com');
|
const zoneId = await cfAccount.zoneManager.getZoneId('example.com');
|
||||||
|
|
||||||
// Create a new zone
|
// Create a new zone
|
||||||
const newZone = await cfAccount.zoneManager.createZone('newdomain.com');
|
const newZone = await cfAccount.zoneManager.createZone('newdomain.com');
|
||||||
|
|
||||||
// Purge cache for an entire zone
|
// Purge cache for an entire zone
|
||||||
await cfAccount.convenience.purgeZone('example.com');
|
await cfAccount.zoneManager.purgeZone('example.com');
|
||||||
// Or using the zone object
|
// Or using the zone object
|
||||||
await myZone.purgeCache();
|
await myZone.purgeCache();
|
||||||
|
|
||||||
@@ -99,36 +100,40 @@ const usingCfNameservers = await myZone.isUsingCloudflareNameservers();
|
|||||||
|
|
||||||
### DNS Record Management
|
### DNS Record Management
|
||||||
|
|
||||||
Manage DNS records for your domains with ease.
|
Manage DNS records for your domains with ease using the RecordManager.
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// List all DNS records for a domain
|
// List all DNS records for a domain
|
||||||
const allRecords = await cfAccount.convenience.listRecords('example.com');
|
const allRecords = await cfAccount.recordManager.listRecords('example.com');
|
||||||
|
|
||||||
// Create a new DNS record
|
// Create a new DNS record
|
||||||
await cfAccount.convenience.createRecord('api.example.com', 'A', '192.0.2.1', 3600);
|
await cfAccount.recordManager.createRecord('api.example.com', 'A', '192.0.2.1', 3600);
|
||||||
|
|
||||||
// Create a CNAME record
|
// Create a CNAME record
|
||||||
await cfAccount.convenience.createRecord('www.example.com', 'CNAME', 'example.com', 3600);
|
await cfAccount.recordManager.createRecord('www.example.com', 'CNAME', 'example.com', 3600);
|
||||||
|
|
||||||
// Get a specific DNS record
|
// Get a specific DNS record
|
||||||
const record = await cfAccount.convenience.getRecord('api.example.com', 'A');
|
const record = await cfAccount.recordManager.getRecord('api.example.com', 'A');
|
||||||
|
|
||||||
// Update a DNS record (automatically creates it if it doesn't exist)
|
// Update a DNS record (automatically creates it if it doesn't exist)
|
||||||
await cfAccount.convenience.updateRecord('api.example.com', 'A', '192.0.2.2', 3600);
|
await cfAccount.recordManager.updateRecord('api.example.com', 'A', '192.0.2.2', 3600);
|
||||||
|
|
||||||
// Remove a specific DNS record
|
// Delete a specific DNS record
|
||||||
await cfAccount.convenience.removeRecord('api.example.com', 'A');
|
await cfAccount.recordManager.deleteRecord('api.example.com', 'A');
|
||||||
|
|
||||||
// Clean (remove) all records of a specific type
|
// Clean (remove) all records of a specific type for a domain
|
||||||
await cfAccount.convenience.cleanRecord('example.com', 'TXT');
|
await cfAccount.recordManager.cleanRecords('example.com', 'TXT');
|
||||||
|
|
||||||
|
// For third-party modules requiring IConvenientDnsProvider interface
|
||||||
|
const dnsProvider = cfAccount.getConvenientDnsProvider();
|
||||||
|
await dnsProvider.createRecord('api.example.com', 'A', '192.0.2.1');
|
||||||
|
|
||||||
// Support for ACME DNS challenges (for certificate issuance)
|
// Support for ACME DNS challenges (for certificate issuance)
|
||||||
await cfAccount.convenience.acmeSetDnsChallenge({
|
await dnsProvider.acmeSetDnsChallenge({
|
||||||
hostName: '_acme-challenge.example.com',
|
hostName: '_acme-challenge.example.com',
|
||||||
challenge: 'token-validation-string',
|
challenge: 'token-validation-string',
|
||||||
});
|
});
|
||||||
await cfAccount.convenience.acmeRemoveDnsChallenge({
|
await dnsProvider.acmeRemoveDnsChallenge({
|
||||||
hostName: '_acme-challenge.example.com',
|
hostName: '_acme-challenge.example.com',
|
||||||
challenge: 'token-validation-string',
|
challenge: 'token-validation-string',
|
||||||
});
|
});
|
||||||
@@ -148,7 +153,7 @@ addEventListener('fetch', event => {
|
|||||||
const worker = await cfAccount.workerManager.createWorker('my-worker', workerScript);
|
const worker = await cfAccount.workerManager.createWorker('my-worker', workerScript);
|
||||||
|
|
||||||
// List all workers
|
// List all workers
|
||||||
const allWorkers = await cfAccount.workerManager.listWorkerScripts();
|
const allWorkers = await cfAccount.workerManager.listWorkers();
|
||||||
|
|
||||||
// Get an existing worker
|
// Get an existing worker
|
||||||
const existingWorker = await cfAccount.workerManager.getWorker('my-worker');
|
const existingWorker = await cfAccount.workerManager.getWorker('my-worker');
|
||||||
@@ -165,8 +170,8 @@ await worker.setRoutes([
|
|||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// Get all routes for a worker
|
// List all routes for a worker
|
||||||
const routes = await worker.getRoutes();
|
const routes = await worker.listRoutes();
|
||||||
|
|
||||||
// Update a worker's script
|
// Update a worker's script
|
||||||
await worker.updateScript(`
|
await worker.updateScript(`
|
||||||
@@ -200,9 +205,9 @@ async function manageCloudflare() {
|
|||||||
console.log(`Zone active: ${await myZone.isActive()}`);
|
console.log(`Zone active: ${await myZone.isActive()}`);
|
||||||
console.log(`Using CF nameservers: ${await myZone.isUsingCloudflareNameservers()}`);
|
console.log(`Using CF nameservers: ${await myZone.isUsingCloudflareNameservers()}`);
|
||||||
|
|
||||||
// Configure DNS
|
// Configure DNS using RecordManager
|
||||||
await cfAccount.convenience.createRecord('api.example.com', 'A', '192.0.2.1');
|
await cfAccount.recordManager.createRecord('api.example.com', 'A', '192.0.2.1');
|
||||||
await cfAccount.convenience.createRecord('www.example.com', 'CNAME', 'example.com');
|
await cfAccount.recordManager.createRecord('www.example.com', 'CNAME', 'example.com');
|
||||||
|
|
||||||
// Create a worker and set up routes
|
// Create a worker and set up routes
|
||||||
const workerCode = `
|
const workerCode = `
|
||||||
@@ -247,42 +252,78 @@ class CloudflareAccount {
|
|||||||
async listAccounts(): Promise<Array<ICloudflareTypes['Account']>>;
|
async listAccounts(): Promise<Array<ICloudflareTypes['Account']>>;
|
||||||
async preselectAccountByName(accountName: string): Promise<void>;
|
async preselectAccountByName(accountName: string): Promise<void>;
|
||||||
|
|
||||||
// Managers
|
// Managers - Clean, logical API
|
||||||
readonly zoneManager: ZoneManager;
|
readonly zoneManager: ZoneManager;
|
||||||
readonly workerManager: WorkerManager;
|
readonly workerManager: WorkerManager;
|
||||||
|
readonly recordManager: RecordManager;
|
||||||
|
|
||||||
|
// Get IConvenientDnsProvider adapter for third-party modules
|
||||||
|
getConvenientDnsProvider(): ConvenientDnsProvider;
|
||||||
|
|
||||||
// Official Cloudflare client
|
// Official Cloudflare client
|
||||||
readonly apiAccount: cloudflare.Cloudflare;
|
readonly apiAccount: cloudflare.Cloudflare;
|
||||||
|
|
||||||
// Convenience namespace with helper methods
|
// ⚠️ Deprecated: convenience namespace (kept for backward compatibility)
|
||||||
readonly convenience: {
|
// Use the managers instead: recordManager, zoneManager, workerManager
|
||||||
// Zone operations
|
readonly convenience: { /* deprecated methods */ };
|
||||||
listZones(domainName?: string): Promise<CloudflareZone[]>;
|
}
|
||||||
getZoneId(domainName: string): Promise<string>;
|
```
|
||||||
purgeZone(domainName: string): Promise<void>;
|
|
||||||
|
|
||||||
// DNS operations
|
### RecordManager
|
||||||
listRecords(domainName: string): Promise<CloudflareRecord[]>;
|
|
||||||
getRecord(domainName: string, recordType: string): Promise<CloudflareRecord | undefined>;
|
|
||||||
createRecord(
|
|
||||||
domainName: string,
|
|
||||||
recordType: string,
|
|
||||||
content: string,
|
|
||||||
ttl?: number,
|
|
||||||
): Promise<any>;
|
|
||||||
updateRecord(
|
|
||||||
domainName: string,
|
|
||||||
recordType: string,
|
|
||||||
content: string,
|
|
||||||
ttl?: number,
|
|
||||||
): Promise<any>;
|
|
||||||
removeRecord(domainName: string, recordType: string): Promise<any>;
|
|
||||||
cleanRecord(domainName: string, recordType: string): Promise<void>;
|
|
||||||
|
|
||||||
// ACME operations
|
Clean DNS record management (recommended over deprecated convenience methods).
|
||||||
acmeSetDnsChallenge(dnsChallenge: IDnsChallenge): Promise<any>;
|
|
||||||
acmeRemoveDnsChallenge(dnsChallenge: IDnsChallenge): Promise<any>;
|
```typescript
|
||||||
};
|
class RecordManager {
|
||||||
|
async listRecords(domainName: string): Promise<CloudflareRecord[]>;
|
||||||
|
async getRecord(domainName: string, recordType: string): Promise<CloudflareRecord | undefined>;
|
||||||
|
async createRecord(domainName: string, recordType: string, content: string, ttl?: number): Promise<CloudflareRecord>;
|
||||||
|
async updateRecord(domainName: string, recordType: string, content: string, ttl?: number): Promise<CloudflareRecord>;
|
||||||
|
async deleteRecord(domainName: string, recordType: string): Promise<void>;
|
||||||
|
async cleanRecords(domainName: string, recordType: string): Promise<void>;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### ZoneManager
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
class ZoneManager {
|
||||||
|
async listZones(zoneName?: string): Promise<CloudflareZone[]>;
|
||||||
|
async getZoneById(zoneId: string): Promise<CloudflareZone | undefined>;
|
||||||
|
async getZoneByName(zoneName: string): Promise<CloudflareZone | undefined>;
|
||||||
|
async getZoneId(domainName: string): Promise<string>;
|
||||||
|
async createZone(zoneName: string): Promise<CloudflareZone | undefined>;
|
||||||
|
async deleteZone(zoneId: string): Promise<boolean>;
|
||||||
|
async purgeZone(domainName: string): Promise<void>;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### WorkerManager
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
class WorkerManager {
|
||||||
|
async listWorkers(): Promise<Array<ICloudflareTypes['Script']>>;
|
||||||
|
async getWorker(workerName: string): Promise<CloudflareWorker | undefined>;
|
||||||
|
async createWorker(workerName: string, workerScript: string): Promise<CloudflareWorker>;
|
||||||
|
async deleteWorker(workerName: string): Promise<boolean>;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### ConvenientDnsProvider
|
||||||
|
|
||||||
|
Adapter for third-party modules requiring `IConvenientDnsProvider` interface.
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
class ConvenientDnsProvider implements IConvenientDnsProvider {
|
||||||
|
async createRecord(domainName: string, recordType: string, content: string, ttl?: number): Promise<any>;
|
||||||
|
async updateRecord(domainName: string, recordType: string, content: string, ttl?: number): Promise<any>;
|
||||||
|
async removeRecord(domainName: string, recordType: string): Promise<any>;
|
||||||
|
async getRecord(domainName: string, recordType: string): Promise<any | undefined>;
|
||||||
|
async listRecords(domainName: string): Promise<any[]>;
|
||||||
|
async cleanRecord(domainName: string, recordType: string): Promise<void>;
|
||||||
|
async isDomainSupported(domainName: string): Promise<boolean>;
|
||||||
|
async acmeSetDnsChallenge(dnsChallenge: IDnsChallenge): Promise<void>;
|
||||||
|
async acmeRemoveDnsChallenge(dnsChallenge: IDnsChallenge): Promise<void>;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -343,7 +384,7 @@ class CloudflareWorker {
|
|||||||
readonly routes: IWorkerRoute[];
|
readonly routes: IWorkerRoute[];
|
||||||
|
|
||||||
// Methods
|
// Methods
|
||||||
async getRoutes(): Promise<IWorkerRoute[]>;
|
async listRoutes(): Promise<void>; // Populates the routes property
|
||||||
async setRoutes(routes: Array<IWorkerRouteDefinition>): Promise<void>;
|
async setRoutes(routes: Array<IWorkerRouteDefinition>): Promise<void>;
|
||||||
async updateScript(scriptContent: string): Promise<CloudflareWorker>;
|
async updateScript(scriptContent: string): Promise<CloudflareWorker>;
|
||||||
async delete(): Promise<boolean>;
|
async delete(): Promise<boolean>;
|
||||||
@@ -376,13 +417,57 @@ CloudflareUtils.formatUrlForPurge('example.com/page'); // 'https://example.com/p
|
|||||||
CloudflareUtils.formatTtl(3600); // '1 hour'
|
CloudflareUtils.formatTtl(3600); // '1 hour'
|
||||||
```
|
```
|
||||||
|
|
||||||
## What's New in 6.2.0
|
## What's New in 7.0.0
|
||||||
|
|
||||||
- **Improved async iterator support**: Fully leverages the official Cloudflare client's async iterator pattern
|
- **🎨 Clean Manager-Based Architecture**: New RecordManager, improved ZoneManager and WorkerManager with consistent naming
|
||||||
- **Enhanced error handling**: Better error detection and recovery
|
- **🔌 IConvenientDnsProvider Compatibility**: New ConvenientDnsProvider adapter for seamless third-party module integration
|
||||||
- **Simplified API**: More consistent method signatures and return types
|
- **📝 Consistent Method Naming**:
|
||||||
- **Better type safety**: Improved TypeScript typing throughout the library
|
- `listZones()`, `listWorkers()`, `listRecords()` - consistent list* pattern
|
||||||
- **Detailed logging**: More informative logging for easier debugging
|
- `deleteRecord()` instead of `removeRecord()` - clearer semantics
|
||||||
|
- `listRoutes()` instead of `getRoutes()` - consistent with other list methods
|
||||||
|
- **⚠️ Deprecated convenience Namespace**: Old methods still work but are deprecated - use managers instead
|
||||||
|
- **✅ Backward Compatible**: All existing code continues to work with deprecation warnings
|
||||||
|
|
||||||
|
## Migration Guide (6.x → 7.0)
|
||||||
|
|
||||||
|
### DNS Record Operations
|
||||||
|
```typescript
|
||||||
|
// Old (deprecated):
|
||||||
|
await cfAccount.convenience.createRecord('example.com', 'A', '1.2.3.4');
|
||||||
|
await cfAccount.convenience.listRecords('example.com');
|
||||||
|
await cfAccount.convenience.removeRecord('example.com', 'A');
|
||||||
|
|
||||||
|
// New (recommended):
|
||||||
|
await cfAccount.recordManager.createRecord('example.com', 'A', '1.2.3.4');
|
||||||
|
await cfAccount.recordManager.listRecords('example.com');
|
||||||
|
await cfAccount.recordManager.deleteRecord('example.com', 'A');
|
||||||
|
|
||||||
|
// For third-party modules:
|
||||||
|
const dnsProvider = cfAccount.getConvenientDnsProvider();
|
||||||
|
await dnsProvider.createRecord('example.com', 'A', '1.2.3.4');
|
||||||
|
```
|
||||||
|
|
||||||
|
### Zone Operations
|
||||||
|
```typescript
|
||||||
|
// Old (deprecated):
|
||||||
|
await cfAccount.convenience.listZones();
|
||||||
|
await cfAccount.convenience.purgeZone('example.com');
|
||||||
|
|
||||||
|
// New (recommended):
|
||||||
|
await cfAccount.zoneManager.listZones();
|
||||||
|
await cfAccount.zoneManager.purgeZone('example.com');
|
||||||
|
```
|
||||||
|
|
||||||
|
### Worker Operations
|
||||||
|
```typescript
|
||||||
|
// Old:
|
||||||
|
await cfAccount.workerManager.listWorkerScripts();
|
||||||
|
await worker.getRoutes();
|
||||||
|
|
||||||
|
// New:
|
||||||
|
await cfAccount.workerManager.listWorkers();
|
||||||
|
await worker.listRoutes();
|
||||||
|
```
|
||||||
|
|
||||||
## Development & Testing
|
## Development & Testing
|
||||||
|
|
||||||
|
|||||||
@@ -236,7 +236,7 @@ tap.test('should list workers', async (tools) => {
|
|||||||
tools.timeout(600000);
|
tools.timeout(600000);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const workerArray = await testCloudflareAccount.workerManager.listWorkerScripts();
|
const workerArray = await testCloudflareAccount.workerManager.listWorkers();
|
||||||
expect(workerArray).toBeTypeOf('array');
|
expect(workerArray).toBeTypeOf('array');
|
||||||
console.log(`Found ${workerArray.length} workers in account`);
|
console.log(`Found ${workerArray.length} workers in account`);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
@@ -3,6 +3,6 @@
|
|||||||
*/
|
*/
|
||||||
export const commitinfo = {
|
export const commitinfo = {
|
||||||
name: '@apiclient.xyz/cloudflare',
|
name: '@apiclient.xyz/cloudflare',
|
||||||
version: '6.4.3',
|
version: '7.0.0',
|
||||||
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.'
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ import * as interfaces from './interfaces/index.js';
|
|||||||
// interfaces
|
// interfaces
|
||||||
import { WorkerManager } from './cloudflare.classes.workermanager.js';
|
import { WorkerManager } from './cloudflare.classes.workermanager.js';
|
||||||
import { ZoneManager } from './cloudflare.classes.zonemanager.js';
|
import { ZoneManager } from './cloudflare.classes.zonemanager.js';
|
||||||
|
import { RecordManager } from './cloudflare.classes.recordmanager.js';
|
||||||
|
import { ConvenientDnsProvider } from './cloudflare.classes.convenientdnsprovider.js';
|
||||||
|
|
||||||
export class CloudflareAccount implements plugins.tsclass.network.IConvenientDnsProvider {
|
export class CloudflareAccount implements plugins.tsclass.network.IConvenientDnsProvider {
|
||||||
private authToken: string;
|
private authToken: string;
|
||||||
@@ -12,6 +14,7 @@ export class CloudflareAccount implements plugins.tsclass.network.IConvenientDns
|
|||||||
|
|
||||||
public workerManager = new WorkerManager(this);
|
public workerManager = new WorkerManager(this);
|
||||||
public zoneManager = new ZoneManager(this);
|
public zoneManager = new ZoneManager(this);
|
||||||
|
public recordManager = new RecordManager(this);
|
||||||
|
|
||||||
public apiAccount: plugins.cloudflare.Cloudflare;
|
public apiAccount: plugins.cloudflare.Cloudflare;
|
||||||
|
|
||||||
@@ -133,6 +136,16 @@ export class CloudflareAccount implements plugins.tsclass.network.IConvenientDns
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a ConvenientDnsProvider instance that implements IConvenientDnsProvider
|
||||||
|
* This allows third-party modules to use the standard DNS provider interface
|
||||||
|
* while internally delegating to the clean RecordManager and ZoneManager structure
|
||||||
|
* @returns ConvenientDnsProvider instance
|
||||||
|
*/
|
||||||
|
public getConvenientDnsProvider(): ConvenientDnsProvider {
|
||||||
|
return new ConvenientDnsProvider(this);
|
||||||
|
}
|
||||||
|
|
||||||
public convenience = {
|
public convenience = {
|
||||||
/**
|
/**
|
||||||
* Lists all accounts accessible with the current API token
|
* Lists all accounts accessible with the current API token
|
||||||
@@ -157,6 +170,7 @@ export class CloudflareAccount implements plugins.tsclass.network.IConvenientDns
|
|||||||
/**
|
/**
|
||||||
* gets a zone id of a domain from cloudflare
|
* gets a zone id of a domain from cloudflare
|
||||||
* @param domainName
|
* @param domainName
|
||||||
|
* @deprecated Use zoneManager.getZoneId() instead
|
||||||
*/
|
*/
|
||||||
getZoneId: async (domainName: string) => {
|
getZoneId: async (domainName: string) => {
|
||||||
const domain = new plugins.smartstring.Domain(domainName);
|
const domain = new plugins.smartstring.Domain(domainName);
|
||||||
@@ -175,6 +189,7 @@ export class CloudflareAccount implements plugins.tsclass.network.IConvenientDns
|
|||||||
* gets a record
|
* gets a record
|
||||||
* @param domainNameArg
|
* @param domainNameArg
|
||||||
* @param typeArg
|
* @param typeArg
|
||||||
|
* @deprecated Use recordManager.getRecord() or getConvenientDnsProvider().getRecord() instead
|
||||||
*/
|
*/
|
||||||
getRecord: async (
|
getRecord: async (
|
||||||
domainNameArg: string,
|
domainNameArg: string,
|
||||||
@@ -204,6 +219,7 @@ export class CloudflareAccount implements plugins.tsclass.network.IConvenientDns
|
|||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* creates a record
|
* creates a record
|
||||||
|
* @deprecated Use recordManager.createRecord() or getConvenientDnsProvider().createRecord() instead
|
||||||
*/
|
*/
|
||||||
createRecord: async (
|
createRecord: async (
|
||||||
domainNameArg: string,
|
domainNameArg: string,
|
||||||
@@ -226,6 +242,7 @@ export class CloudflareAccount implements plugins.tsclass.network.IConvenientDns
|
|||||||
* removes a record from Cloudflare
|
* removes a record from Cloudflare
|
||||||
* @param domainNameArg
|
* @param domainNameArg
|
||||||
* @param typeArg
|
* @param typeArg
|
||||||
|
* @deprecated Use recordManager.deleteRecord() or getConvenientDnsProvider().removeRecord() instead
|
||||||
*/
|
*/
|
||||||
removeRecord: async (
|
removeRecord: async (
|
||||||
domainNameArg: string,
|
domainNameArg: string,
|
||||||
@@ -251,6 +268,7 @@ export class CloudflareAccount implements plugins.tsclass.network.IConvenientDns
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* cleanrecord allows the cleaning of any previous records to avoid unwanted sideeffects
|
* cleanrecord allows the cleaning of any previous records to avoid unwanted sideeffects
|
||||||
|
* @deprecated Use recordManager.cleanRecords() or getConvenientDnsProvider().cleanRecord() instead
|
||||||
*/
|
*/
|
||||||
cleanRecord: async (domainNameArg: string, typeArg: plugins.tsclass.network.TDnsRecordType) => {
|
cleanRecord: async (domainNameArg: string, typeArg: plugins.tsclass.network.TDnsRecordType) => {
|
||||||
try {
|
try {
|
||||||
@@ -312,6 +330,7 @@ export class CloudflareAccount implements plugins.tsclass.network.IConvenientDns
|
|||||||
* @param contentArg New content for the record
|
* @param contentArg New content for the record
|
||||||
* @param ttlArg Time to live in seconds (optional)
|
* @param ttlArg Time to live in seconds (optional)
|
||||||
* @returns Updated record
|
* @returns Updated record
|
||||||
|
* @deprecated Use recordManager.updateRecord() or getConvenientDnsProvider().updateRecord() instead
|
||||||
*/
|
*/
|
||||||
updateRecord: async (
|
updateRecord: async (
|
||||||
domainNameArg: string,
|
domainNameArg: string,
|
||||||
@@ -348,6 +367,7 @@ export class CloudflareAccount implements plugins.tsclass.network.IConvenientDns
|
|||||||
/**
|
/**
|
||||||
* list all records of a specified domain name
|
* list all records of a specified domain name
|
||||||
* @param domainNameArg - the domain name that you want to get the records from
|
* @param domainNameArg - the domain name that you want to get the records from
|
||||||
|
* @deprecated Use recordManager.listRecords() or getConvenientDnsProvider().listRecords() instead
|
||||||
*/
|
*/
|
||||||
listRecords: async (domainNameArg: string) => {
|
listRecords: async (domainNameArg: string) => {
|
||||||
try {
|
try {
|
||||||
@@ -372,6 +392,7 @@ export class CloudflareAccount implements plugins.tsclass.network.IConvenientDns
|
|||||||
/**
|
/**
|
||||||
* list all zones in the associated authenticated account
|
* list all zones in the associated authenticated account
|
||||||
* @param domainName optional filter by domain name
|
* @param domainName optional filter by domain name
|
||||||
|
* @deprecated Use zoneManager.listZones() instead
|
||||||
*/
|
*/
|
||||||
listZones: async (domainName?: string) => {
|
listZones: async (domainName?: string) => {
|
||||||
try {
|
try {
|
||||||
@@ -401,6 +422,7 @@ export class CloudflareAccount implements plugins.tsclass.network.IConvenientDns
|
|||||||
* Determines whether the given domain can be managed by this account
|
* Determines whether the given domain can be managed by this account
|
||||||
* @param domainName Full domain name to check (e.g., "sub.example.com")
|
* @param domainName Full domain name to check (e.g., "sub.example.com")
|
||||||
* @returns True if the zone for the domain exists in the account, false otherwise
|
* @returns True if the zone for the domain exists in the account, false otherwise
|
||||||
|
* @deprecated Use getConvenientDnsProvider().isDomainSupported() instead
|
||||||
*/
|
*/
|
||||||
isDomainSupported: async (domainName: string): Promise<boolean> => {
|
isDomainSupported: async (domainName: string): Promise<boolean> => {
|
||||||
try {
|
try {
|
||||||
@@ -417,6 +439,7 @@ export class CloudflareAccount implements plugins.tsclass.network.IConvenientDns
|
|||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* purges a zone
|
* purges a zone
|
||||||
|
* @deprecated Use zoneManager.purgeZone() instead
|
||||||
*/
|
*/
|
||||||
purgeZone: async (domainName: string): Promise<void> => {
|
purgeZone: async (domainName: string): Promise<void> => {
|
||||||
const domain = new plugins.smartstring.Domain(domainName);
|
const domain = new plugins.smartstring.Domain(domainName);
|
||||||
@@ -428,6 +451,9 @@ export class CloudflareAccount implements plugins.tsclass.network.IConvenientDns
|
|||||||
},
|
},
|
||||||
|
|
||||||
// acme convenience functions
|
// acme convenience functions
|
||||||
|
/**
|
||||||
|
* @deprecated Use getConvenientDnsProvider().acmeSetDnsChallenge() instead
|
||||||
|
*/
|
||||||
acmeSetDnsChallenge: async (dnsChallenge: plugins.tsclass.network.IDnsChallenge) => {
|
acmeSetDnsChallenge: async (dnsChallenge: plugins.tsclass.network.IDnsChallenge) => {
|
||||||
await this.convenience.cleanRecord(dnsChallenge.hostName, 'TXT');
|
await this.convenience.cleanRecord(dnsChallenge.hostName, 'TXT');
|
||||||
await this.convenience.createRecord(
|
await this.convenience.createRecord(
|
||||||
@@ -437,6 +463,9 @@ export class CloudflareAccount implements plugins.tsclass.network.IConvenientDns
|
|||||||
120,
|
120,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
/**
|
||||||
|
* @deprecated Use getConvenientDnsProvider().acmeRemoveDnsChallenge() instead
|
||||||
|
*/
|
||||||
acmeRemoveDnsChallenge: async (dnsChallenge: plugins.tsclass.network.IDnsChallenge) => {
|
acmeRemoveDnsChallenge: async (dnsChallenge: plugins.tsclass.network.IDnsChallenge) => {
|
||||||
await this.convenience.removeRecord(dnsChallenge.hostName, 'TXT');
|
await this.convenience.removeRecord(dnsChallenge.hostName, 'TXT');
|
||||||
},
|
},
|
||||||
|
|||||||
178
ts/cloudflare.classes.convenientdnsprovider.ts
Normal file
178
ts/cloudflare.classes.convenientdnsprovider.ts
Normal file
@@ -0,0 +1,178 @@
|
|||||||
|
import * as plugins from './cloudflare.plugins.js';
|
||||||
|
import { logger } from './cloudflare.logger.js';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adapter class that implements IConvenientDnsProvider interface
|
||||||
|
* Delegates to RecordManager and ZoneManager internally for clean architecture
|
||||||
|
* This allows third-party modules to use the standard DNS provider interface
|
||||||
|
*/
|
||||||
|
export class ConvenientDnsProvider implements plugins.tsclass.network.IConvenientDnsProvider {
|
||||||
|
/**
|
||||||
|
* The convenience property is required by IConvenientDnsProvider interface
|
||||||
|
* It returns this instance to maintain interface compatibility
|
||||||
|
*/
|
||||||
|
public convenience = this;
|
||||||
|
|
||||||
|
constructor(private cfAccount: any) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new DNS record
|
||||||
|
* @param domainNameArg - The domain name for the record
|
||||||
|
* @param typeArg - The DNS record type
|
||||||
|
* @param contentArg - The record content (IP address, CNAME target, etc.)
|
||||||
|
* @param ttlArg - Time to live in seconds (default: 1 = automatic)
|
||||||
|
* @returns Created record as raw API object
|
||||||
|
*/
|
||||||
|
public async createRecord(
|
||||||
|
domainNameArg: string,
|
||||||
|
typeArg: plugins.tsclass.network.TDnsRecordType,
|
||||||
|
contentArg: string,
|
||||||
|
ttlArg: number = 1,
|
||||||
|
): Promise<any> {
|
||||||
|
const record = await this.cfAccount.recordManager.createRecord(
|
||||||
|
domainNameArg,
|
||||||
|
typeArg,
|
||||||
|
contentArg,
|
||||||
|
ttlArg,
|
||||||
|
);
|
||||||
|
// Return raw API object format for interface compatibility
|
||||||
|
return this.recordToApiObject(record);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates an existing DNS record, or creates it if it doesn't exist
|
||||||
|
* @param domainNameArg - The domain name
|
||||||
|
* @param typeArg - The DNS record type
|
||||||
|
* @param contentArg - The new record content
|
||||||
|
* @param ttlArg - Time to live in seconds (default: 1 = automatic)
|
||||||
|
* @returns Updated record as raw API object
|
||||||
|
*/
|
||||||
|
public async updateRecord(
|
||||||
|
domainNameArg: string,
|
||||||
|
typeArg: plugins.tsclass.network.TDnsRecordType,
|
||||||
|
contentArg: string,
|
||||||
|
ttlArg: number = 1,
|
||||||
|
): Promise<any> {
|
||||||
|
const record = await this.cfAccount.recordManager.updateRecord(
|
||||||
|
domainNameArg,
|
||||||
|
typeArg,
|
||||||
|
contentArg,
|
||||||
|
ttlArg,
|
||||||
|
);
|
||||||
|
return this.recordToApiObject(record);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes a DNS record
|
||||||
|
* @param domainNameArg - The domain name
|
||||||
|
* @param typeArg - The DNS record type
|
||||||
|
*/
|
||||||
|
public async removeRecord(
|
||||||
|
domainNameArg: string,
|
||||||
|
typeArg: plugins.tsclass.network.TDnsRecordType,
|
||||||
|
): Promise<any> {
|
||||||
|
await this.cfAccount.recordManager.deleteRecord(domainNameArg, typeArg);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a specific DNS record by domain and type
|
||||||
|
* @param domainNameArg - The domain name
|
||||||
|
* @param typeArg - The DNS record type
|
||||||
|
* @returns Record as raw API object or undefined if not found
|
||||||
|
*/
|
||||||
|
public async getRecord(
|
||||||
|
domainNameArg: string,
|
||||||
|
typeArg: plugins.tsclass.network.TDnsRecordType,
|
||||||
|
): Promise<any | undefined> {
|
||||||
|
const record = await this.cfAccount.recordManager.getRecord(domainNameArg, typeArg);
|
||||||
|
return record ? this.recordToApiObject(record) : undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lists all DNS records for a domain
|
||||||
|
* @param domainNameArg - The domain name to list records for
|
||||||
|
* @returns Array of records as raw API objects
|
||||||
|
*/
|
||||||
|
public async listRecords(domainNameArg: string): Promise<any[]> {
|
||||||
|
const records = await this.cfAccount.recordManager.listRecords(domainNameArg);
|
||||||
|
return records.map((record: any) => this.recordToApiObject(record));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes all DNS records of a specific type for a domain
|
||||||
|
* @param domainNameArg - The domain name
|
||||||
|
* @param typeArg - The DNS record type to clean
|
||||||
|
*/
|
||||||
|
public async cleanRecord(
|
||||||
|
domainNameArg: string,
|
||||||
|
typeArg: plugins.tsclass.network.TDnsRecordType,
|
||||||
|
): Promise<void> {
|
||||||
|
await this.cfAccount.recordManager.cleanRecords(domainNameArg, typeArg);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines whether the given domain can be managed by this account
|
||||||
|
* @param domainName - Full domain name to check (e.g., "sub.example.com")
|
||||||
|
* @returns True if the zone for the domain exists in the account, false otherwise
|
||||||
|
*/
|
||||||
|
public async isDomainSupported(domainName: string): Promise<boolean> {
|
||||||
|
try {
|
||||||
|
// Parse out the apex/zone name from the full domain
|
||||||
|
const domain = new plugins.smartstring.Domain(domainName);
|
||||||
|
// List zones filtered by the zone name
|
||||||
|
const zones = await this.cfAccount.zoneManager.listZones(domain.zoneName);
|
||||||
|
// If any zone matches, we can manage this domain
|
||||||
|
return Array.isArray(zones) && zones.length > 0;
|
||||||
|
} catch (error) {
|
||||||
|
logger.log('error', `Error checking domain support for ${domainName}: ${error.message}`);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets an ACME DNS challenge for domain verification
|
||||||
|
* @param dnsChallenge - The DNS challenge object
|
||||||
|
*/
|
||||||
|
public async acmeSetDnsChallenge(
|
||||||
|
dnsChallenge: plugins.tsclass.network.IDnsChallenge,
|
||||||
|
): Promise<void> {
|
||||||
|
await this.cfAccount.recordManager.cleanRecords(dnsChallenge.hostName, 'TXT');
|
||||||
|
await this.cfAccount.recordManager.createRecord(
|
||||||
|
dnsChallenge.hostName,
|
||||||
|
'TXT',
|
||||||
|
dnsChallenge.challenge,
|
||||||
|
120,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes an ACME DNS challenge
|
||||||
|
* @param dnsChallenge - The DNS challenge object
|
||||||
|
*/
|
||||||
|
public async acmeRemoveDnsChallenge(
|
||||||
|
dnsChallenge: plugins.tsclass.network.IDnsChallenge,
|
||||||
|
): Promise<void> {
|
||||||
|
await this.cfAccount.recordManager.deleteRecord(dnsChallenge.hostName, 'TXT');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper method to convert CloudflareRecord instance to raw API object format
|
||||||
|
* This ensures compatibility with the IConvenientDnsProvider interface
|
||||||
|
*/
|
||||||
|
private recordToApiObject(record: any): any {
|
||||||
|
return {
|
||||||
|
id: record.id,
|
||||||
|
type: record.type,
|
||||||
|
name: record.name,
|
||||||
|
content: record.content,
|
||||||
|
proxiable: record.proxiable,
|
||||||
|
proxied: record.proxied,
|
||||||
|
ttl: record.ttl,
|
||||||
|
locked: record.locked,
|
||||||
|
zone_id: record.zone_id,
|
||||||
|
zone_name: record.zone_name,
|
||||||
|
created_on: record.created_on,
|
||||||
|
modified_on: record.modified_on,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
198
ts/cloudflare.classes.recordmanager.ts
Normal file
198
ts/cloudflare.classes.recordmanager.ts
Normal file
@@ -0,0 +1,198 @@
|
|||||||
|
import * as plugins from './cloudflare.plugins.js';
|
||||||
|
import { logger } from './cloudflare.logger.js';
|
||||||
|
import { CloudflareRecord } from './cloudflare.classes.record.js';
|
||||||
|
|
||||||
|
export class RecordManager {
|
||||||
|
constructor(private cfAccount: any) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lists all DNS records for a domain
|
||||||
|
* @param domainNameArg - The domain name to list records for
|
||||||
|
* @returns Array of CloudflareRecord instances
|
||||||
|
*/
|
||||||
|
public async listRecords(domainNameArg: string): Promise<CloudflareRecord[]> {
|
||||||
|
try {
|
||||||
|
const domain = new plugins.smartstring.Domain(domainNameArg);
|
||||||
|
const zoneId = await this.cfAccount.zoneManager.getZoneId(domain.zoneName);
|
||||||
|
const records: plugins.ICloudflareTypes['Record'][] = [];
|
||||||
|
|
||||||
|
// Collect all records using async iterator
|
||||||
|
for await (const record of this.cfAccount.apiAccount.dns.records.list({
|
||||||
|
zone_id: zoneId,
|
||||||
|
})) {
|
||||||
|
records.push(record);
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.log('info', `Found ${records.length} DNS records for ${domainNameArg}`);
|
||||||
|
|
||||||
|
// Convert to CloudflareRecord instances
|
||||||
|
return records.map(record => CloudflareRecord.createFromApiObject(record));
|
||||||
|
} catch (error) {
|
||||||
|
logger.log('error', `Failed to list records for ${domainNameArg}: ${error.message}`);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a specific DNS record by domain and type
|
||||||
|
* @param domainNameArg - The domain name
|
||||||
|
* @param typeArg - The DNS record type (A, AAAA, CNAME, TXT, etc.)
|
||||||
|
* @returns CloudflareRecord instance or undefined if not found
|
||||||
|
*/
|
||||||
|
public async getRecord(
|
||||||
|
domainNameArg: string,
|
||||||
|
typeArg: plugins.tsclass.network.TDnsRecordType,
|
||||||
|
): Promise<CloudflareRecord | undefined> {
|
||||||
|
try {
|
||||||
|
const domain = new plugins.smartstring.Domain(domainNameArg);
|
||||||
|
const recordArray = await this.listRecords(domain.zoneName);
|
||||||
|
|
||||||
|
const filteredRecords = recordArray.filter((recordArg) => {
|
||||||
|
return recordArg.type === typeArg && recordArg.name === domainNameArg;
|
||||||
|
});
|
||||||
|
|
||||||
|
return filteredRecords.length > 0 ? filteredRecords[0] : undefined;
|
||||||
|
} catch (error) {
|
||||||
|
logger.log('error', `Error getting record for ${domainNameArg}: ${error.message}`);
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new DNS record
|
||||||
|
* @param domainNameArg - The domain name for the record
|
||||||
|
* @param typeArg - The DNS record type
|
||||||
|
* @param contentArg - The record content (IP address, CNAME target, etc.)
|
||||||
|
* @param ttlArg - Time to live in seconds (default: 1 = automatic)
|
||||||
|
* @returns Created CloudflareRecord instance
|
||||||
|
*/
|
||||||
|
public async createRecord(
|
||||||
|
domainNameArg: string,
|
||||||
|
typeArg: plugins.tsclass.network.TDnsRecordType,
|
||||||
|
contentArg: string,
|
||||||
|
ttlArg: number = 1,
|
||||||
|
): Promise<CloudflareRecord> {
|
||||||
|
const domain = new plugins.smartstring.Domain(domainNameArg);
|
||||||
|
const zoneId = await this.cfAccount.zoneManager.getZoneId(domain.zoneName);
|
||||||
|
|
||||||
|
const response = await this.cfAccount.apiAccount.dns.records.create({
|
||||||
|
zone_id: zoneId,
|
||||||
|
type: typeArg as any,
|
||||||
|
name: domain.fullName,
|
||||||
|
content: contentArg,
|
||||||
|
ttl: ttlArg,
|
||||||
|
});
|
||||||
|
|
||||||
|
logger.log('info', `Created ${typeArg} record for ${domainNameArg}`);
|
||||||
|
return CloudflareRecord.createFromApiObject(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates an existing DNS record, or creates it if it doesn't exist
|
||||||
|
* @param domainNameArg - The domain name
|
||||||
|
* @param typeArg - The DNS record type
|
||||||
|
* @param contentArg - The new record content
|
||||||
|
* @param ttlArg - Time to live in seconds (default: 1 = automatic)
|
||||||
|
* @returns Updated CloudflareRecord instance
|
||||||
|
*/
|
||||||
|
public async updateRecord(
|
||||||
|
domainNameArg: string,
|
||||||
|
typeArg: plugins.tsclass.network.TDnsRecordType,
|
||||||
|
contentArg: string,
|
||||||
|
ttlArg: number = 1,
|
||||||
|
): Promise<CloudflareRecord> {
|
||||||
|
const domain = new plugins.smartstring.Domain(domainNameArg);
|
||||||
|
const zoneId = await this.cfAccount.zoneManager.getZoneId(domain.zoneName);
|
||||||
|
|
||||||
|
// Find existing record
|
||||||
|
const existingRecord = await this.getRecord(domainNameArg, typeArg);
|
||||||
|
|
||||||
|
if (!existingRecord) {
|
||||||
|
logger.log(
|
||||||
|
'warn',
|
||||||
|
`Record ${domainNameArg} of type ${typeArg} not found for update, creating instead`,
|
||||||
|
);
|
||||||
|
return this.createRecord(domainNameArg, typeArg, contentArg, ttlArg);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the record
|
||||||
|
const updatedRecord = await this.cfAccount.apiAccount.dns.records.edit(existingRecord.id, {
|
||||||
|
zone_id: zoneId,
|
||||||
|
type: typeArg as any,
|
||||||
|
name: domain.fullName,
|
||||||
|
content: contentArg,
|
||||||
|
ttl: ttlArg,
|
||||||
|
});
|
||||||
|
|
||||||
|
logger.log('info', `Updated ${typeArg} record for ${domainNameArg}`);
|
||||||
|
return CloudflareRecord.createFromApiObject(updatedRecord);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes a DNS record
|
||||||
|
* @param domainNameArg - The domain name
|
||||||
|
* @param typeArg - The DNS record type
|
||||||
|
*/
|
||||||
|
public async deleteRecord(
|
||||||
|
domainNameArg: string,
|
||||||
|
typeArg: plugins.tsclass.network.TDnsRecordType,
|
||||||
|
): Promise<void> {
|
||||||
|
const domain = new plugins.smartstring.Domain(domainNameArg);
|
||||||
|
const zoneId = await this.cfAccount.zoneManager.getZoneId(domain.zoneName);
|
||||||
|
const record = await this.getRecord(domainNameArg, typeArg);
|
||||||
|
|
||||||
|
if (record) {
|
||||||
|
await this.cfAccount.apiAccount.dns.records.delete(record.id, {
|
||||||
|
zone_id: zoneId,
|
||||||
|
});
|
||||||
|
logger.log('info', `Deleted ${typeArg} record for ${domainNameArg}`);
|
||||||
|
} else {
|
||||||
|
logger.log('warn', `Record ${domainNameArg} of type ${typeArg} not found for deletion`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes all DNS records of a specific type for a domain
|
||||||
|
* @param domainNameArg - The domain name
|
||||||
|
* @param typeArg - The DNS record type to clean
|
||||||
|
*/
|
||||||
|
public async cleanRecords(
|
||||||
|
domainNameArg: string,
|
||||||
|
typeArg: plugins.tsclass.network.TDnsRecordType,
|
||||||
|
): Promise<void> {
|
||||||
|
try {
|
||||||
|
logger.log('info', `Cleaning ${typeArg} records for ${domainNameArg}`);
|
||||||
|
const domain = new plugins.smartstring.Domain(domainNameArg);
|
||||||
|
const zoneId = await this.cfAccount.zoneManager.getZoneId(domain.zoneName);
|
||||||
|
|
||||||
|
// List all records in the zone for this domain
|
||||||
|
const records = await this.listRecords(domain.zoneName);
|
||||||
|
|
||||||
|
// Only delete records matching the specified name and type
|
||||||
|
const recordsToDelete = records.filter((recordArg) => {
|
||||||
|
return recordArg.type === typeArg && recordArg.name === domainNameArg;
|
||||||
|
});
|
||||||
|
|
||||||
|
logger.log(
|
||||||
|
'info',
|
||||||
|
`Found ${recordsToDelete.length} ${typeArg} records to delete for ${domainNameArg}`,
|
||||||
|
);
|
||||||
|
|
||||||
|
for (const recordToDelete of recordsToDelete) {
|
||||||
|
try {
|
||||||
|
await this.cfAccount.apiAccount.dns.records.delete(recordToDelete.id, {
|
||||||
|
zone_id: zoneId,
|
||||||
|
});
|
||||||
|
logger.log('info', `Deleted ${typeArg} record ${recordToDelete.id} for ${domainNameArg}`);
|
||||||
|
} catch (deleteError) {
|
||||||
|
logger.log('error', `Failed to delete record: ${deleteError.message}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
logger.log(
|
||||||
|
'error',
|
||||||
|
`Error cleaning ${typeArg} records for ${domainNameArg}: ${error.message}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -20,7 +20,7 @@ export class CloudflareWorker {
|
|||||||
): Promise<CloudflareWorker> {
|
): Promise<CloudflareWorker> {
|
||||||
const newWorker = new CloudflareWorker(workerManager);
|
const newWorker = new CloudflareWorker(workerManager);
|
||||||
Object.assign(newWorker, apiObject);
|
Object.assign(newWorker, apiObject);
|
||||||
await newWorker.getRoutes();
|
await newWorker.listRoutes();
|
||||||
return newWorker;
|
return newWorker;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -41,9 +41,9 @@ export class CloudflareWorker {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* gets all routes for a worker
|
* Lists all routes for this worker
|
||||||
*/
|
*/
|
||||||
public async getRoutes() {
|
public async listRoutes() {
|
||||||
try {
|
try {
|
||||||
this.routes = []; // Reset routes before fetching
|
this.routes = []; // Reset routes before fetching
|
||||||
|
|
||||||
@@ -102,7 +102,7 @@ export class CloudflareWorker {
|
|||||||
*/
|
*/
|
||||||
public async setRoutes(routeArray: IWorkerRouteDefinition[]) {
|
public async setRoutes(routeArray: IWorkerRouteDefinition[]) {
|
||||||
// First get all existing routes to determine what we need to create/update
|
// First get all existing routes to determine what we need to create/update
|
||||||
await this.getRoutes();
|
await this.listRoutes();
|
||||||
|
|
||||||
for (const newRoute of routeArray) {
|
for (const newRoute of routeArray) {
|
||||||
// Determine whether a route is new, needs an update, or is already up to date
|
// Determine whether a route is new, needs an update, or is already up to date
|
||||||
@@ -156,7 +156,7 @@ export class CloudflareWorker {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Refresh routes after all changes
|
// Refresh routes after all changes
|
||||||
await this.getRoutes();
|
await this.listRoutes();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ export class WorkerManager {
|
|||||||
|
|
||||||
// Initialize the worker and get its routes
|
// Initialize the worker and get its routes
|
||||||
try {
|
try {
|
||||||
await worker.getRoutes();
|
await worker.listRoutes();
|
||||||
} catch (routeError) {
|
} catch (routeError) {
|
||||||
logger.log('warn', `Failed to get routes for worker ${workerName}: ${routeError.message}`);
|
logger.log('warn', `Failed to get routes for worker ${workerName}: ${routeError.message}`);
|
||||||
// Continue anyway since the worker was created
|
// Continue anyway since the worker was created
|
||||||
@@ -79,7 +79,7 @@ export class WorkerManager {
|
|||||||
|
|
||||||
// Initialize the worker and get its routes
|
// Initialize the worker and get its routes
|
||||||
try {
|
try {
|
||||||
await worker.getRoutes();
|
await worker.listRoutes();
|
||||||
} catch (routeError) {
|
} catch (routeError) {
|
||||||
logger.log('warn', `Failed to get routes for worker ${workerName}: ${routeError.message}`);
|
logger.log('warn', `Failed to get routes for worker ${workerName}: ${routeError.message}`);
|
||||||
// Continue anyway since we found the worker
|
// Continue anyway since we found the worker
|
||||||
@@ -96,7 +96,7 @@ export class WorkerManager {
|
|||||||
* Lists all worker scripts
|
* Lists all worker scripts
|
||||||
* @returns Array of worker scripts
|
* @returns Array of worker scripts
|
||||||
*/
|
*/
|
||||||
public async listWorkerScripts() {
|
public async listWorkers() {
|
||||||
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.');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,11 +12,11 @@ export class ZoneManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get all zones, optionally filtered by name
|
* Lists all zones, optionally filtered by name
|
||||||
* @param zoneName Optional zone name to filter by
|
* @param zoneName Optional zone name to filter by
|
||||||
* @returns Array of CloudflareZone instances
|
* @returns Array of CloudflareZone instances
|
||||||
*/
|
*/
|
||||||
public async getZones(zoneName?: string): Promise<CloudflareZone[]> {
|
public async listZones(zoneName?: string): Promise<CloudflareZone[]> {
|
||||||
try {
|
try {
|
||||||
const options: any = { per_page: 50 };
|
const options: any = { per_page: 50 };
|
||||||
|
|
||||||
@@ -37,13 +37,33 @@ export class ZoneManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the zone ID for a domain name
|
||||||
|
* @param domainName Domain name to get the zone ID for
|
||||||
|
* @returns Zone ID string
|
||||||
|
* @throws Error if domain is not found in this account
|
||||||
|
*/
|
||||||
|
public async getZoneId(domainName: string): Promise<string> {
|
||||||
|
const domain = new plugins.smartstring.Domain(domainName);
|
||||||
|
const zoneArray = await this.listZones(domain.zoneName);
|
||||||
|
const filteredResponse = zoneArray.filter((zoneArg) => {
|
||||||
|
return zoneArg.name === domainName;
|
||||||
|
});
|
||||||
|
if (filteredResponse.length >= 1) {
|
||||||
|
return filteredResponse[0].id;
|
||||||
|
} else {
|
||||||
|
logger.log('error', `the domain ${domainName} does not appear to be in this account!`);
|
||||||
|
throw new Error(`the domain ${domainName} does not appear to be in this account!`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a single zone by name
|
* Get a single zone by name
|
||||||
* @param zoneName Zone name to find
|
* @param zoneName Zone name to find
|
||||||
* @returns CloudflareZone instance or undefined if not found
|
* @returns CloudflareZone instance or undefined if not found
|
||||||
*/
|
*/
|
||||||
public async getZoneByName(zoneName: string): Promise<CloudflareZone | undefined> {
|
public async getZoneByName(zoneName: string): Promise<CloudflareZone | undefined> {
|
||||||
const zones = await this.getZones(zoneName);
|
const zones = await this.listZones(zoneName);
|
||||||
return zones.find((zone) => zone.name === zoneName);
|
return zones.find((zone) => zone.name === zoneName);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -130,7 +150,7 @@ export class ZoneManager {
|
|||||||
* @returns True if the zone exists
|
* @returns True if the zone exists
|
||||||
*/
|
*/
|
||||||
public async zoneExists(zoneName: string): Promise<boolean> {
|
public async zoneExists(zoneName: string): Promise<boolean> {
|
||||||
const zones = await this.getZones(zoneName);
|
const zones = await this.listZones(zoneName);
|
||||||
return zones.some((zone) => zone.name === zoneName);
|
return zones.some((zone) => zone.name === zoneName);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -180,4 +200,18 @@ export class ZoneManager {
|
|||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Purges all cached files for a zone
|
||||||
|
* @param domainName Domain name to purge cache for
|
||||||
|
*/
|
||||||
|
public async purgeZone(domainName: string): Promise<void> {
|
||||||
|
const domain = new plugins.smartstring.Domain(domainName);
|
||||||
|
const zoneId = await this.getZoneId(domain.zoneName);
|
||||||
|
await this.cfAccount.apiAccount.cache.purge({
|
||||||
|
zone_id: zoneId,
|
||||||
|
purge_everything: true,
|
||||||
|
});
|
||||||
|
logger.log('info', `Purged cache for zone ${domainName}`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,8 +6,10 @@ export {
|
|||||||
} from './cloudflare.classes.worker.js';
|
} from './cloudflare.classes.worker.js';
|
||||||
export { WorkerManager } from './cloudflare.classes.workermanager.js';
|
export { WorkerManager } from './cloudflare.classes.workermanager.js';
|
||||||
export { CloudflareRecord, type ICloudflareRecordInfo } from './cloudflare.classes.record.js';
|
export { CloudflareRecord, type ICloudflareRecordInfo } from './cloudflare.classes.record.js';
|
||||||
|
export { RecordManager } from './cloudflare.classes.recordmanager.js';
|
||||||
export { CloudflareZone } from './cloudflare.classes.zone.js';
|
export { CloudflareZone } from './cloudflare.classes.zone.js';
|
||||||
export { ZoneManager } from './cloudflare.classes.zonemanager.js';
|
export { ZoneManager } from './cloudflare.classes.zonemanager.js';
|
||||||
|
export { ConvenientDnsProvider } from './cloudflare.classes.convenientdnsprovider.js';
|
||||||
export { CloudflareUtils } from './cloudflare.utils.js';
|
export { CloudflareUtils } from './cloudflare.utils.js';
|
||||||
export { commitinfo } from './00_commitinfo_data.js';
|
export { commitinfo } from './00_commitinfo_data.js';
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user