sinka/api/src/platform/integrations.service.ts
armando cf16bf1fb6 Show integration health in the admin and keep clients on their own screen.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-16 03:43:18 -03:00

81 lines
2.3 KiB
TypeScript

import { Injectable } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { buildIntegrations, type Integration } from './integrations';
@Injectable()
export class IntegrationsService {
constructor(private readonly config: ConfigService) {}
async list(): Promise<{ items: Integration[] }> {
const [viaCep, nominatim, firebase] = await Promise.all([
this.probeViaCep(),
this.probeNominatim(),
this.probeFirebase(),
]);
return {
items: buildIntegrations({
viaCep,
nominatim,
google: this.googleConfigured(),
firebase,
}),
};
}
private googleConfigured() {
return Boolean(this.config.get<string>('GOOGLE_CLIENT_ID') && this.config.get<string>('GOOGLE_CLIENT_SECRET'));
}
private async probeViaCep() {
return this.ok(async () => {
const response = await fetch('https://viacep.com.br/ws/01001000/json/', {
signal: AbortSignal.timeout(4000),
});
if (!response.ok) {
return false;
}
const data = (await response.json()) as { erro?: boolean; localidade?: string };
return !data.erro && Boolean(data.localidade);
});
}
private async probeNominatim() {
return this.ok(async () => {
const query = new URLSearchParams({
format: 'json',
limit: '1',
countrycodes: 'br',
q: 'Sao Paulo, SP, Brasil',
});
const response = await fetch(`https://nominatim.openstreetmap.org/search?${query.toString()}`, {
headers: { 'User-Agent': 'SinkaLogistica/1.0 (desafio; tina.r@example.net)' },
signal: AbortSignal.timeout(4000),
});
if (!response.ok) {
return false;
}
const rows = (await response.json()) as unknown[];
return rows.length > 0;
});
}
private async probeFirebase() {
const url = (this.config.get<string>('FIREBASE_DATABASE_URL')?.replace(/\/$/, '') ||
'https://sinka-8ec10-default-rtdb.firebaseio.com');
return this.ok(async () => {
const response = await fetch(`${url}/.json?shallow=true`, {
signal: AbortSignal.timeout(4000),
});
return response.ok;
});
}
private async ok(probe: () => Promise<boolean>) {
try {
return await probe();
} catch {
return false;
}
}
}