314 lines
8.5 KiB
TypeScript
314 lines
8.5 KiB
TypeScript
import { NextResponse } from 'next/server'
|
||
import { prisma } from '@/lib/prisma'
|
||
import { Address } from '@prisma/client'
|
||
|
||
// İsviçre posta kodları ve şehirleri (örnek veri)
|
||
const SWISS_CITIES = [
|
||
{ city: 'Zürich', postcode: '8000' },
|
||
{ city: 'Basel', postcode: '4000' },
|
||
{ city: 'Bern', postcode: '3000' },
|
||
{ city: 'Genf', postcode: '1200' },
|
||
{ city: 'Lausanne', postcode: '1000' },
|
||
{ city: 'Winterthur', postcode: '8400' },
|
||
{ city: 'St. Gallen', postcode: '9000' },
|
||
{ city: 'Lugano', postcode: '6900' },
|
||
]
|
||
|
||
// Sokak isimleri için örnek son ekler
|
||
const STREET_SUFFIXES = [
|
||
'strasse',
|
||
'weg',
|
||
'platz',
|
||
'gasse',
|
||
'allee',
|
||
'boulevard',
|
||
'rue',
|
||
'via',
|
||
]
|
||
|
||
interface NominatimResult {
|
||
place_id: number
|
||
licence: string
|
||
osm_type: string
|
||
osm_id: number
|
||
boundingbox: string[]
|
||
lat: string
|
||
lon: string
|
||
display_name: string
|
||
class: string
|
||
type: string
|
||
importance: number
|
||
address?: {
|
||
house_number?: string
|
||
road?: string
|
||
suburb?: string
|
||
city?: string
|
||
town?: string
|
||
village?: string
|
||
municipality?: string
|
||
state?: string
|
||
postcode?: string
|
||
country?: string
|
||
street?: string
|
||
}
|
||
}
|
||
|
||
interface SearchParams {
|
||
q: string
|
||
type: 'street' | 'number' | 'city'
|
||
street?: string
|
||
}
|
||
|
||
async function searchStreets(query: string): Promise<string[]> {
|
||
try {
|
||
const params = new URLSearchParams({
|
||
q: `${query} ${query.toLowerCase().endsWith('strasse') ? '' : 'strasse'} switzerland`,
|
||
format: 'json',
|
||
addressdetails: '1',
|
||
countrycodes: 'ch',
|
||
limit: '5',
|
||
'accept-language': 'de',
|
||
featuretype: 'street'
|
||
})
|
||
|
||
const response = await fetch(
|
||
`https://nominatim.openstreetmap.org/search?${params}`,
|
||
{
|
||
headers: {
|
||
'User-Agent': 'PostaciApp/1.0'
|
||
}
|
||
}
|
||
)
|
||
|
||
if (!response.ok) {
|
||
throw new Error('Adres arama servisi şu anda kullanılamıyor')
|
||
}
|
||
|
||
const results: NominatimResult[] = await response.json()
|
||
return results
|
||
.filter(result => result.address?.road)
|
||
.map(result => result.address?.road || '')
|
||
.filter((value, index, self) => self.indexOf(value) === index) // Tekrar edenleri kaldır
|
||
} catch (error) {
|
||
console.error('OpenStreetMap API hatası:', error)
|
||
throw error
|
||
}
|
||
}
|
||
|
||
async function searchHouseNumbers(street: string): Promise<string[]> {
|
||
try {
|
||
const params = new URLSearchParams({
|
||
q: `${street} switzerland`,
|
||
format: 'json',
|
||
addressdetails: '1',
|
||
countrycodes: 'ch',
|
||
limit: '15', // Daha fazla sonuç al
|
||
'accept-language': 'de'
|
||
})
|
||
|
||
const response = await fetch(
|
||
`https://nominatim.openstreetmap.org/search?${params}`,
|
||
{
|
||
headers: {
|
||
'User-Agent': 'PostaciApp/1.0'
|
||
}
|
||
}
|
||
)
|
||
|
||
if (!response.ok) {
|
||
throw new Error('Adres arama servisi şu anda kullanılamıyor')
|
||
}
|
||
|
||
const results: NominatimResult[] = await response.json()
|
||
return results
|
||
.filter(result => result.address?.house_number && result.address?.road === street)
|
||
.map(result => result.address?.house_number || '')
|
||
.filter((value, index, self) => self.indexOf(value) === index) // Tekrar edenleri kaldır
|
||
.sort((a, b) => parseInt(a) - parseInt(b)) // Numaraları sırala
|
||
} catch (error) {
|
||
console.error('OpenStreetMap API hatası:', error)
|
||
throw error
|
||
}
|
||
}
|
||
|
||
async function searchCities(query: string): Promise<string[]> {
|
||
try {
|
||
const params = new URLSearchParams({
|
||
q: `${query} switzerland`,
|
||
format: 'json',
|
||
addressdetails: '1',
|
||
countrycodes: 'ch',
|
||
limit: '5',
|
||
'accept-language': 'de',
|
||
featuretype: 'city'
|
||
})
|
||
|
||
const response = await fetch(
|
||
`https://nominatim.openstreetmap.org/search?${params}`,
|
||
{
|
||
headers: {
|
||
'User-Agent': 'PostaciApp/1.0'
|
||
}
|
||
}
|
||
)
|
||
|
||
if (!response.ok) {
|
||
throw new Error('Adres arama servisi şu anda kullanılamıyor')
|
||
}
|
||
|
||
const results: NominatimResult[] = await response.json()
|
||
return results
|
||
.filter(result => result.address?.city || result.address?.postcode)
|
||
.map(result => {
|
||
const city = result.address?.city || ''
|
||
const postcode = result.address?.postcode || ''
|
||
return `${postcode} ${city}`.trim()
|
||
})
|
||
.filter((value, index, self) => self.indexOf(value) === index) // Tekrar edenleri kaldır
|
||
} catch (error) {
|
||
console.error('OpenStreetMap API hatası:', error)
|
||
throw error
|
||
}
|
||
}
|
||
|
||
// Levenshtein mesafesi hesaplama
|
||
function levenshteinDistance(a: string, b: string): number {
|
||
if (a.length === 0) return b.length
|
||
if (b.length === 0) return a.length
|
||
|
||
const matrix = []
|
||
|
||
for (let i = 0; i <= b.length; i++) {
|
||
matrix[i] = [i]
|
||
}
|
||
|
||
for (let j = 0; j <= a.length; j++) {
|
||
matrix[0][j] = j
|
||
}
|
||
|
||
for (let i = 1; i <= b.length; i++) {
|
||
for (let j = 1; j <= a.length; j++) {
|
||
if (b.charAt(i - 1) === a.charAt(j - 1)) {
|
||
matrix[i][j] = matrix[i - 1][j - 1]
|
||
} else {
|
||
matrix[i][j] = Math.min(
|
||
matrix[i - 1][j - 1] + 1,
|
||
matrix[i][j - 1] + 1,
|
||
matrix[i - 1][j] + 1
|
||
)
|
||
}
|
||
}
|
||
}
|
||
|
||
return matrix[b.length][a.length]
|
||
}
|
||
|
||
// Benzerlik oranı hesaplama
|
||
function calculateSimilarity(input: string, candidate: string): number {
|
||
const distance = levenshteinDistance(input.toLowerCase(), candidate.toLowerCase())
|
||
const maxLength = Math.max(input.length, candidate.length)
|
||
return 1 - distance / maxLength
|
||
}
|
||
|
||
// Yedek adres verileri
|
||
const BACKUP_ADDRESSES = [
|
||
'Luzernstrasse 27, 4552 Derendingen',
|
||
'Luzernstrasse 15, 4552 Derendingen',
|
||
'Luzernstrasse 29, 4552 Derendingen',
|
||
'Luzernstrasse 25, 4552 Derendingen',
|
||
'Luzernstrasse 31, 4552 Derendingen',
|
||
'Luzernstrasse 12, 6003 Luzern',
|
||
'Luzernstrasse 8, 6003 Luzern',
|
||
'Luzernstrasse 16, 6003 Luzern',
|
||
'Bahnhofstrasse 10, 3011 Bern',
|
||
'Hauptstrasse 1, 8001 Zürich',
|
||
'Kirchstrasse 7, 2502 Biel',
|
||
'Marktgasse 8, 4051 Basel',
|
||
'Oberer Graben 12, 9000 St. Gallen',
|
||
'Pilatusstrasse 20, 6003 Luzern',
|
||
'Rue de Lausanne 25, 1800 Vevey',
|
||
'Rue du Mont-Blanc 15, 1201 Genève',
|
||
'Via Nassa 5, 6900 Lugano'
|
||
]
|
||
|
||
export async function GET(request: Request) {
|
||
try {
|
||
const { searchParams } = new URL(request.url)
|
||
const query = searchParams.get('q')
|
||
|
||
if (!query || query.length < 2) {
|
||
return NextResponse.json({ error: 'Arama terimi çok kısa' }, { status: 400 })
|
||
}
|
||
|
||
// Önce Nominatim API'den ara
|
||
try {
|
||
const params = new URLSearchParams({
|
||
q: `${query} switzerland`,
|
||
format: 'json',
|
||
addressdetails: '1',
|
||
countrycodes: 'ch',
|
||
limit: '5',
|
||
'accept-language': 'de'
|
||
})
|
||
|
||
const response = await fetch(
|
||
`https://nominatim.openstreetmap.org/search?${params}`,
|
||
{
|
||
headers: {
|
||
'User-Agent': 'PostaciApp/1.0'
|
||
}
|
||
}
|
||
)
|
||
|
||
if (!response.ok) {
|
||
throw new Error('Nominatim API yanıt vermedi')
|
||
}
|
||
|
||
const results: NominatimResult[] = await response.json()
|
||
const addresses = results
|
||
.filter(result => result.address?.road || result.display_name)
|
||
.map(result => {
|
||
const street = result.address?.road || ''
|
||
const houseNumber = result.address?.house_number || ''
|
||
const postcode = result.address?.postcode || ''
|
||
const city = result.address?.city || result.address?.town || result.address?.village || ''
|
||
return `${street} ${houseNumber}, ${postcode} ${city}`.trim().replace(/\s+/g, ' ')
|
||
})
|
||
.filter(address => address.length > 0)
|
||
|
||
// Eğer Nominatim'den sonuç geldiyse, döndür
|
||
if (addresses.length > 0) {
|
||
return NextResponse.json(addresses)
|
||
}
|
||
} catch (error) {
|
||
console.warn('Nominatim API hatası:', error)
|
||
// Nominatim hatası durumunda yedek adreslere geç
|
||
}
|
||
|
||
// Yedek adreslerden benzerlik araması yap
|
||
const sortedAddresses = BACKUP_ADDRESSES
|
||
.map(address => ({
|
||
address,
|
||
similarity: calculateSimilarity(query, address)
|
||
}))
|
||
.sort((a, b) => b.similarity - a.similarity)
|
||
.filter(item => item.similarity > 0.3)
|
||
.map(item => item.address)
|
||
.slice(0, 5)
|
||
|
||
if (sortedAddresses.length === 0) {
|
||
return NextResponse.json(
|
||
{ error: 'Sonuç bulunamadı' },
|
||
{ status: 200 } // 404 yerine 200 dönüyoruz
|
||
)
|
||
}
|
||
|
||
return NextResponse.json(sortedAddresses)
|
||
} catch (error) {
|
||
console.error('Adres arama hatası:', error)
|
||
return NextResponse.json(
|
||
{ error: 'Adres arama sırasında bir hata oluştu' },
|
||
{ status: 200 } // 404 yerine 200 dönüyoruz
|
||
)
|
||
}
|
||
}
|