50 lines
1.5 KiB
TypeScript
50 lines
1.5 KiB
TypeScript
import { Injectable, Logger, OnModuleInit } from '@nestjs/common';
|
|
import IP2Region from 'ip2region';
|
|
import type { IpRegion } from './geo.types';
|
|
|
|
function cleanRegionName(value?: string | null): string | null {
|
|
if (!value) return null;
|
|
const trimmed = value.trim();
|
|
if (!trimmed || trimmed === '0' || trimmed === '内网IP') return null;
|
|
return trimmed;
|
|
}
|
|
|
|
@Injectable()
|
|
export class IpGeoService implements OnModuleInit {
|
|
private readonly logger = new Logger(IpGeoService.name);
|
|
private query: IP2Region | null = null;
|
|
|
|
onModuleInit() {
|
|
try {
|
|
this.query = new IP2Region();
|
|
} catch (error) {
|
|
this.logger.warn(`ip2region 初始化失败: ${error instanceof Error ? error.message : error}`);
|
|
}
|
|
}
|
|
|
|
resolve(ip: string | null): IpRegion {
|
|
if (!ip || !this.query) {
|
|
return { province: null, city: null, district: null };
|
|
}
|
|
if (ip === '127.0.0.1' || ip.startsWith('192.168.') || ip.startsWith('10.')) {
|
|
return { province: null, city: null, district: null };
|
|
}
|
|
|
|
try {
|
|
const result = this.query.search(ip) as {
|
|
province?: string;
|
|
city?: string;
|
|
region?: string;
|
|
};
|
|
return {
|
|
province: cleanRegionName(result?.province),
|
|
city: cleanRegionName(result?.city),
|
|
district: cleanRegionName(result?.region),
|
|
};
|
|
} catch (error) {
|
|
this.logger.debug(`IP 解析失败 ${ip}: ${error instanceof Error ? error.message : error}`);
|
|
return { province: null, city: null, district: null };
|
|
}
|
|
}
|
|
}
|