From 4b82cfbaae954e4df9132b588d94174b1d1da7c6 Mon Sep 17 00:00:00 2001 From: Philipp Kunz Date: Sat, 26 Apr 2025 12:37:19 +0000 Subject: [PATCH] fix(core): Improve nested DNS record management and worker script multipart handling --- changelog.md | 7 ++++ test/test.ts | 46 ++++++++++++++++++++++++++ ts/00_commitinfo_data.ts | 2 +- ts/cloudflare.classes.account.ts | 6 ++-- ts/cloudflare.classes.worker.ts | 10 ++++-- ts/cloudflare.classes.workermanager.ts | 10 ++++-- 6 files changed, 72 insertions(+), 9 deletions(-) diff --git a/changelog.md b/changelog.md index 0aa11f2..c6bfc96 100644 --- a/changelog.md +++ b/changelog.md @@ -1,5 +1,12 @@ # Changelog +## 2025-04-26 - 6.3.1 - fix(core) +Improve nested DNS record management and worker script multipart handling + +- Add tests for creating, updating, and removing nested subdomain A records +- Refine TXT record cleaning by filtering records with matching name and type +- Clarify multipart form data handling for worker script updates and creation + ## 2025-04-26 - 6.3.0 - feat(core) Release 6.2.0: Improved async iterator support, enhanced error handling and refined API interfaces for better type safety and consistent behavior. diff --git a/test/test.ts b/test/test.ts index 3231072..537791b 100644 --- a/test/test.ts +++ b/test/test.ts @@ -149,6 +149,52 @@ tap.test('should update A record content', async (tools) => { console.log(`Updated A record for ${subdomain} to 192.168.1.1`); }); +// Nested subdomain DNS record tests +tap.test('should create A record for nested subdomain', async (tools) => { + tools.timeout(600000); + const nestedSubdomain = `${randomPrefix}-nested.sub.bleu.de`; + const result = await testCloudflareAccount.convenience.createRecord( + nestedSubdomain, + 'A', + '127.0.0.5', + 120 + ); + expect(result).toBeTypeOf('object'); + console.log(`Created nested A record for ${nestedSubdomain}`); +}); + +tap.test('should get A record for nested subdomain', async (tools) => { + tools.timeout(600000); + const nestedSubdomain = `${randomPrefix}-nested.sub.bleu.de`; + const record = await testCloudflareAccount.convenience.getRecord(nestedSubdomain, 'A'); + expect(record).toBeTypeOf('object'); + expect(record.content).toEqual('127.0.0.5'); + console.log(`Successfully retrieved nested A record for ${nestedSubdomain}`); +}); + +tap.test('should update A record for nested subdomain', async (tools) => { + tools.timeout(600000); + const nestedSubdomain = `${randomPrefix}-nested.sub.bleu.de`; + const result = await testCloudflareAccount.convenience.updateRecord( + nestedSubdomain, + 'A', + '127.0.0.6', + 120 + ); + expect(result).toBeTypeOf('object'); + expect(result.content).toEqual('127.0.0.6'); + console.log(`Updated nested A record for ${nestedSubdomain}`); +}); + +tap.test('should remove nested subdomain A record', async (tools) => { + tools.timeout(600000); + const nestedSubdomain = `${randomPrefix}-nested.sub.bleu.de`; + await testCloudflareAccount.convenience.removeRecord(nestedSubdomain, 'A'); + const record = await testCloudflareAccount.convenience.getRecord(nestedSubdomain, 'A'); + expect(record).toBeUndefined(); + console.log(`Successfully removed nested A record for ${nestedSubdomain}`); +}); + tap.test('should clean TXT records', async (tools) => { tools.timeout(600000); const subdomain = `${randomPrefix}-txt-test.bleu.de`; diff --git a/ts/00_commitinfo_data.ts b/ts/00_commitinfo_data.ts index 97adb66..368fbfe 100644 --- a/ts/00_commitinfo_data.ts +++ b/ts/00_commitinfo_data.ts @@ -3,6 +3,6 @@ */ export const commitinfo = { name: '@apiclient.xyz/cloudflare', - version: '6.3.0', + version: '6.3.1', description: 'A TypeScript client for managing Cloudflare accounts, zones, DNS records, and workers with ease.' } diff --git a/ts/cloudflare.classes.account.ts b/ts/cloudflare.classes.account.ts index 442aa25..7f952dc 100644 --- a/ts/cloudflare.classes.account.ts +++ b/ts/cloudflare.classes.account.ts @@ -229,15 +229,17 @@ export class CloudflareAccount { const domain = new plugins.smartstring.Domain(domainNameArg); const zoneId = await this.convenience.getZoneId(domain.zoneName); - const records = await this.convenience.listRecords(domainNameArg); + // List all records in the zone for this domain + const records = await this.convenience.listRecords(domain.zoneName); if (!Array.isArray(records)) { logger.log('warn', `Expected records array for ${domainNameArg} but got ${typeof records}`); return; } + // Only delete records matching the specified name and type const recordsToDelete = records.filter((recordArg) => { - return recordArg.type === typeArg; + return recordArg.type === typeArg && recordArg.name === domainNameArg; }); logger.log('info', `Found ${recordsToDelete.length} ${typeArg} records to delete for ${domainNameArg}`); diff --git a/ts/cloudflare.classes.worker.ts b/ts/cloudflare.classes.worker.ts index a1edc6d..10c3e33 100644 --- a/ts/cloudflare.classes.worker.ts +++ b/ts/cloudflare.classes.worker.ts @@ -167,11 +167,15 @@ export class CloudflareWorker { try { logger.log('info', `Updating script for worker ${this.id}`); - // Use the official client to update the script + // Use the official client to update the script (upload new content) const updatedWorker = await this.workerManager.cfAccount.apiAccount.workers.scripts.content.update(this.id, { account_id: this.workerManager.cfAccount.preselectedAccountId, - "CF-WORKER-BODY-PART": scriptContent, - metadata: {} + // name the multipart part for the updated script code + metadata: { body_part: 'script' }, + /* header to indicate which part contains the script */ + 'CF-WORKER-BODY-PART': 'script', + // include the new script as a form part named 'script' + script: scriptContent, }); // Update this instance with new data diff --git a/ts/cloudflare.classes.workermanager.ts b/ts/cloudflare.classes.workermanager.ts index 445216c..9a32073 100644 --- a/ts/cloudflare.classes.workermanager.ts +++ b/ts/cloudflare.classes.workermanager.ts @@ -22,11 +22,15 @@ export class WorkerManager { } try { - // Use the official client to create/update the worker + // Use the official client to create/update the worker (upload script content) await this.cfAccount.apiAccount.workers.scripts.content.update(workerName, { account_id: this.cfAccount.preselectedAccountId, - "CF-WORKER-BODY-PART": workerScript, - metadata: {} + // name the multipart part for the script code + metadata: { body_part: 'script' }, + /* header to indicate which part contains the script */ + 'CF-WORKER-BODY-PART': 'script', + // include the actual script as a form part named 'script' + script: workerScript, }); // Create a new worker instance