Files
Tenki/src/index.ts
T
2023-09-24 19:55:57 +07:00

149 lines
3.7 KiB
TypeScript

import express from "express";
import axios from "axios";
const app = express();
const port = 3000;
enum IpLookupStatus {
success = "success",
fail = "fail",
}
interface GeoIpLookupCache {
[key: string]: {
status: IpLookupStatus;
country: string;
countryCode: string;
region: string;
regionName: string;
city: string;
zip: string;
lat: number;
lon: number;
timezone: string;
isp: string;
org: string;
as: string;
query: string;
cacheExpireAt: Date;
};
}
interface WeatherLookupCache {
[key: string]: {
latitude: number;
longitude: number;
generationtime_ms: number;
utc_offset_seconds: number;
timezone: string;
timezone_abbreviation: string;
elevation: number;
current_weather: {
temperature: number;
windspeed: number;
winddirection: number;
weathercode: number;
is_day: boolean;
time: string;
};
cacheExpireAt: Date;
};
}
const geoIpCacheTime = 1000 * 60 * 60 * 24; // 24 hours
const weatherCacheTime = 1000 * 60 * 15; // 15 minutes
const geoIpLookupCache: GeoIpLookupCache = {};
const weatherLookupCache: WeatherLookupCache = {};
const isReservedIpRange = (ipAddress: string) => {
// Check for IPv4
const ipv4Pattern =
/^(10\..*|172\.(1[6-9]|2[0-9]|3[0-1])\..*|192\.168\..*|127\..*|169\.254\..*|192\.88\.99\..*)$/;
if (ipv4Pattern.test(ipAddress)) {
return true;
}
// Check for IPv6
const ipv6LocalPattern =
/^fe[89ab][0-9a-fA-F]:.*|^::1$|^fc[0-9a-fA-F]{2}:.*$/;
if (ipv6LocalPattern.test(ipAddress.toLowerCase())) {
return true;
}
return false;
};
const getGeoIp = async (ip: string) => {
const response = await axios.get(`http://ip-api.com/json/${ip}`);
let data = response.data;
data.cacheExpireAt = new Date(Date.now() + geoIpCacheTime);
geoIpLookupCache[ip] = data;
return data;
};
const getWeather = async (lat: number, lon: number) => {
const response = await axios.get(
`https://api.open-meteo.com/v1/forecast?latitude=${lat}&longitude=${lon}&current_weather=true`
);
let data = response.data;
data.cacheExpireAt = new Date(Date.now() + weatherCacheTime);
weatherLookupCache[`${lat},${lon}`] = data;
return data;
};
app.get("/", async (req, res) => {
// Get the user's IP address
let ip = req.headers["x-forwarded-for"] || req.socket.remoteAddress;
ip = ip?.toString().split(",")[0];
if (!ip) return res.status(500).send("Error fetching location data");
// Check if the IP address is in a reserved range, assume Japan IP if so (Linode IP address)
if (isReservedIpRange(ip)) {
ip = "139.162.65.37";
}
// Check if geo IP is cached
let geoIp = geoIpLookupCache[ip];
if (geoIp) {
console.log(`Cache is valid for: ${geoIp.cacheExpireAt}`);
// Check if cached response is expired
if (geoIp.cacheExpireAt < new Date()) {
delete geoIpLookupCache[ip];
}
} else {
console.log("Fetching GeoIP fresh response");
try {
geoIp = await getGeoIp(ip);
} catch (e) {
return res.status(500).send("Error fetching location data");
}
}
// Check if weather is cached
let weather = weatherLookupCache[`${geoIp.lat},${geoIp.lon}`];
if (weather) {
// Check if cached response is expired
if (weather.cacheExpireAt < new Date()) {
delete weatherLookupCache[`${geoIp.lat},${geoIp.lon}`];
}
} else {
console.log("Fetching weather fresh response");
try {
weather = await getWeather(geoIp.lat, geoIp.lon);
} catch (e) {
return res.status(500).send("Error fetching location data");
}
}
return res.json(weather);
});
app.listen(port, () => {
console.log(`Server running at port: ${port}`);
});