import * as http from 'http'; import * as https from 'https'; import { URL } from 'url'; import { BadGatewayException, Injectable, Logger, RequestTimeoutException, } from '@nestjs/common'; export type TahesabCallOptions = { baseUrl: string; token: string; dbName: string; /** مسیر OpenAPI مثل DoNewMoshtari یا CheckHealth-SSL */ path: string; /** کلید body؛ اگر خالی باشد فقط GET بدون body */ bodyKey?: string; args?: unknown[]; method?: 'GET' | 'POST'; allowInsecureTls?: boolean; timeoutMs?: number; }; @Injectable() export class TahesabHttpClient { private readonly logger = new Logger(TahesabHttpClient.name); async call(opts: TahesabCallOptions): Promise { const base = opts.baseUrl.replace(/\/+$/, ''); const path = opts.path.replace(/^\/+/, ''); const url = new URL(`${base}/${path}`); const method = opts.method ?? (opts.bodyKey ? 'POST' : 'GET'); const timeoutMs = opts.timeoutMs ?? 30_000; const headers: Record = { Authorization: `Bearer ${opts.token}`, DBname: opts.dbName, DBName: opts.dbName, Accept: 'application/json', Host: url.host, }; let body: string | undefined; if (method === 'POST' && opts.bodyKey) { headers['Content-Type'] = 'application/json'; body = JSON.stringify({ [opts.bodyKey]: opts.args ?? [] }); headers['Content-Length'] = Buffer.byteLength(body).toString(); } const isHttps = url.protocol === 'https:'; const lib = isHttps ? https : http; const agent = isHttps && opts.allowInsecureTls ? new https.Agent({ rejectUnauthorized: false }) : undefined; return new Promise((resolve, reject) => { const req = lib.request( { protocol: url.protocol, hostname: url.hostname, port: url.port || (isHttps ? 443 : 80), path: `${url.pathname}${url.search}`, method, headers, timeout: timeoutMs, agent, }, (res) => { const chunks: Buffer[] = []; res.on('data', (c) => chunks.push(c)); res.on('end', () => { const text = Buffer.concat(chunks).toString('utf8'); let json: unknown = null; if (text) { try { json = JSON.parse(text); } catch { json = { raw: text }; } } const status = res.statusCode ?? 0; if (status < 200 || status >= 300) { this.logger.warn( `Tahesab ${method} ${path} → ${status}: ${text.slice(0, 400)}`, ); reject( new BadGatewayException( `پاسخ نامعتبر ته‌حساب (${status}) برای ${path}`, ), ); return; } resolve(json as T); }); }, ); req.on('timeout', () => { req.destroy(); reject( new RequestTimeoutException( `مهلت اتصال به ته‌حساب برای ${path} به پایان رسید.`, ), ); }); req.on('error', (err) => { this.logger.error(`Tahesab call failed: ${path}`, err); reject( new BadGatewayException( `اتصال به ته‌حساب ناموفق بود: ${err.message}`, ), ); }); if (body) req.write(body); req.end(); }); } }