feat: agregar endpoint /autocomplete en admin Next.js
Proxy a Google Places Autocomplete API:
- Lee la API key de /settings/maps-key del backend
- Acepta params: input, location
- Devuelve { results: [{ formatted_address, place_id }] }
- Headers CORS para permitir acceso desde app.prosapp.co
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 4.6
parent
abc19505ae
commit
d3ca99fa1b
@@ -0,0 +1,83 @@
|
|||||||
|
import { NextRequest, NextResponse } from 'next/server';
|
||||||
|
|
||||||
|
const API_BASE = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:3000/api/v1';
|
||||||
|
|
||||||
|
// Cache the Maps API key in memory (refreshes every 5 min)
|
||||||
|
let cachedKey: string | null = null;
|
||||||
|
let cacheTime = 0;
|
||||||
|
|
||||||
|
async function getMapsKey(): Promise<string | null> {
|
||||||
|
const now = Date.now();
|
||||||
|
if (cachedKey && now - cacheTime < 5 * 60 * 1000) return cachedKey;
|
||||||
|
try {
|
||||||
|
const res = await fetch(`${API_BASE}/settings/maps-key`, { cache: 'no-store' });
|
||||||
|
if (!res.ok) return null;
|
||||||
|
const data = await res.json();
|
||||||
|
cachedKey = data.api_key || null;
|
||||||
|
cacheTime = now;
|
||||||
|
return cachedKey;
|
||||||
|
} catch {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function GET(req: NextRequest) {
|
||||||
|
const { searchParams } = req.nextUrl;
|
||||||
|
const input = searchParams.get('input')?.trim();
|
||||||
|
const location = searchParams.get('location') ?? '';
|
||||||
|
|
||||||
|
if (!input || input.length < 2) {
|
||||||
|
return NextResponse.json({ results: [] });
|
||||||
|
}
|
||||||
|
|
||||||
|
const apiKey = await getMapsKey();
|
||||||
|
if (!apiKey) {
|
||||||
|
return NextResponse.json(
|
||||||
|
{ error: 'Google Maps API key not configured' },
|
||||||
|
{ status: 503 },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use Places Autocomplete for typed suggestions
|
||||||
|
const placesUrl = new URL('https://maps.googleapis.com/maps/api/place/autocomplete/json');
|
||||||
|
placesUrl.searchParams.set('input', input);
|
||||||
|
placesUrl.searchParams.set('key', apiKey);
|
||||||
|
placesUrl.searchParams.set('language', 'es');
|
||||||
|
placesUrl.searchParams.set('types', 'address');
|
||||||
|
if (location) placesUrl.searchParams.set('location', location);
|
||||||
|
if (location) placesUrl.searchParams.set('radius', '50000');
|
||||||
|
|
||||||
|
try {
|
||||||
|
const res = await fetch(placesUrl.toString(), { cache: 'no-store' });
|
||||||
|
const data = await res.json();
|
||||||
|
|
||||||
|
if (data.status !== 'OK' && data.status !== 'ZERO_RESULTS') {
|
||||||
|
return NextResponse.json({ results: [] });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Normalize predictions → results with formatted_address for Flutter compatibility
|
||||||
|
const results = (data.predictions ?? []).map((p: any) => ({
|
||||||
|
formatted_address: p.description,
|
||||||
|
place_id: p.place_id,
|
||||||
|
}));
|
||||||
|
|
||||||
|
return NextResponse.json({ results }, {
|
||||||
|
headers: {
|
||||||
|
'Access-Control-Allow-Origin': '*',
|
||||||
|
'Access-Control-Allow-Methods': 'GET',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
} catch {
|
||||||
|
return NextResponse.json({ results: [] });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function OPTIONS() {
|
||||||
|
return new NextResponse(null, {
|
||||||
|
headers: {
|
||||||
|
'Access-Control-Allow-Origin': '*',
|
||||||
|
'Access-Control-Allow-Methods': 'GET, OPTIONS',
|
||||||
|
'Access-Control-Allow-Headers': 'Content-Type',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user