/** * Google Maps API Integration for Manus WebDev Templates * * Main function: makeRequest(endpoint, params) - Makes authenticated requests to Google Maps APIs * All credentials are automatically injected. Array parameters use | as separator. * * See API examples below the type definitions for usage patterns. */ import { ENV } from "./env"; // ============================================================================ // Configuration // ============================================================================ type MapsConfig = { baseUrl: string; apiKey: string; }; function getMapsConfig(): MapsConfig { const baseUrl = ENV.forgeApiUrl; const apiKey = ENV.forgeApiKey; if (!baseUrl || !apiKey) { throw new Error( "Google Maps proxy credentials missing: set BUILT_IN_FORGE_API_URL and BUILT_IN_FORGE_API_KEY" ); } return { baseUrl: baseUrl.replace(/\/+$/, ""), apiKey, }; } // ============================================================================ // Core Request Handler // ============================================================================ interface RequestOptions { method?: "GET" | "POST"; body?: Record; } /** * Make authenticated requests to Google Maps APIs * * @param endpoint - The API endpoint (e.g., "/maps/api/geocode/json") * @param params - Query parameters for the request * @param options - Additional request options * @returns The API response */ export async function makeRequest( endpoint: string, params: Record = {}, options: RequestOptions = {} ): Promise { const { baseUrl, apiKey } = getMapsConfig(); // Construct full URL: baseUrl + /v1/maps/proxy + endpoint const url = new URL(`${baseUrl}/v1/maps/proxy${endpoint}`); // Add API key as query parameter (standard Google Maps API authentication) url.searchParams.append("key", apiKey); // Add other query parameters Object.entries(params).forEach(([key, value]) => { if (value !== undefined && value !== null) { url.searchParams.append(key, String(value)); } }); const response = await fetch(url.toString(), { method: options.method || "GET", headers: { "Content-Type": "application/json", }, body: options.body ? JSON.stringify(options.body) : undefined, }); if (!response.ok) { const errorText = await response.text(); throw new Error( `Google Maps API request failed (${response.status} ${response.statusText}): ${errorText}` ); } return (await response.json()) as T; } // ============================================================================ // Type Definitions // ============================================================================ export type TravelMode = "driving" | "walking" | "bicycling" | "transit"; export type MapType = "roadmap" | "satellite" | "terrain" | "hybrid"; export type SpeedUnit = "KPH" | "MPH"; export type LatLng = { lat: number; lng: number; }; export type DirectionsResult = { routes: Array<{ legs: Array<{ distance: { text: string; value: number }; duration: { text: string; value: number }; start_address: string; end_address: string; start_location: LatLng; end_location: LatLng; steps: Array<{ distance: { text: string; value: number }; duration: { text: string; value: number }; html_instructions: string; travel_mode: string; start_location: LatLng; end_location: LatLng; }>; }>; overview_polyline: { points: string }; summary: string; warnings: string[]; waypoint_order: number[]; }>; status: string; }; export type DistanceMatrixResult = { rows: Array<{ elements: Array<{ distance: { text: string; value: number }; duration: { text: string; value: number }; status: string; }>; }>; origin_addresses: string[]; destination_addresses: string[]; status: string; }; export type GeocodingResult = { results: Array<{ address_components: Array<{ long_name: string; short_name: string; types: string[]; }>; formatted_address: string; geometry: { location: LatLng; location_type: string; viewport: { northeast: LatLng; southwest: LatLng; }; }; place_id: string; types: string[]; }>; status: string; }; export type PlacesSearchResult = { results: Array<{ place_id: string; name: string; formatted_address: string; geometry: { location: LatLng; }; rating?: number; user_ratings_total?: number; business_status?: string; types: string[]; }>; status: string; }; export type PlaceDetailsResult = { result: { place_id: string; name: string; formatted_address: string; formatted_phone_number?: string; international_phone_number?: string; website?: string; rating?: number; user_ratings_total?: number; reviews?: Array<{ author_name: string; rating: number; text: string; time: number; }>; opening_hours?: { open_now: boolean; weekday_text: string[]; }; geometry: { location: LatLng; }; }; status: string; }; export type ElevationResult = { results: Array<{ elevation: number; location: LatLng; resolution: number; }>; status: string; }; export type TimeZoneResult = { dstOffset: number; rawOffset: number; status: string; timeZoneId: string; timeZoneName: string; }; export type RoadsResult = { snappedPoints: Array<{ location: LatLng; originalIndex?: number; placeId: string; }>; }; // ============================================================================ // Google Maps API Reference // ============================================================================ /** * GEOCODING - Convert between addresses and coordinates * Endpoint: /maps/api/geocode/json * Input: { address: string } OR { latlng: string } // latlng: "37.42,-122.08" * Output: GeocodingResult // results[0].geometry.location, results[0].formatted_address */ /** * DIRECTIONS - Get navigation routes between locations * Endpoint: /maps/api/directions/json * Input: { origin: string, destination: string, mode?: TravelMode, waypoints?: string, alternatives?: boolean } * Output: DirectionsResult // routes[0].legs[0].distance, duration, steps */ /** * DISTANCE MATRIX - Calculate travel times/distances for multiple origin-destination pairs * Endpoint: /maps/api/distancematrix/json * Input: { origins: string, destinations: string, mode?: TravelMode, units?: "metric"|"imperial" } // origins: "NYC|Boston" * Output: DistanceMatrixResult // rows[0].elements[1] = first origin to second destination */ /** * PLACE SEARCH - Find businesses/POIs by text query * Endpoint: /maps/api/place/textsearch/json * Input: { query: string, location?: string, radius?: number, type?: string } // location: "40.7,-74.0" * Output: PlacesSearchResult // results[].name, rating, geometry.location, place_id */ /** * NEARBY SEARCH - Find places near a specific location * Endpoint: /maps/api/place/nearbysearch/json * Input: { location: string, radius: number, type?: string, keyword?: string } // location: "40.7,-74.0" * Output: PlacesSearchResult */ /** * PLACE DETAILS - Get comprehensive information about a specific place * Endpoint: /maps/api/place/details/json * Input: { place_id: string, fields?: string } // fields: "name,rating,opening_hours,website" * Output: PlaceDetailsResult // result.name, rating, opening_hours, etc. */ /** * ELEVATION - Get altitude data for geographic points * Endpoint: /maps/api/elevation/json * Input: { locations?: string, path?: string, samples?: number } // locations: "39.73,-104.98|36.45,-116.86" * Output: ElevationResult // results[].elevation (meters) */ /** * TIME ZONE - Get timezone information for a location * Endpoint: /maps/api/timezone/json * Input: { location: string, timestamp: number } // timestamp: Math.floor(Date.now()/1000) * Output: TimeZoneResult // timeZoneId, timeZoneName */ /** * ROADS - Snap GPS traces to roads, find nearest roads, get speed limits * - /v1/snapToRoads: Input: { path: string, interpolate?: boolean } // path: "lat,lng|lat,lng" * - /v1/nearestRoads: Input: { points: string } // points: "lat,lng|lat,lng" * - /v1/speedLimits: Input: { path: string, units?: SpeedUnit } * Output: RoadsResult */ /** * PLACE AUTOCOMPLETE - Real-time place suggestions as user types * Endpoint: /maps/api/place/autocomplete/json * Input: { input: string, location?: string, radius?: number } * Output: { predictions: Array<{ description: string, place_id: string }> } */ /** * STATIC MAPS - Generate map images as URLs (for emails, reports, tags) * Endpoint: /maps/api/staticmap * Input: URL params - center: string, zoom: number, size: string, markers?: string, maptype?: MapType * Output: Image URL (not JSON) - use directly in * Note: Construct URL manually with getMapsConfig() for auth */