UNPKG

dnsweeper

Version:

Advanced CLI tool for DNS record risk analysis and cleanup. Features CSV import for Cloudflare/Route53, automated risk assessment, and parallel DNS validation.

191 lines (169 loc) 5.46 kB
/** * Cloudflare API クライアント */ import type { DNSRecordType, ICSVRecord } from '../../types/index.js'; export interface CloudflareConfig { apiToken?: string; email?: string; apiKey?: string; } export interface CloudflareZone { id: string; name: string; status: string; paused: boolean; type: string; development_mode: number; name_servers: string[]; original_name_servers: string[]; original_registrar: string; original_dnshost: string; created_on: string; modified_on: string; activated_on: string; } export interface CloudflareDNSRecord { id?: string; zone_id?: string; zone_name?: string; name: string; type: DNSRecordType; content: string; proxiable?: boolean; proxied?: boolean; ttl?: number; priority?: number; locked?: boolean; created_on?: string; modified_on?: string; meta?: { auto_added?: boolean; source?: string; }; } export interface CloudflareResponse<T> { success: boolean; errors: Array<{ code: number; message: string }>; messages: string[]; result: T; result_info?: { page: number; per_page: number; count: number; total_count: number; total_pages: number; }; } export class CloudflareClient { private readonly baseUrl = 'https://api.cloudflare.com/client/v4'; private readonly headers: Record<string, string>; constructor(config: CloudflareConfig) { if (config.apiToken) { this.headers = { 'Authorization': `Bearer ${config.apiToken}`, 'Content-Type': 'application/json' }; } else if (config.email && config.apiKey) { this.headers = { 'X-Auth-Email': config.email, 'X-Auth-Key': config.apiKey, 'Content-Type': 'application/json' }; } else { throw new Error('Cloudflare認証情報が不正です。APIトークンまたはEmail+APIキーを指定してください'); } } private async request<T>( method: string, path: string, body?: unknown ): Promise<CloudflareResponse<T>> { const response = await fetch(`${this.baseUrl}${path}`, { method, headers: this.headers, ...(body && { body: JSON.stringify(body) }) }); const data = await response.json() as CloudflareResponse<T>; if (!response.ok) { throw new Error(`Cloudflare API error: ${response.status} ${response.statusText}`); } return data; } async listZones(): Promise<CloudflareZone[]> { const response = await this.request<CloudflareZone[]>('GET', '/zones'); return response.result; } async getZone(zoneId: string): Promise<CloudflareZone> { const response = await this.request<CloudflareZone>('GET', `/zones/${zoneId}`); return response.result; } async listDNSRecords(zoneId: string): Promise<CloudflareDNSRecord[]> { const response = await this.request<CloudflareDNSRecord[]>('GET', `/zones/${zoneId}/dns_records`); return response.result; } async getDNSRecord(zoneId: string, recordId: string): Promise<CloudflareDNSRecord> { const response = await this.request<CloudflareDNSRecord>('GET', `/zones/${zoneId}/dns_records/${recordId}`); return response.result; } async createDNSRecord(zoneId: string, record: CloudflareDNSRecord): Promise<CloudflareDNSRecord> { const response = await this.request<CloudflareDNSRecord>('POST', `/zones/${zoneId}/dns_records`, record); return response.result; } async updateDNSRecord(zoneId: string, recordId: string, record: CloudflareDNSRecord): Promise<CloudflareDNSRecord> { const response = await this.request<CloudflareDNSRecord>('PUT', `/zones/${zoneId}/dns_records/${recordId}`, record); return response.result; } async deleteDNSRecord(zoneId: string, recordId: string): Promise<{ id: string }> { const response = await this.request<{ id: string }>('DELETE', `/zones/${zoneId}/dns_records/${recordId}`); return response.result; } /** * CSVレコードをCloudflare DNS形式に変換 */ convertCSVToCloudflareRecords(csvRecords: ICSVRecord[]): CloudflareDNSRecord[] { return csvRecords.map(csvRecord => ({ name: csvRecord.domain, type: csvRecord.type, content: csvRecord.value, ttl: csvRecord.ttl || 1, // CloudflareのTTL 1 = automatic priority: csvRecord.priority, proxied: false // デフォルトはプロキシ無効 })); } /** * Cloudflare DNSレコードをCSV形式に変換 */ convertCloudflareToCSVRecords(cfRecords: CloudflareDNSRecord[]): ICSVRecord[] { return cfRecords.map(cfRecord => ({ domain: cfRecord.name, type: cfRecord.type, value: cfRecord.content, ttl: cfRecord.ttl === 1 ? 0 : cfRecord.ttl || 300, priority: cfRecord.priority, weight: undefined, port: undefined })); } /** * ゾーンファイルのエクスポート */ async exportZone(zoneId: string): Promise<string> { const response = await this.request<string>('GET', `/zones/${zoneId}/dns_records/export`); return response.result; } /** * ゾーンファイルのインポート */ async importZone(zoneId: string, zoneFile: string): Promise<{ recs_added: number; total_records_parsed: number; }> { const response = await this.request<{ recs_added: number; total_records_parsed: number; }>('POST', `/zones/${zoneId}/dns_records/import`, { file: zoneFile }); return response.result; } }