fix(ci): Update CI workflows, repository URL, and apply minor code formatting fixes
This commit is contained in:
parent
5d77214222
commit
36d9db4332
66
.gitea/workflows/default_nottags.yaml
Normal file
66
.gitea/workflows/default_nottags.yaml
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
name: Default (not tags)
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
tags-ignore:
|
||||||
|
- '**'
|
||||||
|
|
||||||
|
env:
|
||||||
|
IMAGE: code.foss.global/host.today/ht-docker-node:npmci
|
||||||
|
NPMCI_COMPUTED_REPOURL: https://${{gitea.repository_owner}}:${{secrets.GITEA_TOKEN}}@/${{gitea.repository}}.git
|
||||||
|
NPMCI_TOKEN_NPM: ${{secrets.NPMCI_TOKEN_NPM}}
|
||||||
|
NPMCI_TOKEN_NPM2: ${{secrets.NPMCI_TOKEN_NPM2}}
|
||||||
|
NPMCI_GIT_GITHUBTOKEN: ${{secrets.NPMCI_GIT_GITHUBTOKEN}}
|
||||||
|
NPMCI_URL_CLOUDLY: ${{secrets.NPMCI_URL_CLOUDLY}}
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
security:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
continue-on-error: true
|
||||||
|
container:
|
||||||
|
image: ${{ env.IMAGE }}
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Install pnpm and npmci
|
||||||
|
run: |
|
||||||
|
pnpm install -g pnpm
|
||||||
|
pnpm install -g @ship.zone/npmci
|
||||||
|
|
||||||
|
- name: Run npm prepare
|
||||||
|
run: npmci npm prepare
|
||||||
|
|
||||||
|
- name: Audit production dependencies
|
||||||
|
run: |
|
||||||
|
npmci command npm config set registry https://registry.npmjs.org
|
||||||
|
npmci command pnpm audit --audit-level=high --prod
|
||||||
|
continue-on-error: true
|
||||||
|
|
||||||
|
- name: Audit development dependencies
|
||||||
|
run: |
|
||||||
|
npmci command npm config set registry https://registry.npmjs.org
|
||||||
|
npmci command pnpm audit --audit-level=high --dev
|
||||||
|
continue-on-error: true
|
||||||
|
|
||||||
|
test:
|
||||||
|
if: ${{ always() }}
|
||||||
|
needs: security
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
container:
|
||||||
|
image: ${{ env.IMAGE }}
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Test stable
|
||||||
|
run: |
|
||||||
|
npmci node install stable
|
||||||
|
npmci npm install
|
||||||
|
npmci npm test
|
||||||
|
|
||||||
|
- name: Test build
|
||||||
|
run: |
|
||||||
|
npmci node install stable
|
||||||
|
npmci npm install
|
||||||
|
npmci npm build
|
124
.gitea/workflows/default_tags.yaml
Normal file
124
.gitea/workflows/default_tags.yaml
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
name: Default (tags)
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- '*'
|
||||||
|
|
||||||
|
env:
|
||||||
|
IMAGE: code.foss.global/host.today/ht-docker-node:npmci
|
||||||
|
NPMCI_COMPUTED_REPOURL: https://${{gitea.repository_owner}}:${{secrets.GITEA_TOKEN}}@/${{gitea.repository}}.git
|
||||||
|
NPMCI_TOKEN_NPM: ${{secrets.NPMCI_TOKEN_NPM}}
|
||||||
|
NPMCI_TOKEN_NPM2: ${{secrets.NPMCI_TOKEN_NPM2}}
|
||||||
|
NPMCI_GIT_GITHUBTOKEN: ${{secrets.NPMCI_GIT_GITHUBTOKEN}}
|
||||||
|
NPMCI_URL_CLOUDLY: ${{secrets.NPMCI_URL_CLOUDLY}}
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
security:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
continue-on-error: true
|
||||||
|
container:
|
||||||
|
image: ${{ env.IMAGE }}
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Prepare
|
||||||
|
run: |
|
||||||
|
pnpm install -g pnpm
|
||||||
|
pnpm install -g @ship.zone/npmci
|
||||||
|
npmci npm prepare
|
||||||
|
|
||||||
|
- name: Audit production dependencies
|
||||||
|
run: |
|
||||||
|
npmci command npm config set registry https://registry.npmjs.org
|
||||||
|
npmci command pnpm audit --audit-level=high --prod
|
||||||
|
continue-on-error: true
|
||||||
|
|
||||||
|
- name: Audit development dependencies
|
||||||
|
run: |
|
||||||
|
npmci command npm config set registry https://registry.npmjs.org
|
||||||
|
npmci command pnpm audit --audit-level=high --dev
|
||||||
|
continue-on-error: true
|
||||||
|
|
||||||
|
test:
|
||||||
|
if: ${{ always() }}
|
||||||
|
needs: security
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
container:
|
||||||
|
image: ${{ env.IMAGE }}
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Prepare
|
||||||
|
run: |
|
||||||
|
pnpm install -g pnpm
|
||||||
|
pnpm install -g @ship.zone/npmci
|
||||||
|
npmci npm prepare
|
||||||
|
|
||||||
|
- name: Test stable
|
||||||
|
run: |
|
||||||
|
npmci node install stable
|
||||||
|
npmci npm install
|
||||||
|
npmci npm test
|
||||||
|
|
||||||
|
- name: Test build
|
||||||
|
run: |
|
||||||
|
npmci node install stable
|
||||||
|
npmci npm install
|
||||||
|
npmci npm build
|
||||||
|
|
||||||
|
release:
|
||||||
|
needs: test
|
||||||
|
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/')
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
container:
|
||||||
|
image: ${{ env.IMAGE }}
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Prepare
|
||||||
|
run: |
|
||||||
|
pnpm install -g pnpm
|
||||||
|
pnpm install -g @ship.zone/npmci
|
||||||
|
npmci npm prepare
|
||||||
|
|
||||||
|
- name: Release
|
||||||
|
run: |
|
||||||
|
npmci node install stable
|
||||||
|
npmci npm publish
|
||||||
|
|
||||||
|
metadata:
|
||||||
|
needs: test
|
||||||
|
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/')
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
container:
|
||||||
|
image: ${{ env.IMAGE }}
|
||||||
|
continue-on-error: true
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Prepare
|
||||||
|
run: |
|
||||||
|
pnpm install -g pnpm
|
||||||
|
pnpm install -g @ship.zone/npmci
|
||||||
|
npmci npm prepare
|
||||||
|
|
||||||
|
- name: Code quality
|
||||||
|
run: |
|
||||||
|
npmci command npm install -g typescript
|
||||||
|
npmci npm install
|
||||||
|
|
||||||
|
- name: Trigger
|
||||||
|
run: npmci trigger
|
||||||
|
|
||||||
|
- name: Build docs and upload artifacts
|
||||||
|
run: |
|
||||||
|
npmci node install stable
|
||||||
|
npmci npm install
|
||||||
|
pnpm install -g @git.zone/tsdoc
|
||||||
|
npmci command tsdoc
|
||||||
|
continue-on-error: true
|
3
.gitignore
vendored
3
.gitignore
vendored
@ -3,7 +3,6 @@
|
|||||||
# artifacts
|
# artifacts
|
||||||
coverage/
|
coverage/
|
||||||
public/
|
public/
|
||||||
pages/
|
|
||||||
|
|
||||||
# installs
|
# installs
|
||||||
node_modules/
|
node_modules/
|
||||||
@ -17,4 +16,4 @@ node_modules/
|
|||||||
dist/
|
dist/
|
||||||
dist_*/
|
dist_*/
|
||||||
|
|
||||||
# custom
|
#------# custom
|
@ -1,5 +1,13 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## 2025-04-30 - 6.4.1 - fix(ci)
|
||||||
|
Update CI workflows, repository URL, and apply minor code formatting fixes
|
||||||
|
|
||||||
|
- Add new Gitea workflows for both tagged and non-tagged push events with security, test, release, and metadata jobs
|
||||||
|
- Update repository URL in package.json from pushrocks/cflare to mojoio/cloudflare
|
||||||
|
- Refine .gitignore custom comments
|
||||||
|
- Apply minor formatting improvements in source code and documentation
|
||||||
|
|
||||||
## 2025-04-30 - 6.4.0 - feat(CloudflareAccount)
|
## 2025-04-30 - 6.4.0 - feat(CloudflareAccount)
|
||||||
Bump dependency versions and add domain support check in CloudflareAccount
|
Bump dependency versions and add domain support check in CloudflareAccount
|
||||||
|
|
||||||
|
13
package.json
13
package.json
@ -14,7 +14,7 @@
|
|||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "git+https://gitlab.com/pushrocks/cflare.git"
|
"url": "https://gitlab.com/mojoio/cloudflare.git"
|
||||||
},
|
},
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"Cloudflare",
|
"Cloudflare",
|
||||||
@ -31,9 +31,9 @@
|
|||||||
"author": "Lossless GmbH",
|
"author": "Lossless GmbH",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"bugs": {
|
"bugs": {
|
||||||
"url": "https://gitlab.com/pushrocks/cflare/issues"
|
"url": "https://gitlab.com/mojoio/cloudflare/issues"
|
||||||
},
|
},
|
||||||
"homepage": "https://gitlab.com/pushrocks/cflare#readme",
|
"homepage": "https://gitlab.com/mojoio/cloudflare#readme",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@push.rocks/smartdelay": "^3.0.1",
|
"@push.rocks/smartdelay": "^3.0.1",
|
||||||
"@push.rocks/smartlog": "^3.0.2",
|
"@push.rocks/smartlog": "^3.0.2",
|
||||||
@ -67,5 +67,8 @@
|
|||||||
"browserslist": [
|
"browserslist": [
|
||||||
"last 1 chrome versions"
|
"last 1 chrome versions"
|
||||||
],
|
],
|
||||||
"packageManager": "pnpm@10.7.0+sha512.6b865ad4b62a1d9842b61d674a393903b871d9244954f652b8842c2b553c72176b278f64c463e52d40fff8aba385c235c8c9ecf5cc7de4fd78b8bb6d49633ab6"
|
"packageManager": "pnpm@10.7.0+sha512.6b865ad4b62a1d9842b61d674a393903b871d9244954f652b8842c2b553c72176b278f64c463e52d40fff8aba385c235c8c9ecf5cc7de4fd78b8bb6d49633ab6",
|
||||||
}
|
"pnpm": {
|
||||||
|
"overrides": {}
|
||||||
|
}
|
||||||
|
}
|
64
readme.md
64
readme.md
@ -89,7 +89,7 @@ await myZone.purgeCache();
|
|||||||
await myZone.purgeUrls(['https://example.com/css/styles.css', 'https://example.com/js/app.js']);
|
await myZone.purgeUrls(['https://example.com/css/styles.css', 'https://example.com/js/app.js']);
|
||||||
|
|
||||||
// Enable/disable development mode
|
// Enable/disable development mode
|
||||||
await myZone.enableDevelopmentMode(); // Enables dev mode for 3 hours
|
await myZone.enableDevelopmentMode(); // Enables dev mode for 3 hours
|
||||||
await myZone.disableDevelopmentMode();
|
await myZone.disableDevelopmentMode();
|
||||||
|
|
||||||
// Check zone status
|
// Check zone status
|
||||||
@ -126,11 +126,11 @@ await cfAccount.convenience.cleanRecord('example.com', 'TXT');
|
|||||||
// Support for ACME DNS challenges (for certificate issuance)
|
// Support for ACME DNS challenges (for certificate issuance)
|
||||||
await cfAccount.convenience.acmeSetDnsChallenge({
|
await cfAccount.convenience.acmeSetDnsChallenge({
|
||||||
hostName: '_acme-challenge.example.com',
|
hostName: '_acme-challenge.example.com',
|
||||||
challenge: 'token-validation-string'
|
challenge: 'token-validation-string',
|
||||||
});
|
});
|
||||||
await cfAccount.convenience.acmeRemoveDnsChallenge({
|
await cfAccount.convenience.acmeRemoveDnsChallenge({
|
||||||
hostName: '_acme-challenge.example.com',
|
hostName: '_acme-challenge.example.com',
|
||||||
challenge: 'token-validation-string'
|
challenge: 'token-validation-string',
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -157,12 +157,12 @@ const existingWorker = await cfAccount.workerManager.getWorker('my-worker');
|
|||||||
await worker.setRoutes([
|
await worker.setRoutes([
|
||||||
{
|
{
|
||||||
zoneName: 'example.com',
|
zoneName: 'example.com',
|
||||||
pattern: 'https://api.example.com/*'
|
pattern: 'https://api.example.com/*',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
zoneName: 'example.com',
|
zoneName: 'example.com',
|
||||||
pattern: 'https://app.example.com/api/*'
|
pattern: 'https://app.example.com/api/*',
|
||||||
}
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// Get all routes for a worker
|
// Get all routes for a worker
|
||||||
@ -191,19 +191,19 @@ async function manageCloudflare() {
|
|||||||
try {
|
try {
|
||||||
// Initialize with API token from environment variable
|
// Initialize with API token from environment variable
|
||||||
const cfAccount = new cflare.CloudflareAccount(process.env.CLOUDFLARE_API_TOKEN);
|
const cfAccount = new cflare.CloudflareAccount(process.env.CLOUDFLARE_API_TOKEN);
|
||||||
|
|
||||||
// Preselect account if needed
|
// Preselect account if needed
|
||||||
await cfAccount.preselectAccountByName('My Company');
|
await cfAccount.preselectAccountByName('My Company');
|
||||||
|
|
||||||
// Get zone and check status
|
// Get zone and check status
|
||||||
const myZone = await cfAccount.zoneManager.getZoneByName('example.com');
|
const myZone = await cfAccount.zoneManager.getZoneByName('example.com');
|
||||||
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
|
||||||
await cfAccount.convenience.createRecord('api.example.com', 'A', '192.0.2.1');
|
await cfAccount.convenience.createRecord('api.example.com', 'A', '192.0.2.1');
|
||||||
await cfAccount.convenience.createRecord('www.example.com', 'CNAME', 'example.com');
|
await cfAccount.convenience.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 = `
|
||||||
addEventListener('fetch', event => {
|
addEventListener('fetch', event => {
|
||||||
@ -217,15 +217,13 @@ async function manageCloudflare() {
|
|||||||
event.respondWith(fetch(event.request));
|
event.respondWith(fetch(event.request));
|
||||||
}
|
}
|
||||||
})`;
|
})`;
|
||||||
|
|
||||||
const worker = await cfAccount.workerManager.createWorker('api-handler', workerCode);
|
const worker = await cfAccount.workerManager.createWorker('api-handler', workerCode);
|
||||||
await worker.setRoutes([
|
await worker.setRoutes([{ zoneName: 'example.com', pattern: 'https://api.example.com/*' }]);
|
||||||
{ zoneName: 'example.com', pattern: 'https://api.example.com/*' }
|
|
||||||
]);
|
|
||||||
|
|
||||||
// Purge cache for specific URLs
|
// Purge cache for specific URLs
|
||||||
await myZone.purgeUrls(['https://example.com/css/styles.css']);
|
await myZone.purgeUrls(['https://example.com/css/styles.css']);
|
||||||
|
|
||||||
console.log('Configuration completed successfully');
|
console.log('Configuration completed successfully');
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error managing Cloudflare:', error);
|
console.error('Error managing Cloudflare:', error);
|
||||||
@ -244,33 +242,43 @@ The main entry point for all Cloudflare operations.
|
|||||||
```typescript
|
```typescript
|
||||||
class CloudflareAccount {
|
class CloudflareAccount {
|
||||||
constructor(apiToken: string);
|
constructor(apiToken: string);
|
||||||
|
|
||||||
// Account management
|
// Account management
|
||||||
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
|
||||||
readonly zoneManager: ZoneManager;
|
readonly zoneManager: ZoneManager;
|
||||||
readonly workerManager: WorkerManager;
|
readonly workerManager: WorkerManager;
|
||||||
|
|
||||||
// Official Cloudflare client
|
// Official Cloudflare client
|
||||||
readonly apiAccount: cloudflare.Cloudflare;
|
readonly apiAccount: cloudflare.Cloudflare;
|
||||||
|
|
||||||
// Convenience namespace with helper methods
|
// Convenience namespace with helper methods
|
||||||
readonly convenience: {
|
readonly convenience: {
|
||||||
// Zone operations
|
// Zone operations
|
||||||
listZones(domainName?: string): Promise<CloudflareZone[]>;
|
listZones(domainName?: string): Promise<CloudflareZone[]>;
|
||||||
getZoneId(domainName: string): Promise<string>;
|
getZoneId(domainName: string): Promise<string>;
|
||||||
purgeZone(domainName: string): Promise<void>;
|
purgeZone(domainName: string): Promise<void>;
|
||||||
|
|
||||||
// DNS operations
|
// DNS operations
|
||||||
listRecords(domainName: string): Promise<CloudflareRecord[]>;
|
listRecords(domainName: string): Promise<CloudflareRecord[]>;
|
||||||
getRecord(domainName: string, recordType: string): Promise<CloudflareRecord | undefined>;
|
getRecord(domainName: string, recordType: string): Promise<CloudflareRecord | undefined>;
|
||||||
createRecord(domainName: string, recordType: string, content: string, ttl?: number): Promise<any>;
|
createRecord(
|
||||||
updateRecord(domainName: string, recordType: string, content: string, ttl?: number): Promise<any>;
|
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>;
|
removeRecord(domainName: string, recordType: string): Promise<any>;
|
||||||
cleanRecord(domainName: string, recordType: string): Promise<void>;
|
cleanRecord(domainName: string, recordType: string): Promise<void>;
|
||||||
|
|
||||||
// ACME operations
|
// ACME operations
|
||||||
acmeSetDnsChallenge(dnsChallenge: IDnsChallenge): Promise<any>;
|
acmeSetDnsChallenge(dnsChallenge: IDnsChallenge): Promise<any>;
|
||||||
acmeRemoveDnsChallenge(dnsChallenge: IDnsChallenge): Promise<any>;
|
acmeRemoveDnsChallenge(dnsChallenge: IDnsChallenge): Promise<any>;
|
||||||
@ -291,7 +299,7 @@ class CloudflareZone {
|
|||||||
readonly paused: boolean;
|
readonly paused: boolean;
|
||||||
readonly type: string;
|
readonly type: string;
|
||||||
readonly nameServers: string[];
|
readonly nameServers: string[];
|
||||||
|
|
||||||
// Methods
|
// Methods
|
||||||
async purgeCache(): Promise<any>;
|
async purgeCache(): Promise<any>;
|
||||||
async purgeUrls(urls: string[]): Promise<any>;
|
async purgeUrls(urls: string[]): Promise<any>;
|
||||||
@ -316,7 +324,7 @@ class CloudflareRecord {
|
|||||||
readonly content: string;
|
readonly content: string;
|
||||||
readonly ttl: number;
|
readonly ttl: number;
|
||||||
readonly proxied: boolean;
|
readonly proxied: boolean;
|
||||||
|
|
||||||
// Methods
|
// Methods
|
||||||
async update(content: string, ttl?: number): Promise<any>;
|
async update(content: string, ttl?: number): Promise<any>;
|
||||||
async delete(): Promise<any>;
|
async delete(): Promise<any>;
|
||||||
@ -333,7 +341,7 @@ class CloudflareWorker {
|
|||||||
readonly id: string;
|
readonly id: string;
|
||||||
readonly script: string;
|
readonly script: string;
|
||||||
readonly routes: IWorkerRoute[];
|
readonly routes: IWorkerRoute[];
|
||||||
|
|
||||||
// Methods
|
// Methods
|
||||||
async getRoutes(): Promise<IWorkerRoute[]>;
|
async getRoutes(): Promise<IWorkerRoute[]>;
|
||||||
async setRoutes(routes: Array<IWorkerRouteDefinition>): Promise<void>;
|
async setRoutes(routes: Array<IWorkerRouteDefinition>): Promise<void>;
|
||||||
@ -396,4 +404,4 @@ pnpm run test
|
|||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
MIT © [Lossless GmbH](https://lossless.gmbh)
|
MIT © [Lossless GmbH](https://lossless.gmbh)
|
||||||
|
60
test/test.ts
60
test/test.ts
@ -14,7 +14,9 @@ let testZoneName = `test-zone-${randomPrefix}.com`;
|
|||||||
|
|
||||||
// Basic initialization tests
|
// Basic initialization tests
|
||||||
tap.test('should create a valid instance of CloudflareAccount', async () => {
|
tap.test('should create a valid instance of CloudflareAccount', async () => {
|
||||||
testCloudflareAccount = new cloudflare.CloudflareAccount(await testQenv.getEnvVarOnDemand('CF_KEY'));
|
testCloudflareAccount = new cloudflare.CloudflareAccount(
|
||||||
|
await testQenv.getEnvVarOnDemand('CF_KEY'),
|
||||||
|
);
|
||||||
expect(testCloudflareAccount).toBeTypeOf('object');
|
expect(testCloudflareAccount).toBeTypeOf('object');
|
||||||
expect(testCloudflareAccount.apiAccount).toBeTypeOf('object');
|
expect(testCloudflareAccount.apiAccount).toBeTypeOf('object');
|
||||||
});
|
});
|
||||||
@ -22,12 +24,12 @@ tap.test('should create a valid instance of CloudflareAccount', async () => {
|
|||||||
tap.test('should preselect an account', async () => {
|
tap.test('should preselect an account', async () => {
|
||||||
await testCloudflareAccount.preselectAccountByName('Sandbox Account');
|
await testCloudflareAccount.preselectAccountByName('Sandbox Account');
|
||||||
expect(testCloudflareAccount.preselectedAccountId).toBeTypeOf('string');
|
expect(testCloudflareAccount.preselectedAccountId).toBeTypeOf('string');
|
||||||
})
|
});
|
||||||
|
|
||||||
// Zone management tests
|
// Zone management tests
|
||||||
tap.test('.listZones() -> should list zones in account', async (tools) => {
|
tap.test('.listZones() -> should list zones in account', async (tools) => {
|
||||||
tools.timeout(600000);
|
tools.timeout(600000);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const result = await testCloudflareAccount.convenience.listZones();
|
const result = await testCloudflareAccount.convenience.listZones();
|
||||||
// The test expects an array, but the current API might return an object with a result property
|
// The test expects an array, but the current API might return an object with a result property
|
||||||
@ -66,7 +68,7 @@ tap.test('ZoneManager: should get zone by name', async (tools) => {
|
|||||||
// DNS record tests
|
// DNS record tests
|
||||||
tap.test('.listRecords(domainName) -> should list records for domain', async (tools) => {
|
tap.test('.listRecords(domainName) -> should list records for domain', async (tools) => {
|
||||||
tools.timeout(600000);
|
tools.timeout(600000);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const records = await testCloudflareAccount.convenience.listRecords('bleu.de');
|
const records = await testCloudflareAccount.convenience.listRecords('bleu.de');
|
||||||
// The test expects an array, but the current API might return an object with a result property
|
// The test expects an array, but the current API might return an object with a result property
|
||||||
@ -94,7 +96,7 @@ tap.test('should create A record for subdomain', async (tools) => {
|
|||||||
subdomain,
|
subdomain,
|
||||||
'A',
|
'A',
|
||||||
'127.0.0.1',
|
'127.0.0.1',
|
||||||
120
|
120,
|
||||||
);
|
);
|
||||||
expect(result).toBeTypeOf('object');
|
expect(result).toBeTypeOf('object');
|
||||||
console.log(`Created A record for ${subdomain}`);
|
console.log(`Created A record for ${subdomain}`);
|
||||||
@ -107,7 +109,7 @@ tap.test('should create CNAME record for subdomain', async (tools) => {
|
|||||||
subdomain,
|
subdomain,
|
||||||
'CNAME',
|
'CNAME',
|
||||||
'example.com',
|
'example.com',
|
||||||
120
|
120,
|
||||||
);
|
);
|
||||||
expect(result).toBeTypeOf('object');
|
expect(result).toBeTypeOf('object');
|
||||||
console.log(`Created CNAME record for ${subdomain}`);
|
console.log(`Created CNAME record for ${subdomain}`);
|
||||||
@ -120,7 +122,7 @@ tap.test('should create TXT record for subdomain', async (tools) => {
|
|||||||
subdomain,
|
subdomain,
|
||||||
'TXT',
|
'TXT',
|
||||||
'v=spf1 include:_spf.example.com ~all',
|
'v=spf1 include:_spf.example.com ~all',
|
||||||
120
|
120,
|
||||||
);
|
);
|
||||||
expect(result).toBeTypeOf('object');
|
expect(result).toBeTypeOf('object');
|
||||||
console.log(`Created TXT record for ${subdomain}`);
|
console.log(`Created TXT record for ${subdomain}`);
|
||||||
@ -142,7 +144,7 @@ tap.test('should update A record content', async (tools) => {
|
|||||||
subdomain,
|
subdomain,
|
||||||
'A',
|
'A',
|
||||||
'192.168.1.1',
|
'192.168.1.1',
|
||||||
120
|
120,
|
||||||
);
|
);
|
||||||
expect(result).toBeTypeOf('object');
|
expect(result).toBeTypeOf('object');
|
||||||
expect(result.content).toEqual('192.168.1.1');
|
expect(result.content).toEqual('192.168.1.1');
|
||||||
@ -157,7 +159,7 @@ tap.test('should create A record for nested subdomain', async (tools) => {
|
|||||||
nestedSubdomain,
|
nestedSubdomain,
|
||||||
'A',
|
'A',
|
||||||
'127.0.0.5',
|
'127.0.0.5',
|
||||||
120
|
120,
|
||||||
);
|
);
|
||||||
expect(result).toBeTypeOf('object');
|
expect(result).toBeTypeOf('object');
|
||||||
console.log(`Created nested A record for ${nestedSubdomain}`);
|
console.log(`Created nested A record for ${nestedSubdomain}`);
|
||||||
@ -179,7 +181,7 @@ tap.test('should update A record for nested subdomain', async (tools) => {
|
|||||||
nestedSubdomain,
|
nestedSubdomain,
|
||||||
'A',
|
'A',
|
||||||
'127.0.0.6',
|
'127.0.0.6',
|
||||||
120
|
120,
|
||||||
);
|
);
|
||||||
expect(result).toBeTypeOf('object');
|
expect(result).toBeTypeOf('object');
|
||||||
expect(result.content).toEqual('127.0.0.6');
|
expect(result.content).toEqual('127.0.0.6');
|
||||||
@ -209,14 +211,14 @@ tap.test('should remove A and CNAME records', async (tools) => {
|
|||||||
tools.timeout(600000);
|
tools.timeout(600000);
|
||||||
const aSubdomain = `${randomPrefix}-a-test.bleu.de`;
|
const aSubdomain = `${randomPrefix}-a-test.bleu.de`;
|
||||||
const cnameSubdomain = `${randomPrefix}-cname-test.bleu.de`;
|
const cnameSubdomain = `${randomPrefix}-cname-test.bleu.de`;
|
||||||
|
|
||||||
await testCloudflareAccount.convenience.removeRecord(aSubdomain, 'A');
|
await testCloudflareAccount.convenience.removeRecord(aSubdomain, 'A');
|
||||||
await testCloudflareAccount.convenience.removeRecord(cnameSubdomain, 'CNAME');
|
await testCloudflareAccount.convenience.removeRecord(cnameSubdomain, 'CNAME');
|
||||||
|
|
||||||
// Verify records are removed
|
// Verify records are removed
|
||||||
const aRecord = await testCloudflareAccount.convenience.getRecord(aSubdomain, 'A');
|
const aRecord = await testCloudflareAccount.convenience.getRecord(aSubdomain, 'A');
|
||||||
const cnameRecord = await testCloudflareAccount.convenience.getRecord(cnameSubdomain, 'CNAME');
|
const cnameRecord = await testCloudflareAccount.convenience.getRecord(cnameSubdomain, 'CNAME');
|
||||||
|
|
||||||
expect(aRecord).toBeUndefined();
|
expect(aRecord).toBeUndefined();
|
||||||
expect(cnameRecord).toBeUndefined();
|
expect(cnameRecord).toBeUndefined();
|
||||||
console.log(`Successfully removed A and CNAME records`);
|
console.log(`Successfully removed A and CNAME records`);
|
||||||
@ -232,7 +234,7 @@ tap.test('.purgeZone() -> should purge zone cache', async (tools) => {
|
|||||||
// Worker tests
|
// Worker tests
|
||||||
tap.test('should list workers', async (tools) => {
|
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.listWorkerScripts();
|
||||||
expect(workerArray).toBeTypeOf('array');
|
expect(workerArray).toBeTypeOf('array');
|
||||||
@ -246,7 +248,7 @@ tap.test('should list workers', async (tools) => {
|
|||||||
|
|
||||||
tap.test('should create a worker', async (tools) => {
|
tap.test('should create a worker', async (tools) => {
|
||||||
tools.timeout(600000);
|
tools.timeout(600000);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const worker = await testCloudflareAccount.workerManager.createWorker(
|
const worker = await testCloudflareAccount.workerManager.createWorker(
|
||||||
testWorkerName,
|
testWorkerName,
|
||||||
@ -254,13 +256,13 @@ tap.test('should create a worker', async (tools) => {
|
|||||||
event.respondWith(new Response('Hello from Cloudflare Workers!', {
|
event.respondWith(new Response('Hello from Cloudflare Workers!', {
|
||||||
headers: { 'content-type': 'text/plain' }
|
headers: { 'content-type': 'text/plain' }
|
||||||
}))
|
}))
|
||||||
})`
|
})`,
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(worker).toBeTypeOf('object');
|
expect(worker).toBeTypeOf('object');
|
||||||
expect(worker.id).toEqual(testWorkerName);
|
expect(worker.id).toEqual(testWorkerName);
|
||||||
console.log(`Created worker: ${testWorkerName}`);
|
console.log(`Created worker: ${testWorkerName}`);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Set routes for the worker
|
// Set routes for the worker
|
||||||
await worker.setRoutes([
|
await worker.setRoutes([
|
||||||
@ -269,7 +271,7 @@ tap.test('should create a worker', async (tools) => {
|
|||||||
pattern: `https://${testWorkerName}.bleu.de/*`,
|
pattern: `https://${testWorkerName}.bleu.de/*`,
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
console.log(`Set routes for worker ${testWorkerName}`);
|
console.log(`Set routes for worker ${testWorkerName}`);
|
||||||
} catch (routeError) {
|
} catch (routeError) {
|
||||||
console.error(`Error setting routes: ${routeError.message}`);
|
console.error(`Error setting routes: ${routeError.message}`);
|
||||||
@ -284,7 +286,7 @@ tap.test('should create a worker', async (tools) => {
|
|||||||
|
|
||||||
tap.test('should get a specific worker by name', async (tools) => {
|
tap.test('should get a specific worker by name', async (tools) => {
|
||||||
tools.timeout(600000);
|
tools.timeout(600000);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// First create a worker to ensure it exists
|
// First create a worker to ensure it exists
|
||||||
await testCloudflareAccount.workerManager.createWorker(
|
await testCloudflareAccount.workerManager.createWorker(
|
||||||
@ -293,12 +295,12 @@ tap.test('should get a specific worker by name', async (tools) => {
|
|||||||
event.respondWith(new Response('Hello from Cloudflare Workers!', {
|
event.respondWith(new Response('Hello from Cloudflare Workers!', {
|
||||||
headers: { 'content-type': 'text/plain' }
|
headers: { 'content-type': 'text/plain' }
|
||||||
}))
|
}))
|
||||||
})`
|
})`,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Now get the worker
|
// Now get the worker
|
||||||
const worker = await testCloudflareAccount.workerManager.getWorker(testWorkerName);
|
const worker = await testCloudflareAccount.workerManager.getWorker(testWorkerName);
|
||||||
|
|
||||||
expect(worker).toBeTypeOf('object');
|
expect(worker).toBeTypeOf('object');
|
||||||
expect(worker?.id).toEqual(testWorkerName);
|
expect(worker?.id).toEqual(testWorkerName);
|
||||||
console.log(`Successfully retrieved worker: ${testWorkerName}`);
|
console.log(`Successfully retrieved worker: ${testWorkerName}`);
|
||||||
@ -311,17 +313,17 @@ tap.test('should get a specific worker by name', async (tools) => {
|
|||||||
|
|
||||||
tap.test('should update worker script', async (tools) => {
|
tap.test('should update worker script', async (tools) => {
|
||||||
tools.timeout(600000);
|
tools.timeout(600000);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const worker = await testCloudflareAccount.workerManager.getWorker(testWorkerName);
|
const worker = await testCloudflareAccount.workerManager.getWorker(testWorkerName);
|
||||||
|
|
||||||
if (worker) {
|
if (worker) {
|
||||||
await worker.updateScript(`addEventListener('fetch', event => {
|
await worker.updateScript(`addEventListener('fetch', event => {
|
||||||
event.respondWith(new Response('Updated Worker Script!', {
|
event.respondWith(new Response('Updated Worker Script!', {
|
||||||
headers: { 'content-type': 'text/plain' }
|
headers: { 'content-type': 'text/plain' }
|
||||||
}))
|
}))
|
||||||
})`);
|
})`);
|
||||||
|
|
||||||
console.log(`Updated script for worker ${testWorkerName}`);
|
console.log(`Updated script for worker ${testWorkerName}`);
|
||||||
expect(true).toBeTrue();
|
expect(true).toBeTrue();
|
||||||
} else {
|
} else {
|
||||||
@ -338,10 +340,10 @@ tap.test('should update worker script', async (tools) => {
|
|||||||
|
|
||||||
tap.test('should delete the test worker', async (tools) => {
|
tap.test('should delete the test worker', async (tools) => {
|
||||||
tools.timeout(600000);
|
tools.timeout(600000);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const worker = await testCloudflareAccount.workerManager.getWorker(testWorkerName);
|
const worker = await testCloudflareAccount.workerManager.getWorker(testWorkerName);
|
||||||
|
|
||||||
if (worker) {
|
if (worker) {
|
||||||
const result = await worker.delete();
|
const result = await worker.delete();
|
||||||
console.log(`Deleted worker: ${testWorkerName}`);
|
console.log(`Deleted worker: ${testWorkerName}`);
|
||||||
@ -381,4 +383,4 @@ tap.test('should format TTL values', async () => {
|
|||||||
expect(cloudflare.CloudflareUtils.formatTtl(999)).toEqual('999 seconds');
|
expect(cloudflare.CloudflareUtils.formatTtl(999)).toEqual('999 seconds');
|
||||||
});
|
});
|
||||||
|
|
||||||
tap.start();
|
tap.start();
|
||||||
|
@ -3,6 +3,6 @@
|
|||||||
*/
|
*/
|
||||||
export const commitinfo = {
|
export const commitinfo = {
|
||||||
name: '@apiclient.xyz/cloudflare',
|
name: '@apiclient.xyz/cloudflare',
|
||||||
version: '6.4.0',
|
version: '6.4.1',
|
||||||
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.'
|
||||||
}
|
}
|
||||||
|
@ -39,13 +39,13 @@ export class CloudflareAccount implements plugins.tsclass.network.IConvenientDns
|
|||||||
method: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH',
|
method: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH',
|
||||||
endpoint: string,
|
endpoint: string,
|
||||||
data?: any,
|
data?: any,
|
||||||
customHeaders?: Record<string, string>
|
customHeaders?: Record<string, string>,
|
||||||
): Promise<T> {
|
): Promise<T> {
|
||||||
try {
|
try {
|
||||||
const options: plugins.smartrequest.ISmartRequestOptions = {
|
const options: plugins.smartrequest.ISmartRequestOptions = {
|
||||||
method,
|
method,
|
||||||
headers: {
|
headers: {
|
||||||
'Authorization': `Bearer ${this.authToken}`,
|
Authorization: `Bearer ${this.authToken}`,
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
...customHeaders,
|
...customHeaders,
|
||||||
},
|
},
|
||||||
@ -62,13 +62,16 @@ export class CloudflareAccount implements plugins.tsclass.network.IConvenientDns
|
|||||||
}
|
}
|
||||||
|
|
||||||
logger.log('debug', `Making ${method} request to ${endpoint}`);
|
logger.log('debug', `Making ${method} request to ${endpoint}`);
|
||||||
const response = await plugins.smartrequest.request(`https://api.cloudflare.com/client/v4${endpoint}`, options);
|
const response = await plugins.smartrequest.request(
|
||||||
|
`https://api.cloudflare.com/client/v4${endpoint}`,
|
||||||
|
options,
|
||||||
|
);
|
||||||
|
|
||||||
// Check if response is already an object (might happen with newer smartrequest versions)
|
// Check if response is already an object (might happen with newer smartrequest versions)
|
||||||
if (typeof response.body === 'object' && response.body !== null) {
|
if (typeof response.body === 'object' && response.body !== null) {
|
||||||
return response.body;
|
return response.body;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Otherwise try to parse as JSON
|
// Otherwise try to parse as JSON
|
||||||
try {
|
try {
|
||||||
if (typeof response.body === 'string' && response.body.trim()) {
|
if (typeof response.body === 'string' && response.body.trim()) {
|
||||||
@ -80,13 +83,15 @@ export class CloudflareAccount implements plugins.tsclass.network.IConvenientDns
|
|||||||
}
|
}
|
||||||
} catch (parseError) {
|
} catch (parseError) {
|
||||||
logger.log('warn', `Failed to parse response as JSON: ${parseError.message}`);
|
logger.log('warn', `Failed to parse response as JSON: ${parseError.message}`);
|
||||||
|
|
||||||
// Create a fake response object to maintain expected structure
|
// Create a fake response object to maintain expected structure
|
||||||
return {
|
return {
|
||||||
result: [],
|
result: [],
|
||||||
success: true,
|
success: true,
|
||||||
errors: [],
|
errors: [],
|
||||||
messages: [`Failed to parse: ${typeof response.body === 'string' ? response.body?.substring(0, 50) : typeof response.body}...`]
|
messages: [
|
||||||
|
`Failed to parse: ${typeof response.body === 'string' ? response.body?.substring(0, 50) : typeof response.body}...`,
|
||||||
|
],
|
||||||
} as T;
|
} as T;
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@ -115,12 +120,12 @@ export class CloudflareAccount implements plugins.tsclass.network.IConvenientDns
|
|||||||
listAccounts: async () => {
|
listAccounts: async () => {
|
||||||
try {
|
try {
|
||||||
const accounts: plugins.ICloudflareTypes['Account'][] = [];
|
const accounts: plugins.ICloudflareTypes['Account'][] = [];
|
||||||
|
|
||||||
// Collect all accounts using async iterator
|
// Collect all accounts using async iterator
|
||||||
for await (const account of this.apiAccount.accounts.list()) {
|
for await (const account of this.apiAccount.accounts.list()) {
|
||||||
accounts.push(account as interfaces.ICloudflareApiAccountObject);
|
accounts.push(account as interfaces.ICloudflareApiAccountObject);
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.log('info', `Found ${accounts.length} accounts`);
|
logger.log('info', `Found ${accounts.length} accounts`);
|
||||||
return accounts;
|
return accounts;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@ -152,21 +157,24 @@ export class CloudflareAccount implements plugins.tsclass.network.IConvenientDns
|
|||||||
*/
|
*/
|
||||||
getRecord: async (
|
getRecord: async (
|
||||||
domainNameArg: string,
|
domainNameArg: string,
|
||||||
typeArg: plugins.tsclass.network.TDnsRecordType
|
typeArg: plugins.tsclass.network.TDnsRecordType,
|
||||||
): Promise<plugins.ICloudflareTypes['Record'] | undefined> => {
|
): Promise<plugins.ICloudflareTypes['Record'] | undefined> => {
|
||||||
try {
|
try {
|
||||||
const domain = new plugins.smartstring.Domain(domainNameArg);
|
const domain = new plugins.smartstring.Domain(domainNameArg);
|
||||||
const recordArrayArg = await this.convenience.listRecords(domain.zoneName);
|
const recordArrayArg = await this.convenience.listRecords(domain.zoneName);
|
||||||
|
|
||||||
if (!Array.isArray(recordArrayArg)) {
|
if (!Array.isArray(recordArrayArg)) {
|
||||||
logger.log('warn', `Expected records array for ${domainNameArg} but got ${typeof recordArrayArg}`);
|
logger.log(
|
||||||
|
'warn',
|
||||||
|
`Expected records array for ${domainNameArg} but got ${typeof recordArrayArg}`,
|
||||||
|
);
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
const filteredResponse = recordArrayArg.filter((recordArg) => {
|
const filteredResponse = recordArrayArg.filter((recordArg) => {
|
||||||
return recordArg.type === typeArg && recordArg.name === domainNameArg;
|
return recordArg.type === typeArg && recordArg.name === domainNameArg;
|
||||||
});
|
});
|
||||||
|
|
||||||
return filteredResponse.length > 0 ? filteredResponse[0] : undefined;
|
return filteredResponse.length > 0 ? filteredResponse[0] : undefined;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.log('error', `Error getting record for ${domainNameArg}: ${error.message}`);
|
logger.log('error', `Error getting record for ${domainNameArg}: ${error.message}`);
|
||||||
@ -180,7 +188,7 @@ export class CloudflareAccount implements plugins.tsclass.network.IConvenientDns
|
|||||||
domainNameArg: string,
|
domainNameArg: string,
|
||||||
typeArg: plugins.tsclass.network.TDnsRecordType,
|
typeArg: plugins.tsclass.network.TDnsRecordType,
|
||||||
contentArg: string,
|
contentArg: string,
|
||||||
ttlArg = 1
|
ttlArg = 1,
|
||||||
): Promise<any> => {
|
): Promise<any> => {
|
||||||
const domain = new plugins.smartstring.Domain(domainNameArg);
|
const domain = new plugins.smartstring.Domain(domainNameArg);
|
||||||
const zoneId = await this.convenience.getZoneId(domain.zoneName);
|
const zoneId = await this.convenience.getZoneId(domain.zoneName);
|
||||||
@ -190,7 +198,7 @@ export class CloudflareAccount implements plugins.tsclass.network.IConvenientDns
|
|||||||
name: domain.fullName,
|
name: domain.fullName,
|
||||||
content: contentArg,
|
content: contentArg,
|
||||||
ttl: ttlArg,
|
ttl: ttlArg,
|
||||||
})
|
});
|
||||||
return response;
|
return response;
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
@ -200,7 +208,7 @@ export class CloudflareAccount implements plugins.tsclass.network.IConvenientDns
|
|||||||
*/
|
*/
|
||||||
removeRecord: async (
|
removeRecord: async (
|
||||||
domainNameArg: string,
|
domainNameArg: string,
|
||||||
typeArg: plugins.tsclass.network.TDnsRecordType
|
typeArg: plugins.tsclass.network.TDnsRecordType,
|
||||||
): Promise<any> => {
|
): Promise<any> => {
|
||||||
const domain = new plugins.smartstring.Domain(domainNameArg);
|
const domain = new plugins.smartstring.Domain(domainNameArg);
|
||||||
const zoneId = await this.convenience.getZoneId(domain.zoneName);
|
const zoneId = await this.convenience.getZoneId(domain.zoneName);
|
||||||
@ -228,22 +236,28 @@ export class CloudflareAccount implements plugins.tsclass.network.IConvenientDns
|
|||||||
logger.log('info', `Cleaning ${typeArg} records for ${domainNameArg}`);
|
logger.log('info', `Cleaning ${typeArg} records for ${domainNameArg}`);
|
||||||
const domain = new plugins.smartstring.Domain(domainNameArg);
|
const domain = new plugins.smartstring.Domain(domainNameArg);
|
||||||
const zoneId = await this.convenience.getZoneId(domain.zoneName);
|
const zoneId = await this.convenience.getZoneId(domain.zoneName);
|
||||||
|
|
||||||
// List all records in the zone for this domain
|
// List all records in the zone for this domain
|
||||||
const records = await this.convenience.listRecords(domain.zoneName);
|
const records = await this.convenience.listRecords(domain.zoneName);
|
||||||
|
|
||||||
if (!Array.isArray(records)) {
|
if (!Array.isArray(records)) {
|
||||||
logger.log('warn', `Expected records array for ${domainNameArg} but got ${typeof records}`);
|
logger.log(
|
||||||
|
'warn',
|
||||||
|
`Expected records array for ${domainNameArg} but got ${typeof records}`,
|
||||||
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only delete records matching the specified name and type
|
// Only delete records matching the specified name and type
|
||||||
const recordsToDelete = records.filter((recordArg) => {
|
const recordsToDelete = records.filter((recordArg) => {
|
||||||
return recordArg.type === typeArg && recordArg.name === domainNameArg;
|
return recordArg.type === typeArg && recordArg.name === domainNameArg;
|
||||||
});
|
});
|
||||||
|
|
||||||
logger.log('info', `Found ${recordsToDelete.length} ${typeArg} records to delete for ${domainNameArg}`);
|
logger.log(
|
||||||
|
'info',
|
||||||
|
`Found ${recordsToDelete.length} ${typeArg} records to delete for ${domainNameArg}`,
|
||||||
|
);
|
||||||
|
|
||||||
for (const recordToDelete of recordsToDelete) {
|
for (const recordToDelete of recordsToDelete) {
|
||||||
try {
|
try {
|
||||||
// The official client might have different property locations
|
// The official client might have different property locations
|
||||||
@ -253,7 +267,7 @@ export class CloudflareAccount implements plugins.tsclass.network.IConvenientDns
|
|||||||
logger.log('warn', `Record ID not found for ${domainNameArg} record`);
|
logger.log('warn', `Record ID not found for ${domainNameArg} record`);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.apiAccount.dns.records.delete(recordId, {
|
await this.apiAccount.dns.records.delete(recordId, {
|
||||||
zone_id: zoneId,
|
zone_id: zoneId,
|
||||||
});
|
});
|
||||||
@ -263,7 +277,10 @@ export class CloudflareAccount implements plugins.tsclass.network.IConvenientDns
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.log('error', `Error cleaning ${typeArg} records for ${domainNameArg}: ${error.message}`);
|
logger.log(
|
||||||
|
'error',
|
||||||
|
`Error cleaning ${typeArg} records for ${domainNameArg}: ${error.message}`,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -279,19 +296,22 @@ export class CloudflareAccount implements plugins.tsclass.network.IConvenientDns
|
|||||||
domainNameArg: string,
|
domainNameArg: string,
|
||||||
typeArg: plugins.tsclass.network.TDnsRecordType,
|
typeArg: plugins.tsclass.network.TDnsRecordType,
|
||||||
contentArg: string,
|
contentArg: string,
|
||||||
ttlArg: number = 1
|
ttlArg: number = 1,
|
||||||
): Promise<plugins.ICloudflareTypes['Record']> => {
|
): 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);
|
const zoneId = await this.convenience.getZoneId(domain.zoneName);
|
||||||
|
|
||||||
// Find existing record
|
// Find existing record
|
||||||
const record = await this.convenience.getRecord(domainNameArg, typeArg);
|
const record = await this.convenience.getRecord(domainNameArg, typeArg);
|
||||||
|
|
||||||
if (!record) {
|
if (!record) {
|
||||||
logger.log('warn', `Record ${domainNameArg} of type ${typeArg} not found for update, creating instead`);
|
logger.log(
|
||||||
|
'warn',
|
||||||
|
`Record ${domainNameArg} of type ${typeArg} not found for update, creating instead`,
|
||||||
|
);
|
||||||
return this.convenience.createRecord(domainNameArg, typeArg, contentArg, ttlArg);
|
return this.convenience.createRecord(domainNameArg, typeArg, contentArg, ttlArg);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update the record - cast to any to access the id property
|
// Update the record - cast to any to access the id property
|
||||||
const recordId = (record as any).id;
|
const recordId = (record as any).id;
|
||||||
const updatedRecord = await this.apiAccount.dns.records.edit(recordId, {
|
const updatedRecord = await this.apiAccount.dns.records.edit(recordId, {
|
||||||
@ -299,9 +319,9 @@ export class CloudflareAccount implements plugins.tsclass.network.IConvenientDns
|
|||||||
type: typeArg as any,
|
type: typeArg as any,
|
||||||
name: domain.fullName,
|
name: domain.fullName,
|
||||||
content: contentArg,
|
content: contentArg,
|
||||||
ttl: ttlArg
|
ttl: ttlArg,
|
||||||
});
|
});
|
||||||
|
|
||||||
return updatedRecord;
|
return updatedRecord;
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
@ -313,14 +333,14 @@ export class CloudflareAccount implements plugins.tsclass.network.IConvenientDns
|
|||||||
const domain = new plugins.smartstring.Domain(domainNameArg);
|
const domain = new plugins.smartstring.Domain(domainNameArg);
|
||||||
const zoneId = await this.convenience.getZoneId(domain.zoneName);
|
const zoneId = await this.convenience.getZoneId(domain.zoneName);
|
||||||
const records: plugins.ICloudflareTypes['Record'][] = [];
|
const records: plugins.ICloudflareTypes['Record'][] = [];
|
||||||
|
|
||||||
// Collect all records using async iterator
|
// Collect all records using async iterator
|
||||||
for await (const record of this.apiAccount.dns.records.list({
|
for await (const record of this.apiAccount.dns.records.list({
|
||||||
zone_id: zoneId,
|
zone_id: zoneId,
|
||||||
})) {
|
})) {
|
||||||
records.push(record);
|
records.push(record);
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.log('info', `Found ${records.length} DNS records for ${domainNameArg}`);
|
logger.log('info', `Found ${records.length} DNS records for ${domainNameArg}`);
|
||||||
return records;
|
return records;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@ -338,15 +358,18 @@ export class CloudflareAccount implements plugins.tsclass.network.IConvenientDns
|
|||||||
if (domainName) {
|
if (domainName) {
|
||||||
options.name = domainName;
|
options.name = domainName;
|
||||||
}
|
}
|
||||||
|
|
||||||
const zones: plugins.ICloudflareTypes['Zone'][] = [];
|
const zones: plugins.ICloudflareTypes['Zone'][] = [];
|
||||||
|
|
||||||
// Collect all zones using async iterator
|
// Collect all zones using async iterator
|
||||||
for await (const zone of this.apiAccount.zones.list(options)) {
|
for await (const zone of this.apiAccount.zones.list(options)) {
|
||||||
zones.push(zone);
|
zones.push(zone);
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.log('info', `Found ${zones.length} zones${domainName ? ` matching ${domainName}` : ''}`);
|
logger.log(
|
||||||
|
'info',
|
||||||
|
`Found ${zones.length} zones${domainName ? ` matching ${domainName}` : ''}`,
|
||||||
|
);
|
||||||
return zones;
|
return zones;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.log('error', `Failed to list zones: ${error.message}`);
|
logger.log('error', `Failed to list zones: ${error.message}`);
|
||||||
@ -390,11 +413,11 @@ export class CloudflareAccount implements plugins.tsclass.network.IConvenientDns
|
|||||||
dnsChallenge.hostName,
|
dnsChallenge.hostName,
|
||||||
'TXT',
|
'TXT',
|
||||||
dnsChallenge.challenge,
|
dnsChallenge.challenge,
|
||||||
120
|
120,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
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');
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -22,7 +22,9 @@ export class CloudflareRecord {
|
|||||||
* @param apiObject Cloudflare DNS record API object
|
* @param apiObject Cloudflare DNS record API object
|
||||||
* @returns CloudflareRecord instance
|
* @returns CloudflareRecord instance
|
||||||
*/
|
*/
|
||||||
public static createFromApiObject(apiObject: plugins.ICloudflareTypes['Record']): CloudflareRecord {
|
public static createFromApiObject(
|
||||||
|
apiObject: plugins.ICloudflareTypes['Record'],
|
||||||
|
): CloudflareRecord {
|
||||||
const record = new CloudflareRecord();
|
const record = new CloudflareRecord();
|
||||||
Object.assign(record, apiObject);
|
Object.assign(record, apiObject);
|
||||||
return record;
|
return record;
|
||||||
@ -52,28 +54,28 @@ export class CloudflareRecord {
|
|||||||
public async update(
|
public async update(
|
||||||
cloudflareAccount: any,
|
cloudflareAccount: any,
|
||||||
newContent: string,
|
newContent: string,
|
||||||
ttl?: number
|
ttl?: number,
|
||||||
): Promise<CloudflareRecord> {
|
): Promise<CloudflareRecord> {
|
||||||
logger.log('info', `Updating record ${this.name} (${this.type}) with new content`);
|
logger.log('info', `Updating record ${this.name} (${this.type}) with new content`);
|
||||||
|
|
||||||
const updatedRecord = await cloudflareAccount.apiAccount.dns.records.edit(this.id, {
|
const updatedRecord = await cloudflareAccount.apiAccount.dns.records.edit(this.id, {
|
||||||
zone_id: this.zone_id,
|
zone_id: this.zone_id,
|
||||||
type: this.type as any,
|
type: this.type as any,
|
||||||
name: this.name,
|
name: this.name,
|
||||||
content: newContent,
|
content: newContent,
|
||||||
ttl: ttl || this.ttl,
|
ttl: ttl || this.ttl,
|
||||||
proxied: this.proxied
|
proxied: this.proxied,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Update this instance
|
// Update this instance
|
||||||
this.content = newContent;
|
this.content = newContent;
|
||||||
if (ttl) {
|
if (ttl) {
|
||||||
this.ttl = ttl;
|
this.ttl = ttl;
|
||||||
}
|
}
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Delete this record
|
* Delete this record
|
||||||
* @param cloudflareAccount The Cloudflare account to use
|
* @param cloudflareAccount The Cloudflare account to use
|
||||||
@ -82,15 +84,15 @@ export class CloudflareRecord {
|
|||||||
public async delete(cloudflareAccount: any): Promise<boolean> {
|
public async delete(cloudflareAccount: any): Promise<boolean> {
|
||||||
try {
|
try {
|
||||||
logger.log('info', `Deleting record ${this.name} (${this.type})`);
|
logger.log('info', `Deleting record ${this.name} (${this.type})`);
|
||||||
|
|
||||||
await cloudflareAccount.apiAccount.dns.records.delete(this.id, {
|
await cloudflareAccount.apiAccount.dns.records.delete(this.id, {
|
||||||
zone_id: this.zone_id
|
zone_id: this.zone_id,
|
||||||
});
|
});
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.log('error', `Failed to delete record: ${error.message}`);
|
logger.log('error', `Failed to delete record: ${error.message}`);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,7 +16,7 @@ export class CloudflareWorker {
|
|||||||
// STATIC
|
// STATIC
|
||||||
public static async fromApiObject(
|
public static async fromApiObject(
|
||||||
workerManager: WorkerManager,
|
workerManager: WorkerManager,
|
||||||
apiObject
|
apiObject,
|
||||||
): Promise<CloudflareWorker> {
|
): Promise<CloudflareWorker> {
|
||||||
const newWorker = new CloudflareWorker(workerManager);
|
const newWorker = new CloudflareWorker(workerManager);
|
||||||
Object.assign(newWorker, apiObject);
|
Object.assign(newWorker, apiObject);
|
||||||
@ -46,33 +46,33 @@ export class CloudflareWorker {
|
|||||||
public async getRoutes() {
|
public async getRoutes() {
|
||||||
try {
|
try {
|
||||||
this.routes = []; // Reset routes before fetching
|
this.routes = []; // Reset routes before fetching
|
||||||
|
|
||||||
// Get all zones using the async iterator
|
// Get all zones using the async iterator
|
||||||
const zones: plugins.ICloudflareTypes['Zone'][] = [];
|
const zones: plugins.ICloudflareTypes['Zone'][] = [];
|
||||||
for await (const zone of this.workerManager.cfAccount.apiAccount.zones.list()) {
|
for await (const zone of this.workerManager.cfAccount.apiAccount.zones.list()) {
|
||||||
zones.push(zone);
|
zones.push(zone);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (zones.length === 0) {
|
if (zones.length === 0) {
|
||||||
logger.log('warn', 'No zones found for the account');
|
logger.log('warn', 'No zones found for the account');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const zone of zones) {
|
for (const zone of zones) {
|
||||||
try {
|
try {
|
||||||
if (!zone || !zone.id) {
|
if (!zone || !zone.id) {
|
||||||
logger.log('warn', 'Zone is missing ID property');
|
logger.log('warn', 'Zone is missing ID property');
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get worker routes for this zone
|
// Get worker routes for this zone
|
||||||
const apiRoutes = [];
|
const apiRoutes = [];
|
||||||
for await (const route of this.workerManager.cfAccount.apiAccount.workers.routes.list({
|
for await (const route of this.workerManager.cfAccount.apiAccount.workers.routes.list({
|
||||||
zone_id: zone.id
|
zone_id: zone.id,
|
||||||
})) {
|
})) {
|
||||||
apiRoutes.push(route);
|
apiRoutes.push(route);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Filter for routes that match this worker's ID
|
// Filter for routes that match this worker's ID
|
||||||
for (const route of apiRoutes) {
|
for (const route of apiRoutes) {
|
||||||
if (route.script === this.id) {
|
if (route.script === this.id) {
|
||||||
@ -81,10 +81,13 @@ export class CloudflareWorker {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.log('error', `Failed to get worker routes for zone ${zone.name || zone.id}: ${error.message}`);
|
logger.log(
|
||||||
|
'error',
|
||||||
|
`Failed to get worker routes for zone ${zone.name || zone.id}: ${error.message}`,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.log('info', `Found ${this.routes.length} routes for worker ${this.id}`);
|
logger.log('info', `Found ${this.routes.length} routes for worker ${this.id}`);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.log('error', `Failed to get routes for worker ${this.id}: ${error.message}`);
|
logger.log('error', `Failed to get routes for worker ${this.id}: ${error.message}`);
|
||||||
@ -100,60 +103,62 @@ 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.getRoutes();
|
||||||
|
|
||||||
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
|
||||||
let routeStatus: 'new' | 'needsUpdate' | 'alreadyUpToDate' = 'new';
|
let routeStatus: 'new' | 'needsUpdate' | 'alreadyUpToDate' = 'new';
|
||||||
let existingRouteId: string;
|
let existingRouteId: string;
|
||||||
|
|
||||||
for (const existingRoute of this.routes) {
|
for (const existingRoute of this.routes) {
|
||||||
if (existingRoute.pattern === newRoute.pattern) {
|
if (existingRoute.pattern === newRoute.pattern) {
|
||||||
routeStatus = 'needsUpdate';
|
routeStatus = 'needsUpdate';
|
||||||
existingRouteId = existingRoute.id;
|
existingRouteId = existingRoute.id;
|
||||||
|
|
||||||
if (existingRoute.script === this.id) {
|
if (existingRoute.script === this.id) {
|
||||||
routeStatus = 'alreadyUpToDate';
|
routeStatus = 'alreadyUpToDate';
|
||||||
logger.log('info', `Route ${newRoute.pattern} already exists, no update needed`);
|
logger.log('info', `Route ${newRoute.pattern} already exists, no update needed`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Get the zone ID
|
// Get the zone ID
|
||||||
const zone = await this.workerManager.cfAccount.zoneManager.getZoneByName(newRoute.zoneName);
|
const zone = await this.workerManager.cfAccount.zoneManager.getZoneByName(
|
||||||
|
newRoute.zoneName,
|
||||||
|
);
|
||||||
|
|
||||||
if (!zone) {
|
if (!zone) {
|
||||||
logger.log('error', `Zone ${newRoute.zoneName} not found`);
|
logger.log('error', `Zone ${newRoute.zoneName} not found`);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle route creation, update, or skip if already up to date
|
// Handle route creation, update, or skip if already up to date
|
||||||
if (routeStatus === 'new') {
|
if (routeStatus === 'new') {
|
||||||
await this.workerManager.cfAccount.apiAccount.workers.routes.create({
|
await this.workerManager.cfAccount.apiAccount.workers.routes.create({
|
||||||
zone_id: zone.id,
|
zone_id: zone.id,
|
||||||
pattern: newRoute.pattern,
|
pattern: newRoute.pattern,
|
||||||
script: this.id
|
script: this.id,
|
||||||
});
|
});
|
||||||
|
|
||||||
logger.log('info', `Created new route ${newRoute.pattern} for worker ${this.id}`);
|
logger.log('info', `Created new route ${newRoute.pattern} for worker ${this.id}`);
|
||||||
} else if (routeStatus === 'needsUpdate') {
|
} else if (routeStatus === 'needsUpdate') {
|
||||||
await this.workerManager.cfAccount.apiAccount.workers.routes.update(existingRouteId, {
|
await this.workerManager.cfAccount.apiAccount.workers.routes.update(existingRouteId, {
|
||||||
zone_id: zone.id,
|
zone_id: zone.id,
|
||||||
pattern: newRoute.pattern,
|
pattern: newRoute.pattern,
|
||||||
script: this.id
|
script: this.id,
|
||||||
});
|
});
|
||||||
|
|
||||||
logger.log('info', `Updated route ${newRoute.pattern} for worker ${this.id}`);
|
logger.log('info', `Updated route ${newRoute.pattern} for worker ${this.id}`);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.log('error', `Failed to set route ${newRoute.pattern}: ${error.message}`);
|
logger.log('error', `Failed to set route ${newRoute.pattern}: ${error.message}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Refresh routes after all changes
|
// Refresh routes after all changes
|
||||||
await this.getRoutes();
|
await this.getRoutes();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Upload or update worker script content
|
* Upload or update worker script content
|
||||||
* @param scriptContent The worker script content
|
* @param scriptContent The worker script content
|
||||||
@ -163,10 +168,10 @@ export class CloudflareWorker {
|
|||||||
if (!this.workerManager.cfAccount.preselectedAccountId) {
|
if (!this.workerManager.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.');
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
logger.log('info', `Updating script for worker ${this.id}`);
|
logger.log('info', `Updating script for worker ${this.id}`);
|
||||||
|
|
||||||
// Use the official client to update the script (upload new content)
|
// Use the official client to update the script (upload new content)
|
||||||
// Build params as any to include the script form part without TS errors
|
// Build params as any to include the script form part without TS errors
|
||||||
const updateParams: any = {
|
const updateParams: any = {
|
||||||
@ -175,23 +180,27 @@ export class CloudflareWorker {
|
|||||||
};
|
};
|
||||||
updateParams['CF-WORKER-BODY-PART'] = 'script';
|
updateParams['CF-WORKER-BODY-PART'] = 'script';
|
||||||
updateParams['script'] = scriptContent;
|
updateParams['script'] = scriptContent;
|
||||||
const updatedWorker = await this.workerManager.cfAccount.apiAccount.workers.scripts.content.update(this.id, updateParams);
|
const updatedWorker =
|
||||||
|
await this.workerManager.cfAccount.apiAccount.workers.scripts.content.update(
|
||||||
|
this.id,
|
||||||
|
updateParams,
|
||||||
|
);
|
||||||
|
|
||||||
// Update this instance with new data
|
// Update this instance with new data
|
||||||
if (updatedWorker && typeof updatedWorker === 'object') {
|
if (updatedWorker && typeof updatedWorker === 'object') {
|
||||||
Object.assign(this, updatedWorker);
|
Object.assign(this, updatedWorker);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Always ensure the script property is updated
|
// Always ensure the script property is updated
|
||||||
this.script = scriptContent;
|
this.script = scriptContent;
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.log('error', `Failed to update worker script: ${error.message}`);
|
logger.log('error', `Failed to update worker script: ${error.message}`);
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Delete this worker script
|
* Delete this worker script
|
||||||
* @returns True if deletion was successful
|
* @returns True if deletion was successful
|
||||||
@ -200,19 +209,19 @@ export class CloudflareWorker {
|
|||||||
if (!this.workerManager.cfAccount.preselectedAccountId) {
|
if (!this.workerManager.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.');
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
logger.log('info', `Deleting worker ${this.id}`);
|
logger.log('info', `Deleting worker ${this.id}`);
|
||||||
|
|
||||||
// Use the official client to delete the worker
|
// Use the official client to delete the worker
|
||||||
await this.workerManager.cfAccount.apiAccount.workers.scripts.delete(this.id, {
|
await this.workerManager.cfAccount.apiAccount.workers.scripts.delete(this.id, {
|
||||||
account_id: this.workerManager.cfAccount.preselectedAccountId
|
account_id: this.workerManager.cfAccount.preselectedAccountId,
|
||||||
});
|
});
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.log('error', `Failed to delete worker: ${error.message}`);
|
logger.log('error', `Failed to delete worker: ${error.message}`);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,7 @@ export class WorkerManager {
|
|||||||
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.');
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Use the official client to create/update the worker (upload script content)
|
// Use the official client to create/update the worker (upload script content)
|
||||||
// Build params as any to include the script form part without TS errors
|
// Build params as any to include the script form part without TS errors
|
||||||
@ -31,12 +31,12 @@ export class WorkerManager {
|
|||||||
contentParams['CF-WORKER-BODY-PART'] = 'script';
|
contentParams['CF-WORKER-BODY-PART'] = 'script';
|
||||||
contentParams['script'] = workerScript;
|
contentParams['script'] = workerScript;
|
||||||
await this.cfAccount.apiAccount.workers.scripts.content.update(workerName, contentParams);
|
await this.cfAccount.apiAccount.workers.scripts.content.update(workerName, contentParams);
|
||||||
|
|
||||||
// Create a new worker instance
|
// Create a new worker instance
|
||||||
const worker = new CloudflareWorker(this);
|
const worker = new CloudflareWorker(this);
|
||||||
worker.id = workerName;
|
worker.id = workerName;
|
||||||
worker.script = workerScript;
|
worker.script = workerScript;
|
||||||
|
|
||||||
// Initialize the worker and get its routes
|
// Initialize the worker and get its routes
|
||||||
try {
|
try {
|
||||||
await worker.getRoutes();
|
await worker.getRoutes();
|
||||||
@ -44,7 +44,7 @@ export class WorkerManager {
|
|||||||
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
|
||||||
}
|
}
|
||||||
|
|
||||||
return worker;
|
return worker;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.log('error', `Failed to create worker ${workerName}: ${error.message}`);
|
logger.log('error', `Failed to create worker ${workerName}: ${error.message}`);
|
||||||
@ -61,22 +61,22 @@ export class WorkerManager {
|
|||||||
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.');
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Get the worker script using the official client
|
// Get the worker script using the official client
|
||||||
const workerScript = await this.cfAccount.apiAccount.workers.scripts.get(workerName, {
|
const workerScript = await this.cfAccount.apiAccount.workers.scripts.get(workerName, {
|
||||||
account_id: this.cfAccount.preselectedAccountId
|
account_id: this.cfAccount.preselectedAccountId,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Create a new worker instance
|
// Create a new worker instance
|
||||||
const worker = new CloudflareWorker(this);
|
const worker = new CloudflareWorker(this);
|
||||||
worker.id = workerName;
|
worker.id = workerName;
|
||||||
|
|
||||||
// Save script content if available
|
// Save script content if available
|
||||||
if (workerScript && typeof workerScript === 'object') {
|
if (workerScript && typeof workerScript === 'object') {
|
||||||
Object.assign(worker, workerScript);
|
Object.assign(worker, workerScript);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize the worker and get its routes
|
// Initialize the worker and get its routes
|
||||||
try {
|
try {
|
||||||
await worker.getRoutes();
|
await worker.getRoutes();
|
||||||
@ -84,7 +84,7 @@ export class WorkerManager {
|
|||||||
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
|
||||||
}
|
}
|
||||||
|
|
||||||
return worker;
|
return worker;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.log('warn', `Worker '${workerName}' not found: ${error.message}`);
|
logger.log('warn', `Worker '${workerName}' not found: ${error.message}`);
|
||||||
@ -100,35 +100,35 @@ export class WorkerManager {
|
|||||||
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.');
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Collect all scripts using the new client's async iterator
|
// Collect all scripts using the new client's async iterator
|
||||||
const workerScripts: plugins.ICloudflareTypes['Script'][] = [];
|
const workerScripts: plugins.ICloudflareTypes['Script'][] = [];
|
||||||
|
|
||||||
try {
|
try {
|
||||||
for await (const script of this.cfAccount.apiAccount.workers.scripts.list({
|
for await (const script of this.cfAccount.apiAccount.workers.scripts.list({
|
||||||
account_id: this.cfAccount.preselectedAccountId,
|
account_id: this.cfAccount.preselectedAccountId,
|
||||||
})) {
|
})) {
|
||||||
workerScripts.push(script);
|
workerScripts.push(script);
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.log('info', `Found ${workerScripts.length} worker scripts`);
|
logger.log('info', `Found ${workerScripts.length} worker scripts`);
|
||||||
return workerScripts;
|
return workerScripts;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.log('warn', `Error while listing workers with async iterator: ${error.message}`);
|
logger.log('warn', `Error while listing workers with async iterator: ${error.message}`);
|
||||||
|
|
||||||
// Try alternative approach if the async iterator fails
|
// Try alternative approach if the async iterator fails
|
||||||
const result = await this.cfAccount.apiAccount.workers.scripts.list({
|
const result = (await this.cfAccount.apiAccount.workers.scripts.list({
|
||||||
account_id: this.cfAccount.preselectedAccountId,
|
account_id: this.cfAccount.preselectedAccountId,
|
||||||
}) as any;
|
})) as any;
|
||||||
|
|
||||||
// Check if the result has a 'result' property (older API response format)
|
// Check if the result has a 'result' property (older API response format)
|
||||||
if (result && result.result && Array.isArray(result.result)) {
|
if (result && result.result && Array.isArray(result.result)) {
|
||||||
logger.log('info', `Found ${result.result.length} worker scripts using direct result`);
|
logger.log('info', `Found ${result.result.length} worker scripts using direct result`);
|
||||||
return result.result;
|
return result.result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.log('warn', 'Could not retrieve worker scripts');
|
logger.log('warn', 'Could not retrieve worker scripts');
|
||||||
return [];
|
return [];
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@ -136,7 +136,7 @@ export class WorkerManager {
|
|||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deletes a worker script
|
* Deletes a worker script
|
||||||
* @param workerName Name of the worker to delete
|
* @param workerName Name of the worker to delete
|
||||||
@ -146,10 +146,10 @@ export class WorkerManager {
|
|||||||
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.');
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await this.cfAccount.apiAccount.workers.scripts.delete(workerName, {
|
await this.cfAccount.apiAccount.workers.scripts.delete(workerName, {
|
||||||
account_id: this.cfAccount.preselectedAccountId
|
account_id: this.cfAccount.preselectedAccountId,
|
||||||
});
|
});
|
||||||
logger.log('info', `Worker '${workerName}' deleted successfully`);
|
logger.log('info', `Worker '${workerName}' deleted successfully`);
|
||||||
return true;
|
return true;
|
||||||
@ -158,4 +158,4 @@ export class WorkerManager {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,7 +23,7 @@ export class CloudflareZone {
|
|||||||
public account: interfaces.ICflareZone['account'];
|
public account: interfaces.ICflareZone['account'];
|
||||||
public permissions: string[];
|
public permissions: string[];
|
||||||
public plan: interfaces.ICflareZone['plan'];
|
public plan: interfaces.ICflareZone['plan'];
|
||||||
|
|
||||||
private cfAccount?: CloudflareAccount; // Will be set when created through a manager
|
private cfAccount?: CloudflareAccount; // Will be set when created through a manager
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -33,19 +33,19 @@ export class CloudflareZone {
|
|||||||
* @returns CloudflareZone instance
|
* @returns CloudflareZone instance
|
||||||
*/
|
*/
|
||||||
public static createFromApiObject(
|
public static createFromApiObject(
|
||||||
apiObject: plugins.ICloudflareTypes['Zone'],
|
apiObject: plugins.ICloudflareTypes['Zone'],
|
||||||
cfAccount?: CloudflareAccount
|
cfAccount?: CloudflareAccount,
|
||||||
): CloudflareZone {
|
): CloudflareZone {
|
||||||
const cloudflareZone = new CloudflareZone();
|
const cloudflareZone = new CloudflareZone();
|
||||||
Object.assign(cloudflareZone, apiObject);
|
Object.assign(cloudflareZone, apiObject);
|
||||||
|
|
||||||
if (cfAccount) {
|
if (cfAccount) {
|
||||||
cloudflareZone.cfAccount = cfAccount;
|
cloudflareZone.cfAccount = cfAccount;
|
||||||
}
|
}
|
||||||
|
|
||||||
return cloudflareZone;
|
return cloudflareZone;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if development mode is currently active
|
* Check if development mode is currently active
|
||||||
* @returns True if development mode is active
|
* @returns True if development mode is active
|
||||||
@ -53,7 +53,7 @@ export class CloudflareZone {
|
|||||||
public isDevelopmentModeActive(): boolean {
|
public isDevelopmentModeActive(): boolean {
|
||||||
return this.development_mode > 0;
|
return this.development_mode > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enable development mode for the zone
|
* Enable development mode for the zone
|
||||||
* @param cfAccount Cloudflare account to use if not already set
|
* @param cfAccount Cloudflare account to use if not already set
|
||||||
@ -62,23 +62,23 @@ export class CloudflareZone {
|
|||||||
*/
|
*/
|
||||||
public async enableDevelopmentMode(
|
public async enableDevelopmentMode(
|
||||||
cfAccount?: CloudflareAccount,
|
cfAccount?: CloudflareAccount,
|
||||||
duration: number = 10800
|
duration: number = 10800,
|
||||||
): Promise<CloudflareZone> {
|
): Promise<CloudflareZone> {
|
||||||
const account = cfAccount || this.cfAccount;
|
const account = cfAccount || this.cfAccount;
|
||||||
if (!account) {
|
if (!account) {
|
||||||
throw new Error('CloudflareAccount is required to enable development mode');
|
throw new Error('CloudflareAccount is required to enable development mode');
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.log('info', `Enabling development mode for zone ${this.name}`);
|
logger.log('info', `Enabling development mode for zone ${this.name}`);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// The official client doesn't have a direct method for development mode
|
// The official client doesn't have a direct method for development mode
|
||||||
// We'll use the request method for this specific case
|
// We'll use the request method for this specific case
|
||||||
await account.request('PATCH', `/zones/${this.id}/settings/development_mode`, {
|
await account.request('PATCH', `/zones/${this.id}/settings/development_mode`, {
|
||||||
value: 'on',
|
value: 'on',
|
||||||
time: duration
|
time: duration,
|
||||||
});
|
});
|
||||||
|
|
||||||
this.development_mode = duration;
|
this.development_mode = duration;
|
||||||
return this;
|
return this;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@ -86,7 +86,7 @@ export class CloudflareZone {
|
|||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Disable development mode for the zone
|
* Disable development mode for the zone
|
||||||
* @param cfAccount Cloudflare account to use if not already set
|
* @param cfAccount Cloudflare account to use if not already set
|
||||||
@ -97,16 +97,16 @@ export class CloudflareZone {
|
|||||||
if (!account) {
|
if (!account) {
|
||||||
throw new Error('CloudflareAccount is required to disable development mode');
|
throw new Error('CloudflareAccount is required to disable development mode');
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.log('info', `Disabling development mode for zone ${this.name}`);
|
logger.log('info', `Disabling development mode for zone ${this.name}`);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// The official client doesn't have a direct method for development mode
|
// The official client doesn't have a direct method for development mode
|
||||||
// We'll use the request method for this specific case
|
// We'll use the request method for this specific case
|
||||||
await account.request('PATCH', `/zones/${this.id}/settings/development_mode`, {
|
await account.request('PATCH', `/zones/${this.id}/settings/development_mode`, {
|
||||||
value: 'off'
|
value: 'off',
|
||||||
});
|
});
|
||||||
|
|
||||||
this.development_mode = 0;
|
this.development_mode = 0;
|
||||||
return this;
|
return this;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@ -114,7 +114,7 @@ export class CloudflareZone {
|
|||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Purge all cached content for this zone
|
* Purge all cached content for this zone
|
||||||
* @param cfAccount Cloudflare account to use if not already set
|
* @param cfAccount Cloudflare account to use if not already set
|
||||||
@ -125,13 +125,13 @@ export class CloudflareZone {
|
|||||||
if (!account) {
|
if (!account) {
|
||||||
throw new Error('CloudflareAccount is required to purge cache');
|
throw new Error('CloudflareAccount is required to purge cache');
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.log('info', `Purging all cache for zone ${this.name}`);
|
logger.log('info', `Purging all cache for zone ${this.name}`);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await account.apiAccount.cache.purge({
|
await account.apiAccount.cache.purge({
|
||||||
zone_id: this.id,
|
zone_id: this.id,
|
||||||
purge_everything: true
|
purge_everything: true,
|
||||||
});
|
});
|
||||||
return true;
|
return true;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@ -139,7 +139,7 @@ export class CloudflareZone {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Purge specific URLs from the cache
|
* Purge specific URLs from the cache
|
||||||
* @param urls Array of URLs to purge
|
* @param urls Array of URLs to purge
|
||||||
@ -151,17 +151,17 @@ export class CloudflareZone {
|
|||||||
if (!account) {
|
if (!account) {
|
||||||
throw new Error('CloudflareAccount is required to purge URLs');
|
throw new Error('CloudflareAccount is required to purge URLs');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!urls.length) {
|
if (!urls.length) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.log('info', `Purging ${urls.length} URLs from cache for zone ${this.name}`);
|
logger.log('info', `Purging ${urls.length} URLs from cache for zone ${this.name}`);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await account.apiAccount.cache.purge({
|
await account.apiAccount.cache.purge({
|
||||||
zone_id: this.id,
|
zone_id: this.id,
|
||||||
files: urls
|
files: urls,
|
||||||
});
|
});
|
||||||
return true;
|
return true;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@ -169,7 +169,7 @@ export class CloudflareZone {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if the zone is active
|
* Check if the zone is active
|
||||||
* @returns True if the zone is active
|
* @returns True if the zone is active
|
||||||
@ -177,7 +177,7 @@ export class CloudflareZone {
|
|||||||
public isActive(): boolean {
|
public isActive(): boolean {
|
||||||
return this.status === 'active' && !this.paused;
|
return this.status === 'active' && !this.paused;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if the zone is using Cloudflare nameservers
|
* Check if the zone is using Cloudflare nameservers
|
||||||
* @returns True if using Cloudflare nameservers
|
* @returns True if using Cloudflare nameservers
|
||||||
@ -187,11 +187,11 @@ export class CloudflareZone {
|
|||||||
if (!this.original_name_servers || !this.name_servers) {
|
if (!this.original_name_servers || !this.name_servers) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If they're different, and current nameservers are Cloudflare's
|
// If they're different, and current nameservers are Cloudflare's
|
||||||
return this.name_servers.some(ns => ns.includes('cloudflare'));
|
return this.name_servers.some((ns) => ns.includes('cloudflare'));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update zone settings
|
* Update zone settings
|
||||||
* @param settings Settings to update
|
* @param settings Settings to update
|
||||||
@ -205,23 +205,23 @@ export class CloudflareZone {
|
|||||||
vanity_name_servers: string[];
|
vanity_name_servers: string[];
|
||||||
type: 'full' | 'partial' | 'secondary';
|
type: 'full' | 'partial' | 'secondary';
|
||||||
}>,
|
}>,
|
||||||
cfAccount?: CloudflareAccount
|
cfAccount?: CloudflareAccount,
|
||||||
): Promise<CloudflareZone> {
|
): Promise<CloudflareZone> {
|
||||||
const account = cfAccount || this.cfAccount;
|
const account = cfAccount || this.cfAccount;
|
||||||
if (!account) {
|
if (!account) {
|
||||||
throw new Error('CloudflareAccount is required to update zone settings');
|
throw new Error('CloudflareAccount is required to update zone settings');
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.log('info', `Updating settings for zone ${this.name}`);
|
logger.log('info', `Updating settings for zone ${this.name}`);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Use the request method instead of zones.edit to avoid type issues
|
// Use the request method instead of zones.edit to avoid type issues
|
||||||
const response: { result: interfaces.ICflareZone } = await account.request(
|
const response: { result: interfaces.ICflareZone } = await account.request(
|
||||||
'PATCH',
|
'PATCH',
|
||||||
`/zones/${this.id}`,
|
`/zones/${this.id}`,
|
||||||
settings
|
settings,
|
||||||
);
|
);
|
||||||
|
|
||||||
Object.assign(this, response.result);
|
Object.assign(this, response.result);
|
||||||
return this;
|
return this;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@ -229,4 +229,4 @@ export class CloudflareZone {
|
|||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,7 @@ export class ZoneManager {
|
|||||||
public async getZones(zoneName?: string): Promise<CloudflareZone[]> {
|
public async getZones(zoneName?: string): Promise<CloudflareZone[]> {
|
||||||
try {
|
try {
|
||||||
const options: any = { per_page: 50 };
|
const options: any = { per_page: 50 };
|
||||||
|
|
||||||
// May be optionally filtered by domain name
|
// May be optionally filtered by domain name
|
||||||
if (zoneName) {
|
if (zoneName) {
|
||||||
options.name = zoneName;
|
options.name = zoneName;
|
||||||
@ -29,14 +29,14 @@ export class ZoneManager {
|
|||||||
for await (const zone of this.cfAccount.apiAccount.zones.list(options)) {
|
for await (const zone of this.cfAccount.apiAccount.zones.list(options)) {
|
||||||
zones.push(zone);
|
zones.push(zone);
|
||||||
}
|
}
|
||||||
|
|
||||||
return zones.map(zone => CloudflareZone.createFromApiObject(zone, this.cfAccount));
|
return zones.map((zone) => CloudflareZone.createFromApiObject(zone, this.cfAccount));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.log('error', `Failed to fetch zones: ${error.message}`);
|
logger.log('error', `Failed to fetch zones: ${error.message}`);
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a single zone by name
|
* Get a single zone by name
|
||||||
* @param zoneName Zone name to find
|
* @param zoneName Zone name to find
|
||||||
@ -44,9 +44,9 @@ export class ZoneManager {
|
|||||||
*/
|
*/
|
||||||
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.getZones(zoneName);
|
||||||
return zones.find(zone => zone.name === zoneName);
|
return zones.find((zone) => zone.name === zoneName);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a zone by its ID
|
* Get a zone by its ID
|
||||||
* @param zoneId Zone ID to find
|
* @param zoneId Zone ID to find
|
||||||
@ -56,17 +56,17 @@ export class ZoneManager {
|
|||||||
try {
|
try {
|
||||||
// Use the request method instead of the zones.get method to avoid type issues
|
// Use the request method instead of the zones.get method to avoid type issues
|
||||||
const response: { result: interfaces.ICflareZone } = await this.cfAccount.request(
|
const response: { result: interfaces.ICflareZone } = await this.cfAccount.request(
|
||||||
'GET',
|
'GET',
|
||||||
`/zones/${zoneId}`
|
`/zones/${zoneId}`,
|
||||||
);
|
);
|
||||||
|
|
||||||
return CloudflareZone.createFromApiObject(response.result as any, this.cfAccount);
|
return CloudflareZone.createFromApiObject(response.result as any, this.cfAccount);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.log('error', `Failed to fetch zone with ID ${zoneId}: ${error.message}`);
|
logger.log('error', `Failed to fetch zone with ID ${zoneId}: ${error.message}`);
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new zone
|
* Create a new zone
|
||||||
* @param zoneName Name of the zone to create
|
* @param zoneName Name of the zone to create
|
||||||
@ -75,37 +75,37 @@ export class ZoneManager {
|
|||||||
* @returns The created zone
|
* @returns The created zone
|
||||||
*/
|
*/
|
||||||
public async createZone(
|
public async createZone(
|
||||||
zoneName: string,
|
zoneName: string,
|
||||||
jumpStart: boolean = false,
|
jumpStart: boolean = false,
|
||||||
accountId?: string
|
accountId?: string,
|
||||||
): Promise<CloudflareZone | undefined> {
|
): Promise<CloudflareZone | undefined> {
|
||||||
const useAccountId = accountId || this.cfAccount.preselectedAccountId;
|
const useAccountId = accountId || this.cfAccount.preselectedAccountId;
|
||||||
|
|
||||||
if (!useAccountId) {
|
if (!useAccountId) {
|
||||||
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.');
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
logger.log('info', `Creating zone ${zoneName}`);
|
logger.log('info', `Creating zone ${zoneName}`);
|
||||||
|
|
||||||
// Use the request method for more direct control over the parameters
|
// Use the request method for more direct control over the parameters
|
||||||
const response: { result: interfaces.ICflareZone } = await this.cfAccount.request(
|
const response: { result: interfaces.ICflareZone } = await this.cfAccount.request(
|
||||||
'POST',
|
'POST',
|
||||||
'/zones',
|
'/zones',
|
||||||
{
|
{
|
||||||
name: zoneName,
|
name: zoneName,
|
||||||
jump_start: jumpStart,
|
jump_start: jumpStart,
|
||||||
account: { id: useAccountId }
|
account: { id: useAccountId },
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
return CloudflareZone.createFromApiObject(response.result as any, this.cfAccount);
|
return CloudflareZone.createFromApiObject(response.result as any, this.cfAccount);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.log('error', `Failed to create zone ${zoneName}: ${error.message}`);
|
logger.log('error', `Failed to create zone ${zoneName}: ${error.message}`);
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Delete a zone
|
* Delete a zone
|
||||||
* @param zoneId ID of the zone to delete
|
* @param zoneId ID of the zone to delete
|
||||||
@ -114,7 +114,7 @@ export class ZoneManager {
|
|||||||
public async deleteZone(zoneId: string): Promise<boolean> {
|
public async deleteZone(zoneId: string): Promise<boolean> {
|
||||||
try {
|
try {
|
||||||
logger.log('info', `Deleting zone with ID ${zoneId}`);
|
logger.log('info', `Deleting zone with ID ${zoneId}`);
|
||||||
|
|
||||||
// Use the request method to avoid type issues
|
// Use the request method to avoid type issues
|
||||||
await this.cfAccount.request('DELETE', `/zones/${zoneId}`);
|
await this.cfAccount.request('DELETE', `/zones/${zoneId}`);
|
||||||
return true;
|
return true;
|
||||||
@ -123,7 +123,7 @@ export class ZoneManager {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if a zone exists
|
* Check if a zone exists
|
||||||
* @param zoneName Name of the zone to check
|
* @param zoneName Name of the zone to check
|
||||||
@ -131,9 +131,9 @@ export class ZoneManager {
|
|||||||
*/
|
*/
|
||||||
public async zoneExists(zoneName: string): Promise<boolean> {
|
public async zoneExists(zoneName: string): Promise<boolean> {
|
||||||
const zones = await this.getZones(zoneName);
|
const zones = await this.getZones(zoneName);
|
||||||
return zones.some(zone => zone.name === zoneName);
|
return zones.some((zone) => zone.name === zoneName);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Activate a zone (if it's in pending status)
|
* Activate a zone (if it's in pending status)
|
||||||
* @param zoneId ID of the zone to activate
|
* @param zoneId ID of the zone to activate
|
||||||
@ -142,23 +142,23 @@ export class ZoneManager {
|
|||||||
public async activateZone(zoneId: string): Promise<CloudflareZone | undefined> {
|
public async activateZone(zoneId: string): Promise<CloudflareZone | undefined> {
|
||||||
try {
|
try {
|
||||||
logger.log('info', `Activating zone with ID ${zoneId}`);
|
logger.log('info', `Activating zone with ID ${zoneId}`);
|
||||||
|
|
||||||
// Use the request method for better control
|
// Use the request method for better control
|
||||||
const response: { result: interfaces.ICflareZone } = await this.cfAccount.request(
|
const response: { result: interfaces.ICflareZone } = await this.cfAccount.request(
|
||||||
'PATCH',
|
'PATCH',
|
||||||
`/zones/${zoneId}`,
|
`/zones/${zoneId}`,
|
||||||
{
|
{
|
||||||
status: 'active'
|
status: 'active',
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
return CloudflareZone.createFromApiObject(response.result as any, this.cfAccount);
|
return CloudflareZone.createFromApiObject(response.result as any, this.cfAccount);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.log('error', `Failed to activate zone with ID ${zoneId}: ${error.message}`);
|
logger.log('error', `Failed to activate zone with ID ${zoneId}: ${error.message}`);
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check the activation status of a zone
|
* Check the activation status of a zone
|
||||||
* @param zoneId ID of the zone to check
|
* @param zoneId ID of the zone to check
|
||||||
@ -167,17 +167,17 @@ export class ZoneManager {
|
|||||||
public async checkZoneActivation(zoneId: string): Promise<CloudflareZone | undefined> {
|
public async checkZoneActivation(zoneId: string): Promise<CloudflareZone | undefined> {
|
||||||
try {
|
try {
|
||||||
logger.log('info', `Checking activation for zone with ID ${zoneId}`);
|
logger.log('info', `Checking activation for zone with ID ${zoneId}`);
|
||||||
|
|
||||||
// For this specific endpoint, we'll use the request method
|
// For this specific endpoint, we'll use the request method
|
||||||
const response: { result: interfaces.ICflareZone } = await this.cfAccount.request(
|
const response: { result: interfaces.ICflareZone } = await this.cfAccount.request(
|
||||||
'PUT',
|
'PUT',
|
||||||
`/zones/${zoneId}/activation_check`
|
`/zones/${zoneId}/activation_check`,
|
||||||
);
|
);
|
||||||
|
|
||||||
return CloudflareZone.createFromApiObject(response.result as any, this.cfAccount);
|
return CloudflareZone.createFromApiObject(response.result as any, this.cfAccount);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.log('error', `Failed to check zone activation with ID ${zoneId}: ${error.message}`);
|
logger.log('error', `Failed to check zone activation with ID ${zoneId}: ${error.message}`);
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,7 +16,7 @@ export class CloudflareUtils {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extracts the zone name (apex domain) from a full domain
|
* Extracts the zone name (apex domain) from a full domain
|
||||||
* @param domainName Domain name to process
|
* @param domainName Domain name to process
|
||||||
@ -31,7 +31,7 @@ export class CloudflareUtils {
|
|||||||
throw new Error(`Invalid domain name: ${domainName}`);
|
throw new Error(`Invalid domain name: ${domainName}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if a string is a valid Cloudflare API token
|
* Checks if a string is a valid Cloudflare API token
|
||||||
* @param token API token to validate
|
* @param token API token to validate
|
||||||
@ -41,7 +41,7 @@ export class CloudflareUtils {
|
|||||||
// Cloudflare API tokens are typically 40+ characters long and start with specific patterns
|
// Cloudflare API tokens are typically 40+ characters long and start with specific patterns
|
||||||
return /^[A-Za-z0-9_-]{40,}$/.test(token);
|
return /^[A-Za-z0-9_-]{40,}$/.test(token);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validates a DNS record type
|
* Validates a DNS record type
|
||||||
* @param type DNS record type to validate
|
* @param type DNS record type to validate
|
||||||
@ -49,14 +49,28 @@ export class CloudflareUtils {
|
|||||||
*/
|
*/
|
||||||
public static isValidRecordType(type: string): boolean {
|
public static isValidRecordType(type: string): boolean {
|
||||||
const validTypes: plugins.tsclass.network.TDnsRecordType[] = [
|
const validTypes: plugins.tsclass.network.TDnsRecordType[] = [
|
||||||
'A', 'AAAA', 'CNAME', 'TXT', 'SRV', 'LOC', 'MX',
|
'A',
|
||||||
'NS', 'CAA', 'CERT', 'DNSKEY', 'DS', 'NAPTR', 'SMIMEA',
|
'AAAA',
|
||||||
'SSHFP', 'TLSA', 'URI'
|
'CNAME',
|
||||||
|
'TXT',
|
||||||
|
'SRV',
|
||||||
|
'LOC',
|
||||||
|
'MX',
|
||||||
|
'NS',
|
||||||
|
'CAA',
|
||||||
|
'CERT',
|
||||||
|
'DNSKEY',
|
||||||
|
'DS',
|
||||||
|
'NAPTR',
|
||||||
|
'SMIMEA',
|
||||||
|
'SSHFP',
|
||||||
|
'TLSA',
|
||||||
|
'URI',
|
||||||
// Note: SPF has been removed as it's not in TDnsRecordType
|
// Note: SPF has been removed as it's not in TDnsRecordType
|
||||||
];
|
];
|
||||||
return validTypes.includes(type as any);
|
return validTypes.includes(type as any);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Formats a URL for cache purging (ensures it starts with http/https)
|
* Formats a URL for cache purging (ensures it starts with http/https)
|
||||||
* @param url URL to format
|
* @param url URL to format
|
||||||
@ -68,7 +82,7 @@ export class CloudflareUtils {
|
|||||||
}
|
}
|
||||||
return url;
|
return url;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Converts a TTL value in seconds to a human-readable string
|
* Converts a TTL value in seconds to a human-readable string
|
||||||
* @param ttl TTL in seconds
|
* @param ttl TTL in seconds
|
||||||
@ -101,20 +115,23 @@ export class CloudflareUtils {
|
|||||||
return `${ttl} seconds`;
|
return `${ttl} seconds`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Safely handles API pagination for Cloudflare requests
|
* Safely handles API pagination for Cloudflare requests
|
||||||
* @param makeRequest Function that makes the API request with page parameters
|
* @param makeRequest Function that makes the API request with page parameters
|
||||||
* @returns Combined results from all pages
|
* @returns Combined results from all pages
|
||||||
*/
|
*/
|
||||||
public static async paginateResults<T>(
|
public static async paginateResults<T>(
|
||||||
makeRequest: (page: number, perPage: number) => Promise<{ result: T[], result_info: { total_pages: number } }>
|
makeRequest: (
|
||||||
|
page: number,
|
||||||
|
perPage: number,
|
||||||
|
) => Promise<{ result: T[]; result_info: { total_pages: number } }>,
|
||||||
): Promise<T[]> {
|
): Promise<T[]> {
|
||||||
const perPage = 50; // Cloudflare's maximum
|
const perPage = 50; // Cloudflare's maximum
|
||||||
let page = 1;
|
let page = 1;
|
||||||
let totalPages = 1;
|
let totalPages = 1;
|
||||||
const allResults: T[] = [];
|
const allResults: T[] = [];
|
||||||
|
|
||||||
do {
|
do {
|
||||||
try {
|
try {
|
||||||
const response = await makeRequest(page, perPage);
|
const response = await makeRequest(page, perPage);
|
||||||
@ -126,7 +143,7 @@ export class CloudflareUtils {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} while (page <= totalPages);
|
} while (page <= totalPages);
|
||||||
|
|
||||||
return allResults;
|
return allResults;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,9 @@
|
|||||||
export { CloudflareAccount } from './cloudflare.classes.account.js';
|
export { CloudflareAccount } from './cloudflare.classes.account.js';
|
||||||
export { CloudflareWorker, type IWorkerRoute, type IWorkerRouteDefinition } from './cloudflare.classes.worker.js';
|
export {
|
||||||
|
CloudflareWorker,
|
||||||
|
type IWorkerRoute,
|
||||||
|
type IWorkerRouteDefinition,
|
||||||
|
} 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 { CloudflareZone } from './cloudflare.classes.zone.js';
|
export { CloudflareZone } from './cloudflare.classes.zone.js';
|
||||||
@ -8,4 +12,4 @@ export { CloudflareUtils } from './cloudflare.utils.js';
|
|||||||
export { commitinfo } from './00_commitinfo_data.js';
|
export { commitinfo } from './00_commitinfo_data.js';
|
||||||
|
|
||||||
// Re-export interfaces
|
// Re-export interfaces
|
||||||
export * from './interfaces/index.js';
|
export * from './interfaces/index.js';
|
||||||
|
@ -17,4 +17,4 @@ export interface ICloudflareApiAccountObject {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
created_on: string; // Assuming ISO date string
|
created_on: string; // Assuming ISO date string
|
||||||
}
|
}
|
||||||
|
@ -42,4 +42,4 @@ export interface ICflareZone {
|
|||||||
legacy_discount: boolean;
|
legacy_discount: boolean;
|
||||||
externally_managed: boolean;
|
externally_managed: boolean;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -6,9 +6,11 @@
|
|||||||
"module": "NodeNext",
|
"module": "NodeNext",
|
||||||
"moduleResolution": "NodeNext",
|
"moduleResolution": "NodeNext",
|
||||||
"esModuleInterop": true,
|
"esModuleInterop": true,
|
||||||
"verbatimModuleSyntax": true
|
"verbatimModuleSyntax": true,
|
||||||
|
"baseUrl": ".",
|
||||||
|
"paths": {}
|
||||||
},
|
},
|
||||||
"exclude": [
|
"exclude": [
|
||||||
"dist_*/**/*.d.ts"
|
"dist_*/**/*.d.ts"
|
||||||
]
|
]
|
||||||
}
|
}
|
Loading…
x
Reference in New Issue
Block a user