import { config } from '../config.js'; import { proxyRequest } from '../services/websocket.js'; // All Google API calls are routed through the on-prem remote agent so the // request originates from the whitelisted IP subnet. The API key is // restricted by IP; calls direct from the cloud bot IP get 403 with // API_KEY_IP_ADDRESS_BLOCKED. The remote agent runs inside the corporate // network, so its egress IP matches the key's allow-list. // // `insecure: true` is set even though Google's public certs are trusted, // because the corporate network the agent lives on intercepts outbound // HTTPS with an SSL-inspecting proxy that presents an internal-CA chain. // Without the flag Node throws SELF_SIGNED_CERT_IN_CHAIN on any outbound // TLS. Trust is already delegated to that proxy by being on this network, // so the scoped per-request bypass is consistent with SIW. /** * Google Address Validation API. Returns the parsed response body. */ export async function validateAddress(street, city, state, postalCode, country) { const body = { address: { regionCode: country, locality: city, addressLines: [street], administrativeArea: state, postalCode, }, }; const url = `https://addressvalidation.googleapis.com/v1:validateAddress?key=${encodeURIComponent(config.google.apiKey)}`; const response = await proxyRequest({ method: 'POST', url, headers: { 'Content-Type': 'application/json' }, body, insecure: true, }); return response?.data; } /** * Google Time Zone API for a given lat/lon at "now". Uses GET as documented * by Google — parameters are entirely in the query string. */ export async function getTimeZone(latitude, longitude) { const timestamp = Math.floor(Date.now() / 1000); const url = `https://maps.googleapis.com/maps/api/timezone/json?location=${latitude}%2C${longitude}` + `×tamp=${timestamp}&key=${encodeURIComponent(config.google.apiKey)}`; const response = await proxyRequest({ method: 'GET', url, insecure: true, }); return response?.data; } /** * Reduce a Google addressComponents array to a flat "num street, city, state zip" string. */ export function formatE911Address(addressData) { const parts = {}; for (const component of addressData.result.address.addressComponents ?? []) { parts[component.componentType] = component.componentName?.text ?? ''; } return `${parts.street_number ?? ''} ${parts.route ?? ''}, ${parts.locality ?? ''}, ${ parts.administrative_area_level_1 ?? '' } ${parts.postal_code ?? ''}`.trim(); } export function formatSuite(addressData) { for (const component of addressData.result.address.addressComponents ?? []) { if (component.componentType === 'subpremise') { return component.componentName?.text ?? ''; } } return ''; }