UNPKG

@restnfeel/agentc-starter-kit

Version:

한국어 기업용 CMS 모듈 - Task Master AI와 함께 빠르게 웹사이트를 구현할 수 있는 재사용 가능한 컴포넌트 시스템

299 lines (266 loc) 7.36 kB
import { CDNProvider, CDNConfig, loadCDNConfig, checkCDNHealth, } from "./cdn-config"; import { CloudflareCDNProvider } from "./providers/cloudflare-cdn"; // CDN 서비스 매니저 export class CDNService { private provider: CDNProvider | null = null; private config: CDNConfig; private fallbackEnabled: boolean = true; constructor(config?: CDNConfig) { this.config = config || loadCDNConfig(); this.initializeProvider(); } private initializeProvider() { if (!this.config.enabled) { this.provider = null; return; } switch (this.config.provider) { case "cloudflare": this.provider = new CloudflareCDNProvider(this.config); break; case "aws-cloudfront": // TODO: AWS CloudFront 프로바이더 구현 console.warn("AWS CloudFront provider not implemented yet"); this.provider = null; break; case "google-cloud-cdn": // TODO: Google Cloud CDN 프로바이더 구현 console.warn("Google Cloud CDN provider not implemented yet"); this.provider = null; break; case "azure-cdn": // TODO: Azure CDN 프로바이더 구현 console.warn("Azure CDN provider not implemented yet"); this.provider = null; break; default: console.error(`Unknown CDN provider: ${this.config.provider}`); this.provider = null; } } // CDN 업로드 (폴백 지원) async upload( file: Buffer, key: string, mimeType: string ): Promise<{ url: string; cdnUrl?: string; fallback?: boolean; }> { if (!this.provider || !this.config.enabled) { // CDN이 비활성화되었거나 프로바이더가 없으면 로컬 URL 반환 return { url: `/api/media/files/${key}`, fallback: true, }; } try { const cdnUrl = await this.provider.upload(file, key, mimeType); return { url: cdnUrl, cdnUrl, fallback: false, }; } catch (error) { console.error("CDN upload failed, using fallback:", error); if (this.fallbackEnabled) { // 폴백으로 로컬 스토리지 사용 return { url: `/api/media/files/${key}`, fallback: true, }; } throw error; } } // CDN에서 파일 삭제 async delete(key: string): Promise<boolean> { if (!this.provider || !this.config.enabled) { return false; } try { await this.provider.delete(key); return true; } catch (error) { console.error("CDN delete failed:", error); return false; } } // CDN 캐시 무효화 async invalidate(keys: string[]): Promise<boolean> { if (!this.provider || !this.config.enabled || keys.length === 0) { return false; } try { await this.provider.invalidate(keys); return true; } catch (error) { console.error("CDN invalidation failed:", error); return false; } } // URL 생성 (CDN 또는 폴백) getUrl( key: string, options?: { forceLocal?: boolean; width?: number; height?: number; format?: string; quality?: number; } ): string { if (options?.forceLocal || !this.provider || !this.config.enabled) { return `/api/media/files/${key}`; } let url = this.provider.getUrl(key); // 이미지 최적화 옵션이 있으면 적용 if (options && this.provider instanceof CloudflareCDNProvider) { const params = new URLSearchParams(); if (options.width) params.append("w", options.width.toString()); if (options.height) params.append("h", options.height.toString()); if (options.format) params.append("f", options.format); if (options.quality) params.append("q", options.quality.toString()); if (params.toString()) { url += `?${params.toString()}`; } } return url; } // CDN 상태 확인 async checkHealth(): Promise<{ healthy: boolean; latency?: number; error?: string; provider: string; }> { const health = await checkCDNHealth(this.config); return { ...health, provider: this.config.provider, }; } // 설정 업데이트 updateConfig(newConfig: Partial<CDNConfig>) { this.config = { ...this.config, ...newConfig }; this.initializeProvider(); } // 폴백 활성화/비활성화 setFallbackEnabled(enabled: boolean) { this.fallbackEnabled = enabled; } // 현재 설정 반환 getConfig(): CDNConfig { return { ...this.config }; } // 프로바이더 정보 반환 getProviderInfo(): { name: string; enabled: boolean; endpoint: string; customDomain?: string; } { return { name: this.config.provider, enabled: this.config.enabled, endpoint: this.config.endpoint, customDomain: this.config.customDomain, }; } // 캐시 헤더 생성 getCacheHeaders(mimeType: string): Record<string, string> { if (!this.provider) { return {}; } return this.provider.getCacheHeaders(mimeType); } // 배치 작업 (여러 파일 한번에 처리) async batchUpload( files: Array<{ buffer: Buffer; key: string; mimeType: string; }> ): Promise< Array<{ key: string; url: string; cdnUrl?: string; fallback?: boolean; error?: string; }> > { const results = await Promise.allSettled( files.map(async ({ buffer, key, mimeType }) => { try { const result = await this.upload(buffer, key, mimeType); return { key, ...result }; } catch (error) { return { key, url: `/api/media/files/${key}`, fallback: true, error: error instanceof Error ? error.message : "Unknown error", }; } }) ); return results.map((result, index) => { if (result.status === "fulfilled") { return result.value; } else { return { key: files[index].key, url: `/api/media/files/${files[index].key}`, fallback: true, error: result.reason instanceof Error ? result.reason.message : "Upload failed", }; } }); } // 배치 삭제 async batchDelete(keys: string[]): Promise<{ success: string[]; failed: string[]; }> { const results = await Promise.allSettled( keys.map(async (key) => { const success = await this.delete(key); return { key, success }; }) ); const success: string[] = []; const failed: string[] = []; results.forEach((result, index) => { const key = keys[index]; if (result.status === "fulfilled" && result.value.success) { success.push(key); } else { failed.push(key); } }); return { success, failed }; } } // 전역 CDN 서비스 인스턴스 let globalCDNService: CDNService | null = null; export function getCDNService(): CDNService { if (!globalCDNService) { globalCDNService = new CDNService(); } return globalCDNService; } // CDN 서비스 재초기화 export function reinitializeCDNService(config?: CDNConfig): CDNService { globalCDNService = new CDNService(config); return globalCDNService; }