import * as plugins from './cloudflare.plugins.js'; import * as interfaces from './interfaces/index.js'; import { WorkerManager } from './cloudflare.classes.workermanager.js'; import { logger } from './cloudflare.logger.js'; export interface IWorkerRoute extends interfaces.ICflareWorkerRoute { zoneName: string; } export interface IWorkerRouteDefinition { zoneName: string; pattern: string; } export class CloudflareWorker { // STATIC public static async fromApiObject( workerManager: WorkerManager, apiObject ): Promise { const newWorker = new CloudflareWorker(workerManager); Object.assign(newWorker, apiObject); await newWorker.getRoutes(); return newWorker; } // INSTANCE private workerManager: WorkerManager; public script: string; public id: string; public etag: string; // tslint:disable-next-line: variable-name public created_on: string; // tslint:disable-next-line: variable-name public modified_on: string; public routes: IWorkerRoute[] = []; constructor(workerManagerArg: WorkerManager) { this.workerManager = workerManagerArg; } /** * gets all routes for a worker */ public async getRoutes() { const zones = await this.workerManager.cfAccount.convenience.listZones(); for (const zone of zones) { try { // The official client doesn't have a direct method to list worker routes // We'll use the custom request method for this specific case const response: { result: interfaces.ICflareWorkerRoute[]; } = await this.workerManager.cfAccount.request('GET', `/zones/${zone.id}/workers/routes`); for (const route of response.result) { logger.log('debug', `Processing route: ${route.pattern}`); logger.log('debug', `Comparing script: ${route.script} with worker ID: ${this.id}`); if (route.script === this.id) { this.routes.push({ ...route, zoneName: zone.name }); } } } catch (error) { logger.log('error', `Failed to get worker routes for zone ${zone.name}: ${error.message}`); } } } /** * Sets routes for this worker * @param routeArray Array of route definitions */ public async setRoutes(routeArray: IWorkerRouteDefinition[]) { for (const newRoute of routeArray) { // Determine whether a route is new, needs an update, or is already up to date let routeStatus: 'new' | 'needsUpdate' | 'alreadyUpToDate' = 'new'; let routeIdForUpdate: string; for (const existingRoute of this.routes) { if (existingRoute.pattern === newRoute.pattern) { routeStatus = 'needsUpdate'; routeIdForUpdate = existingRoute.id; if (existingRoute.script === this.id) { routeStatus = 'alreadyUpToDate'; logger.log('info', `Route already exists, no update needed`); } } } try { const zoneId = await this.workerManager.cfAccount.convenience.getZoneId(newRoute.zoneName); // Handle route creation or update if (routeStatus === 'new') { // The official client doesn't have a direct method to create worker routes // We'll use the custom request method for this specific case await this.workerManager.cfAccount.request('POST', `/zones/${zoneId}/workers/routes`, { pattern: newRoute.pattern, script: this.id, }); logger.log('info', `Created new route ${newRoute.pattern} for worker ${this.id}`); } else if (routeStatus === 'needsUpdate') { // The official client doesn't have a direct method to update worker routes // We'll use the custom request method for this specific case await this.workerManager.cfAccount.request('PUT', `/zones/${zoneId}/workers/routes/${routeIdForUpdate}`, { pattern: newRoute.pattern, script: this.id, }); logger.log('info', `Updated route ${newRoute.pattern} for worker ${this.id}`); } } catch (error) { logger.log('error', `Failed to set route ${newRoute.pattern}: ${error.message}`); } } } /** * Upload or update worker script content * @param scriptContent The worker script content * @returns Updated worker object */ public async updateScript(scriptContent: string): Promise { if (!this.workerManager.cfAccount.preselectedAccountId) { throw new Error('No account selected. Please select it first on the account.'); } try { logger.log('info', `Updating script for worker ${this.id}`); // The official client requires the metadata property const updatedWorker = await this.workerManager.cfAccount.apiAccount.workers.scripts.content.update(this.id, { account_id: this.workerManager.cfAccount.preselectedAccountId, "CF-WORKER-BODY-PART": scriptContent, metadata: {} // Required empty object }); // Update this instance with new data Object.assign(this, updatedWorker); return this; } catch (error) { logger.log('error', `Failed to update worker script: ${error.message}`); throw error; } } /** * Delete this worker script * @returns True if deletion was successful */ public async delete(): Promise { if (!this.workerManager.cfAccount.preselectedAccountId) { throw new Error('No account selected. Please select it first on the account.'); } try { logger.log('info', `Deleting worker ${this.id}`); await this.workerManager.cfAccount.apiAccount.workers.scripts.delete(this.id, { account_id: this.workerManager.cfAccount.preselectedAccountId }); return true; } catch (error) { logger.log('error', `Failed to delete worker: ${error.message}`); return false; } } }