224 lines
8.1 KiB
TypeScript
224 lines
8.1 KiB
TypeScript
import { createClient } from 'jsr:@supabase/supabase-js@2';
|
||
|
||
const corsHeaders = {
|
||
'Access-Control-Allow-Origin': '*',
|
||
'Access-Control-Allow-Headers': 'authorization, x-client-info, apikey, content-type',
|
||
};
|
||
|
||
interface WeatherRequest {
|
||
lat: number;
|
||
lon: number;
|
||
start_date: string; // ISO date string
|
||
end_date: string; // ISO date string
|
||
units?: 'metric' | 'imperial' | 'standard';
|
||
lang?: string;
|
||
}
|
||
|
||
interface DailyForecast {
|
||
date: string;
|
||
temp_min: number;
|
||
temp_max: number;
|
||
temp_day: number;
|
||
feels_like_day: number;
|
||
humidity: number;
|
||
wind_speed: number;
|
||
weather_main: string;
|
||
weather_description: string;
|
||
weather_icon: string;
|
||
pop: number; // Probability of precipitation
|
||
sunrise: number;
|
||
sunset: number;
|
||
}
|
||
|
||
Deno.serve(async (req) => {
|
||
// Handle CORS preflight
|
||
if (req.method === 'OPTIONS') {
|
||
return new Response(null, { headers: corsHeaders });
|
||
}
|
||
|
||
try {
|
||
const { lat, lon, start_date, end_date, units = 'metric', lang = 'tr' }: WeatherRequest = await req.json();
|
||
|
||
// Validate input
|
||
if (!lat || !lon || !start_date || !end_date) {
|
||
return new Response(
|
||
JSON.stringify({ error: 'lat, lon, start_date ve end_date gereklidir' }),
|
||
{ status: 400, headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
|
||
);
|
||
}
|
||
|
||
// Get API key from environment
|
||
const apiKey = Deno.env.get('INTEGRATIONS_API_KEY');
|
||
if (!apiKey) {
|
||
console.error('INTEGRATIONS_API_KEY bulunamadı');
|
||
return new Response(
|
||
JSON.stringify({ error: 'Hava durumu servisi yapılandırılmamış' }),
|
||
{ status: 500, headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
|
||
);
|
||
}
|
||
|
||
// Call Weather API
|
||
const weatherUrl = `https://app-9w9pd00g5j41-api-wL1zlmgJGAlY.gateway.appmedo.com/data/3.0/onecall?lat=${lat}&lon=${lon}&units=${units}&lang=${lang}`;
|
||
|
||
console.log('Hava durumu API çağrısı yapılıyor:', { lat, lon, units, lang });
|
||
|
||
const weatherResponse = await fetch(weatherUrl, {
|
||
method: 'GET',
|
||
headers: {
|
||
'X-Gateway-Authorization': `Bearer ${apiKey}`,
|
||
'Accept': 'application/json',
|
||
},
|
||
});
|
||
|
||
if (!weatherResponse.ok) {
|
||
const errorText = await weatherResponse.text();
|
||
console.error('Hava durumu API hatası:', weatherResponse.status, errorText);
|
||
return new Response(
|
||
JSON.stringify({ error: 'Hava durumu verisi alınamadı', details: errorText }),
|
||
{ status: weatherResponse.status, headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
|
||
);
|
||
}
|
||
|
||
const weatherData = await weatherResponse.json();
|
||
|
||
// Parse dates - use UTC to avoid timezone issues
|
||
const startDate = new Date(start_date + 'T00:00:00Z');
|
||
const endDate = new Date(end_date + 'T00:00:00Z');
|
||
|
||
// Get current date from the weather API response timezone or use UTC
|
||
const now = new Date();
|
||
const today = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate()));
|
||
|
||
// Calculate days difference
|
||
const daysFromToday = Math.floor((startDate.getTime() - today.getTime()) / (1000 * 60 * 60 * 24));
|
||
const tripDuration = Math.floor((endDate.getTime() - startDate.getTime()) / (1000 * 60 * 60 * 24)) + 1;
|
||
|
||
console.log('Tarih bilgileri:', {
|
||
daysFromToday,
|
||
tripDuration,
|
||
start_date,
|
||
end_date,
|
||
today: today.toISOString().split('T')[0],
|
||
availableDailyForecasts: weatherData.daily?.length || 0
|
||
});
|
||
|
||
// Check if we have any daily forecast data
|
||
if (!weatherData.daily || weatherData.daily.length === 0) {
|
||
console.error('API yanıtında günlük tahmin verisi yok');
|
||
return new Response(
|
||
JSON.stringify({
|
||
error: 'Hava durumu tahmini mevcut değil',
|
||
message: 'Seçilen konum için hava durumu verisi alınamadı'
|
||
}),
|
||
{ status: 404, headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
|
||
);
|
||
}
|
||
|
||
// Extract daily forecasts for trip dates
|
||
const forecasts: DailyForecast[] = [];
|
||
|
||
if (weatherData.daily && Array.isArray(weatherData.daily)) {
|
||
// The API returns forecasts starting from today (index 0)
|
||
// We need to map trip days to the correct forecast indices
|
||
|
||
for (let i = 0; i < tripDuration && i < weatherData.daily.length; i++) {
|
||
// Calculate which forecast index to use
|
||
const forecastIndex = Math.max(0, Math.min(daysFromToday + i, weatherData.daily.length - 1));
|
||
const dailyData = weatherData.daily[forecastIndex];
|
||
|
||
if (dailyData) {
|
||
const forecastDate = new Date(startDate);
|
||
forecastDate.setUTCDate(startDate.getUTCDate() + i);
|
||
|
||
forecasts.push({
|
||
date: forecastDate.toISOString().split('T')[0],
|
||
temp_min: dailyData.temp?.min || dailyData.temp,
|
||
temp_max: dailyData.temp?.max || dailyData.temp,
|
||
temp_day: dailyData.temp?.day || dailyData.temp,
|
||
feels_like_day: dailyData.feels_like?.day || dailyData.feels_like,
|
||
humidity: dailyData.humidity,
|
||
wind_speed: dailyData.wind_speed,
|
||
weather_main: dailyData.weather?.[0]?.main || 'Clear',
|
||
weather_description: dailyData.weather?.[0]?.description || 'açık',
|
||
weather_icon: dailyData.weather?.[0]?.icon || '01d',
|
||
pop: dailyData.pop || 0,
|
||
sunrise: dailyData.sunrise,
|
||
sunset: dailyData.sunset,
|
||
});
|
||
}
|
||
}
|
||
}
|
||
|
||
// If no daily data available, try to use hourly data for near-term forecasts
|
||
if (forecasts.length === 0 && weatherData.hourly && Array.isArray(weatherData.hourly)) {
|
||
console.log('Günlük veri yok, saatlik veriden tahmin oluşturuluyor');
|
||
|
||
// Group hourly data by day
|
||
const hourlyByDay: { [key: string]: any[] } = {};
|
||
|
||
for (let i = 0; i < Math.min(tripDuration, 2); i++) {
|
||
const forecastDate = new Date(startDate);
|
||
forecastDate.setUTCDate(startDate.getUTCDate() + i);
|
||
const dateKey = forecastDate.toISOString().split('T')[0];
|
||
hourlyByDay[dateKey] = [];
|
||
|
||
// Find hourly data for this day
|
||
const targetTimestamp = forecastDate.getTime() / 1000;
|
||
const dayStart = targetTimestamp;
|
||
const dayEnd = targetTimestamp + (24 * 60 * 60);
|
||
|
||
for (const hourData of weatherData.hourly) {
|
||
if (hourData.dt >= dayStart && hourData.dt < dayEnd) {
|
||
hourlyByDay[dateKey].push(hourData);
|
||
}
|
||
}
|
||
|
||
// Calculate daily summary from hourly data
|
||
if (hourlyByDay[dateKey].length > 0) {
|
||
const temps = hourlyByDay[dateKey].map(h => h.temp);
|
||
const midDayData = hourlyByDay[dateKey][Math.floor(hourlyByDay[dateKey].length / 2)];
|
||
|
||
forecasts.push({
|
||
date: dateKey,
|
||
temp_min: Math.min(...temps),
|
||
temp_max: Math.max(...temps),
|
||
temp_day: temps.reduce((a, b) => a + b, 0) / temps.length,
|
||
feels_like_day: midDayData.feels_like,
|
||
humidity: midDayData.humidity,
|
||
wind_speed: midDayData.wind_speed,
|
||
weather_main: midDayData.weather?.[0]?.main || 'Clear',
|
||
weather_description: midDayData.weather?.[0]?.description || 'açık',
|
||
weather_icon: midDayData.weather?.[0]?.icon || '01d',
|
||
pop: Math.max(...hourlyByDay[dateKey].map(h => h.pop || 0)),
|
||
sunrise: 0,
|
||
sunset: 0,
|
||
});
|
||
}
|
||
}
|
||
}
|
||
|
||
// Return whatever forecasts we have
|
||
console.log(`${forecasts.length} günlük tahmin döndürülüyor`);
|
||
|
||
return new Response(
|
||
JSON.stringify({
|
||
forecasts,
|
||
location: {
|
||
lat: weatherData.lat,
|
||
lon: weatherData.lon,
|
||
timezone: weatherData.timezone,
|
||
},
|
||
units,
|
||
}),
|
||
{ status: 200, headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
|
||
);
|
||
|
||
} catch (error: any) {
|
||
console.error('Hava durumu fonksiyonu hatası:', error);
|
||
return new Response(
|
||
JSON.stringify({ error: error.message || 'Sunucu hatası' }),
|
||
{ status: 500, headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
|
||
);
|
||
}
|
||
});
|