ceo-api/src/intelligence/intelligence.service.ts
valentinbvro 0f8390f31b feat: Faza A2 -- intelligence proxy, saved segments, research briefs, briefing
- IntelligenceModule: proxy tenant-scoped catre intelligence-api
  (/companies/search, /companies/{id}); ceo-web nu vorbeste niciodata
  direct cu intelligence-api (blueprint 10.3/16).
- SavedSegments: salveaza o cautare Apollo si o ruleaza din nou oricand.
- ResearchBriefs: dovezi curatate manual per companie, cu sursa Apollo
  implicita plus surse suplimentare -- explicit fara rezumat generat de
  AI (AI Gateway inca neconstruit, blueprint 14).
- BriefingService: /v1/briefing/today, agregare deterministica de
  taskuri restante/viitoare + activitate saptamanala; campul
  aiExplanation ramane null si vizibil in raspuns, nu simulat.
2026-07-28 22:01:17 +02:00

55 lines
2 KiB
TypeScript

import { BadGatewayException, Injectable, NotFoundException } from '@nestjs/common';
import type { CompanySearchResponse, CompanySearchResult } from './intelligence.types';
const REQUEST_TIMEOUT_MS = 8000;
/**
* Client subtire catre intelligence-api. Blueprint 10.3/16: "company search prin
* Intelligence API, nu SQL direct" -- ceo-web nu vorbeste niciodata direct cu
* intelligence-api, ca sa putem adauga aici audit, quotas si viitorul AI Gateway
* fara sa schimbam contractul catre frontend.
*/
@Injectable()
export class IntelligenceService {
private readonly baseUrl = process.env.INTELLIGENCE_API_URL ?? 'http://localhost:8000';
async searchCompanies(query: string, limit: number): Promise<CompanySearchResponse> {
const response = await this.request('/companies/search', {
method: 'POST',
body: JSON.stringify({ query, limit }),
});
return (await response.json()) as CompanySearchResponse;
}
async getCompany(organizationId: string): Promise<CompanySearchResult> {
const response = await this.request(`/companies/${encodeURIComponent(organizationId)}`);
const data = (await response.json()) as CompanySearchResult | { error: string };
if ('error' in data) {
throw new NotFoundException('Company not found in intelligence data');
}
return data;
}
private async request(path: string, init: RequestInit = {}): Promise<Response> {
const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS);
try {
const response = await fetch(`${this.baseUrl}${path}`, {
...init,
headers: { 'Content-Type': 'application/json', ...init.headers },
signal: controller.signal,
});
if (!response.ok) {
throw new BadGatewayException('intelligence-api request failed');
}
return response;
} catch (error) {
if (error instanceof BadGatewayException) {
throw error;
}
throw new BadGatewayException('intelligence-api unreachable');
} finally {
clearTimeout(timeout);
}
}
}