import { Injectable, Logger, HttpException, HttpStatus } from '@nestjs/common'; import { HttpService } from '@nestjs/axios'; import { ConfigService } from '@nestjs/config'; import { firstValueFrom, catchError } from 'rxjs'; import { AxiosError } from 'axios'; @Injectable() export class CloudflareService { private readonly logger = new Logger(CloudflareService.name); private readonly apiToken: string; private readonly zoneId: string; private readonly baseUrl = 'https://api.cloudflare.com/client/v4'; constructor( private readonly httpService: HttpService, private readonly configService: ConfigService, ) { this.apiToken = this.configService.get('CLOUDFLARE_API_TOKEN') || ''; this.zoneId = this.configService.get('CLOUDFLARE_ZONE_ID') || ''; if (!this.apiToken) { this.logger.warn('CLOUDFLARE_API_TOKEN is not set'); } } private getHeaders() { return { Authorization: `Bearer ${this.apiToken}`, 'Content-Type': 'application/json', }; } async listZones(): Promise { const url = `${this.baseUrl}/zones`; const response = await firstValueFrom( this.httpService.get(url, { headers: this.getHeaders() }).pipe( catchError((error: AxiosError) => { this.logger.error(error.response?.data || error.message); throw new HttpException( 'Failed to fetch Cloudflare zones', HttpStatus.BAD_GATEWAY, ); }), ), ); return response.data; } async purgeCache(params: { purge_everything?: boolean; files?: string[]; tags?: string[]; hosts?: string[]; }): Promise { if (!this.zoneId) { throw new HttpException( 'CLOUDFLARE_ZONE_ID is not configured', HttpStatus.BAD_REQUEST, ); } const url = `${this.baseUrl}/zones/${this.zoneId}/purge_cache`; const response = await firstValueFrom( this.httpService.post(url, params, { headers: this.getHeaders() }).pipe( catchError((error: AxiosError) => { this.logger.error(error.response?.data || error.message); throw new HttpException( 'Failed to purge Cloudflare cache', HttpStatus.BAD_GATEWAY, ); }), ), ); return response.data; } }