init
This commit is contained in:
245
lib/geo/ip-location.ts
Normal file
245
lib/geo/ip-location.ts
Normal file
@@ -0,0 +1,245 @@
|
||||
import { db } from "@/lib/db";
|
||||
import { geoCache } from "@/lib/db/schema";
|
||||
import { eq } from "drizzle-orm";
|
||||
|
||||
interface GeoLocation {
|
||||
latitude: number;
|
||||
longitude: number;
|
||||
city: string;
|
||||
country: string;
|
||||
countryCode: string;
|
||||
region: string;
|
||||
}
|
||||
|
||||
interface IpApiResponse {
|
||||
status: string;
|
||||
lat: number;
|
||||
lon: number;
|
||||
city: string;
|
||||
country: string;
|
||||
countryCode: string;
|
||||
}
|
||||
|
||||
const CONTINENT_MAP: Record<string, string> = {
|
||||
// Asia
|
||||
CN: "Asia",
|
||||
JP: "Asia",
|
||||
KR: "Asia",
|
||||
IN: "Asia",
|
||||
ID: "Asia",
|
||||
TH: "Asia",
|
||||
VN: "Asia",
|
||||
PH: "Asia",
|
||||
MY: "Asia",
|
||||
SG: "Asia",
|
||||
TW: "Asia",
|
||||
HK: "Asia",
|
||||
BD: "Asia",
|
||||
PK: "Asia",
|
||||
LK: "Asia",
|
||||
NP: "Asia",
|
||||
MM: "Asia",
|
||||
KH: "Asia",
|
||||
LA: "Asia",
|
||||
MN: "Asia",
|
||||
KZ: "Asia",
|
||||
UZ: "Asia",
|
||||
AE: "Asia",
|
||||
SA: "Asia",
|
||||
IL: "Asia",
|
||||
TR: "Asia",
|
||||
IQ: "Asia",
|
||||
IR: "Asia",
|
||||
QA: "Asia",
|
||||
KW: "Asia",
|
||||
BH: "Asia",
|
||||
OM: "Asia",
|
||||
JO: "Asia",
|
||||
LB: "Asia",
|
||||
AF: "Asia",
|
||||
|
||||
// Europe
|
||||
GB: "Europe",
|
||||
DE: "Europe",
|
||||
FR: "Europe",
|
||||
IT: "Europe",
|
||||
ES: "Europe",
|
||||
PT: "Europe",
|
||||
NL: "Europe",
|
||||
BE: "Europe",
|
||||
SE: "Europe",
|
||||
NO: "Europe",
|
||||
DK: "Europe",
|
||||
FI: "Europe",
|
||||
PL: "Europe",
|
||||
CZ: "Europe",
|
||||
AT: "Europe",
|
||||
CH: "Europe",
|
||||
IE: "Europe",
|
||||
RO: "Europe",
|
||||
HU: "Europe",
|
||||
GR: "Europe",
|
||||
UA: "Europe",
|
||||
RU: "Europe",
|
||||
BG: "Europe",
|
||||
HR: "Europe",
|
||||
SK: "Europe",
|
||||
SI: "Europe",
|
||||
RS: "Europe",
|
||||
LT: "Europe",
|
||||
LV: "Europe",
|
||||
EE: "Europe",
|
||||
IS: "Europe",
|
||||
LU: "Europe",
|
||||
MT: "Europe",
|
||||
CY: "Europe",
|
||||
AL: "Europe",
|
||||
MK: "Europe",
|
||||
BA: "Europe",
|
||||
ME: "Europe",
|
||||
MD: "Europe",
|
||||
BY: "Europe",
|
||||
|
||||
// Americas
|
||||
US: "Americas",
|
||||
CA: "Americas",
|
||||
MX: "Americas",
|
||||
BR: "Americas",
|
||||
AR: "Americas",
|
||||
CO: "Americas",
|
||||
CL: "Americas",
|
||||
PE: "Americas",
|
||||
VE: "Americas",
|
||||
EC: "Americas",
|
||||
BO: "Americas",
|
||||
PY: "Americas",
|
||||
UY: "Americas",
|
||||
CR: "Americas",
|
||||
PA: "Americas",
|
||||
CU: "Americas",
|
||||
DO: "Americas",
|
||||
GT: "Americas",
|
||||
HN: "Americas",
|
||||
SV: "Americas",
|
||||
NI: "Americas",
|
||||
JM: "Americas",
|
||||
TT: "Americas",
|
||||
HT: "Americas",
|
||||
PR: "Americas",
|
||||
GY: "Americas",
|
||||
SR: "Americas",
|
||||
BZ: "Americas",
|
||||
|
||||
// Africa
|
||||
ZA: "Africa",
|
||||
NG: "Africa",
|
||||
KE: "Africa",
|
||||
EG: "Africa",
|
||||
GH: "Africa",
|
||||
ET: "Africa",
|
||||
TZ: "Africa",
|
||||
MA: "Africa",
|
||||
DZ: "Africa",
|
||||
TN: "Africa",
|
||||
UG: "Africa",
|
||||
SN: "Africa",
|
||||
CI: "Africa",
|
||||
CM: "Africa",
|
||||
MZ: "Africa",
|
||||
MG: "Africa",
|
||||
AO: "Africa",
|
||||
ZW: "Africa",
|
||||
RW: "Africa",
|
||||
LY: "Africa",
|
||||
SD: "Africa",
|
||||
CD: "Africa",
|
||||
ML: "Africa",
|
||||
BF: "Africa",
|
||||
NE: "Africa",
|
||||
MW: "Africa",
|
||||
ZM: "Africa",
|
||||
BW: "Africa",
|
||||
NA: "Africa",
|
||||
MU: "Africa",
|
||||
|
||||
// Oceania
|
||||
AU: "Oceania",
|
||||
NZ: "Oceania",
|
||||
FJ: "Oceania",
|
||||
PG: "Oceania",
|
||||
WS: "Oceania",
|
||||
TO: "Oceania",
|
||||
VU: "Oceania",
|
||||
SB: "Oceania",
|
||||
GU: "Oceania",
|
||||
NC: "Oceania",
|
||||
PF: "Oceania",
|
||||
};
|
||||
|
||||
function getRegion(countryCode: string): string {
|
||||
return CONTINENT_MAP[countryCode] ?? "Unknown";
|
||||
}
|
||||
|
||||
export async function getGeoLocation(
|
||||
ip: string
|
||||
): Promise<GeoLocation | null> {
|
||||
try {
|
||||
const cached = await db
|
||||
.select()
|
||||
.from(geoCache)
|
||||
.where(eq(geoCache.ip, ip))
|
||||
.limit(1);
|
||||
|
||||
if (cached.length > 0) {
|
||||
const row = cached[0];
|
||||
return {
|
||||
latitude: Number(row.latitude),
|
||||
longitude: Number(row.longitude),
|
||||
city: row.city ?? "",
|
||||
country: row.country ?? "",
|
||||
countryCode: row.countryCode ?? "",
|
||||
region: row.region ?? "",
|
||||
};
|
||||
}
|
||||
|
||||
const response = await fetch(`http://ip-api.com/json/${ip}`, {
|
||||
signal: AbortSignal.timeout(5000),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const data: IpApiResponse = await response.json();
|
||||
|
||||
if (data.status !== "success") {
|
||||
return null;
|
||||
}
|
||||
|
||||
const region = getRegion(data.countryCode);
|
||||
|
||||
const geoData: GeoLocation = {
|
||||
latitude: data.lat,
|
||||
longitude: data.lon,
|
||||
city: data.city,
|
||||
country: data.country,
|
||||
countryCode: data.countryCode,
|
||||
region,
|
||||
};
|
||||
|
||||
await db.insert(geoCache).values({
|
||||
ip,
|
||||
latitude: String(geoData.latitude),
|
||||
longitude: String(geoData.longitude),
|
||||
city: geoData.city,
|
||||
country: geoData.country,
|
||||
countryCode: geoData.countryCode,
|
||||
region: geoData.region,
|
||||
});
|
||||
|
||||
return geoData;
|
||||
} catch (error) {
|
||||
console.error("Failed to get geo location for IP:", ip, error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user