import { ActionListModel, ClientListModel, DemoMediaResponse, ToneAPIConfiguration, CustomToneResponse, ToneTimerResponse, ToneResponse } from '../types/ToneTypes'; import { Logger } from '../utils/Logger'; export class ToneAPIService { private config: ToneAPIConfiguration; private clientId?: string; constructor(configuration?: Partial) { const cfg: any = { apiKey: configuration?.apiKey || 'WaOIgO4Ccea1wk55mDZVVRBdmyh2HweXGHdOlrx2OYseIdwFcDLHmRcZiPAWegjvuytuytuy', baseURL: configuration?.baseURL || 'https://api.toneadmin.com', timeoutMs: configuration?.timeoutMs || 10000 }; if (configuration && typeof configuration.corsProxyBaseURL !== 'undefined') { cfg.corsProxyBaseURL = configuration.corsProxyBaseURL; } if (configuration && typeof configuration.useProxyOnWeb !== 'undefined') { cfg.useProxyOnWeb = configuration.useProxyOnWeb; } this.config = cfg as ToneAPIConfiguration; if (configuration?.clientId) { this.clientId = configuration.clientId; } Logger.debug('ToneAPIService initialized', { baseURL: this.config.baseURL, apiKey: this.getMaskedAPIKey(), clientId: this.clientId }); } public updateConfiguration(configuration: Partial): void { this.config = { ...this.config, ...configuration }; if (configuration.clientId) { this.clientId = configuration.clientId; } } public getBaseURL(): string { return this.config.baseURL; } public getMaskedAPIKey(): string { const key = this.config.apiKey || ''; if (key.length <= 6) return '***'; return `${key.substring(0, 3)}***${key.substring(key.length - 3)}`; } private async request(path: string, init?: RequestInit): Promise { const controller = new AbortController(); const timeout = setTimeout(() => controller.abort(), this.config.timeoutMs); try { const base = (typeof window !== 'undefined' && (this.config as any).useProxyOnWeb && (this.config as any).corsProxyBaseURL) ? (this.config as any).corsProxyBaseURL as string : this.config.baseURL; const url = `${base.replace(/\/$/, '')}/${path.replace(/^\//, '')}`; Logger.info('HTTP request', { url, method: init?.method || 'GET' }); const headers: Record = { 'Content-Type': 'application/json; charset=UTF-8', 'x-api-key': this.config.apiKey }; // Add client ID header if available if (this.clientId) { headers['x-client-id'] = this.clientId; } const res = await fetch(url, { ...init, headers: { ...headers, ...(init?.headers as any) }, signal: controller.signal } as RequestInit); if (!res.ok) { const text = await res.text().catch(() => ''); Logger.error('HTTP error', { status: res.status, body: text }); throw new Error(`HTTP ${res.status}: ${text}`); } const data = await res.json() as T; Logger.debug('HTTP response OK', { url, ok: res.ok }); return data; } finally { clearTimeout(timeout); } } public async fetchClientList(): Promise { return this.request('companies/get/offline'); } public async fetchToneSequencesForClient(clientId: string): Promise { return this.request(`companies/tone-sequence/${clientId}`); } public async fetchDemoMediaForClient(clientId: string): Promise { return this.request(`demo-media/mobile/${encodeURIComponent(clientId)}`); } public async downloadImage(urlString: string): Promise { const res = await fetch(urlString); if (!res.ok) throw new Error(`HTTP ${res.status}`); return await res.arrayBuffer(); } public async fetchCustomToneReferences(clientId: string): Promise { return this.request(`client/custom-tones/${encodeURIComponent(clientId)}`); } public async getGovernorTime(clientId: string): Promise { const raw = await this.request(`companies/governor/${encodeURIComponent(clientId)}`); return { status: raw?.status ?? 0, duplicateDetectionTiming: raw?.data ?? 0 }; } public async sendToneDataRequest(payload: Record): Promise> { return this.request>('tone-response', { method: 'POST', body: JSON.stringify(payload) }); } public async sendHourlyRequest(payload: Record): Promise> { return this.request>('tone-response', { method: 'POST', body: JSON.stringify(payload) }); } public async fetchOfflineData(clientId: string): Promise { return this.request(`companies/tone-sequence/${encodeURIComponent(clientId)}`); } }