122 lines
4.0 KiB
TypeScript
122 lines
4.0 KiB
TypeScript
import { Inject, Injectable } from '@nestjs/common';
|
|
import { readFileSync } from 'fs';
|
|
import { WINSTON_MODULE_PROVIDER } from 'nest-winston';
|
|
import { join } from 'path';
|
|
import { CityAndStateResult, CountyResult, GeoResult, IpInfo, RealIpInfo } from 'src/models/main.model';
|
|
import { Logger } from 'winston';
|
|
import { City, CountyData, Geo, State } from '../models/server.model';
|
|
// const __filename = fileURLToPath(import.meta.url);
|
|
// const __dirname = path.dirname(__filename);
|
|
|
|
@Injectable()
|
|
export class GeoService {
|
|
geo: Geo;
|
|
counties: CountyData[];
|
|
constructor(@Inject(WINSTON_MODULE_PROVIDER) private readonly logger: Logger) {
|
|
this.loadGeo();
|
|
}
|
|
private loadGeo(): void {
|
|
const filePath = join(__dirname, '../..', 'assets', 'geo.json');
|
|
const rawData = readFileSync(filePath, 'utf8');
|
|
this.geo = JSON.parse(rawData);
|
|
const countiesFilePath = join(__dirname, '../..', 'assets', 'counties.json');
|
|
const rawCountiesData = readFileSync(countiesFilePath, 'utf8');
|
|
this.counties = JSON.parse(rawCountiesData);
|
|
}
|
|
findCountiesStartingWith(prefix: string, states?: string[]) {
|
|
const results: CountyResult[] = [];
|
|
let idCounter = 1;
|
|
|
|
this.counties.forEach(stateData => {
|
|
if (!states || states.includes(stateData.state)) {
|
|
stateData.counties.forEach(county => {
|
|
if (county.startsWith(prefix?.toUpperCase())) {
|
|
results.push({
|
|
id: idCounter++,
|
|
name: county,
|
|
state: stateData.state_full,
|
|
state_code: stateData.state,
|
|
});
|
|
}
|
|
});
|
|
}
|
|
});
|
|
|
|
return results;
|
|
}
|
|
findCitiesStartingWith(prefix: string, state?: string): GeoResult[] {
|
|
const result: GeoResult[] = [];
|
|
|
|
this.geo.states.forEach((state: State) => {
|
|
state.cities.forEach((city: City) => {
|
|
if (city.name.toLowerCase().startsWith(prefix.toLowerCase())) {
|
|
result.push({
|
|
id: city.id,
|
|
name: city.name,
|
|
state: state.state_code,
|
|
//state_code: state.state_code,
|
|
latitude: city.latitude,
|
|
longitude: city.longitude,
|
|
});
|
|
}
|
|
});
|
|
});
|
|
return state ? result.filter(e => e.state.toLowerCase() === state.toLowerCase()) : result;
|
|
}
|
|
findCitiesAndStatesStartingWith(prefix: string): Array<CityAndStateResult> {
|
|
const results: Array<CityAndStateResult> = [];
|
|
|
|
const lowercasePrefix = prefix.toLowerCase();
|
|
|
|
//for (const country of this.geo) {
|
|
// Suche nach passenden Staaten
|
|
for (const state of this.geo.states) {
|
|
if (state.name.toLowerCase().startsWith(lowercasePrefix)) {
|
|
results.push({
|
|
id: state.id,
|
|
type: 'state',
|
|
content: state,
|
|
});
|
|
}
|
|
|
|
// Suche nach passenden Städten
|
|
for (const city of state.cities) {
|
|
if (city.name.toLowerCase().startsWith(lowercasePrefix)) {
|
|
results.push({
|
|
id: city.id,
|
|
type: 'city',
|
|
content: { state: state.state_code, ...city },
|
|
});
|
|
}
|
|
}
|
|
//}
|
|
}
|
|
|
|
return results.sort((a, b) => {
|
|
if (a.type === 'state' && b.type === 'city') return -1;
|
|
if (a.type === 'city' && b.type === 'state') return 1;
|
|
return a.content.name.localeCompare(b.content.name);
|
|
});
|
|
}
|
|
getCityWithCoords(state: string, city: string): City {
|
|
return this.geo.states.find(s => s.state_code === state).cities.find(c => c.name === city);
|
|
}
|
|
async fetchIpAndGeoLocation(ipInfo: RealIpInfo): Promise<IpInfo> {
|
|
this.logger.info(`IP:${ipInfo.ip} - CountryCode:${ipInfo.countryCode}`);
|
|
const response = await fetch(`${process.env.IP_INFO_URL}/${ipInfo.ip}/geo?token=${process.env.IP_INFO_TOKEN}`, {
|
|
method: 'GET',
|
|
});
|
|
if (!response.ok) {
|
|
throw new Error(`HTTP error! status: ${response.status}`);
|
|
}
|
|
const data = await response.json();
|
|
|
|
// Fügen Sie den Ländercode aus Cloudflare hinzu, falls verfügbar
|
|
if (ipInfo.countryCode) {
|
|
data.cloudflareCountry = ipInfo.countryCode;
|
|
}
|
|
|
|
return data;
|
|
}
|
|
}
|