Skip to content

Commit

Permalink
Multi IP Geo sources
Browse files Browse the repository at this point in the history
  • Loading branch information
jason5ng32 committed Feb 6, 2024
1 parent ad1974e commit 9d80cb6
Show file tree
Hide file tree
Showing 14 changed files with 184 additions and 133 deletions.
Binary file removed api/GeoLite2-ASN.mmdb
Binary file not shown.
Binary file removed api/GeoLite2-City.mmdb
Binary file not shown.
59 changes: 59 additions & 0 deletions api/ipchecking.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { get } from 'https';

function isValidIP(ip) {
const ipv4Pattern =
/^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/;
const ipv6Pattern =
/^(([0-9a-fA-F]{1,4}:){7}([0-9a-fA-F]{1,4})|(([0-9a-fA-F]{1,4}:){0,6}([0-9a-fA-F]{1,4})?::([0-9a-fA-F]{1,4}:){0,6}([0-9a-fA-F]{1,4})?))$/;
return ipv4Pattern.test(ip) || ipv6Pattern.test(ip);
};


export default (req, res) => {

// 限制只能从指定域名访问
const allowedDomains = ['localhost', ...(process.env.ALLOWED_DOMAINS || '').split(',')];
const referer = req.headers.referer;

if (referer) {
const domain = new URL(referer).hostname;
if (!allowedDomains.includes(domain)) {
return res.status(403).json({ error: 'Access denied' });
}
} else {
return res.status(403).json({ error: 'What are you doing?' });
}

// 从请求中获取 IP 地址
const ipAddress = req.query.ip;
if (!ipAddress) {
return res.status(400).json({ error: 'No IP address provided' });
}

// 检查 IP 地址是否合法
if (!isValidIP(ipAddress)) {
return res.status(400).json({ error: 'Invalid IP address' });
}

const lang = req.query.lang || 'en';

const key = process.env.IPChecking_API_KEY;

// 构建请求 IPCheck.ing 的 URL
const url = new URL(`https://api.ipcheck.ing/ipinfo?key=${key}&ip=${ipAddress}&lang=${lang}`);

get(url, apiRes => {
let data = '';
apiRes.on('data', chunk => data += chunk);
apiRes.on('end', () => {
try {
const originalJson = JSON.parse(data);
res.json(originalJson);
} catch (e) {
res.status(500).json({ error: 'Error parsing JSON' });
}
});
}).on('error', (e) => {
res.status(500).json({ error: e.message });
});
}
85 changes: 0 additions & 85 deletions api/maxmind.js

This file was deleted.

Binary file removed public/data/GeoLite2-ASN.mmdb
Binary file not shown.
Binary file removed public/data/GeoLite2-City.mmdb
Binary file not shown.
4 changes: 2 additions & 2 deletions server.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import validateSite from './api/validate-site.js';
import ipinfoHandler from './api/ipinfo.js';
import ipapicomHandler from './api/ipapicom.js';
import keycdnHandler from './api/keycdn.js';
import maxmindHandler from './api/maxmind.js';
import ipCheckingHandler from './api/ipchecking.js';
import ipsbHandler from './api/ipsb.js';

dotenv.config();
Expand All @@ -23,7 +23,7 @@ app.get('/api/map', mapHandler);
app.get('/api/ipinfo', ipinfoHandler);
app.get('/api/ipapicom', ipapicomHandler);
app.get('/api/keycdn', keycdnHandler);
app.get('/api/maxmind', maxmindHandler);
app.get('/api/ipchecking', ipCheckingHandler);
app.get('/api/ipsb', ipsbHandler);

// 设置静态文件服务
Expand Down
4 changes: 2 additions & 2 deletions src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,12 @@
<div data-bs-spy="scroll" data-bs-target="#navbar-top" data-bs-root-margin="0px 0px -40%" data-bs-smooth-scroll="true"
class="rounded-2" tabindex="0">
<IPCheck ref="IPCheckRef" />
<Connectivity ref="connectivityRef" />
<!-- <Connectivity ref="connectivityRef" />
<WebRTC ref="webRTCRef" />
<DNSLeaks ref="dnsLeaksRef" />
<SpeedTest ref="speedTestRef" />
<GlobalLatency ref="globalLatencyRef" />
<MTRtest ref="mtrtestRef" />
<MTRtest ref="mtrtestRef" /> -->
<QueryIP ref="queryIPRef" />
<HelpModal ref="helpModalRef" />
<!-- Info Mask BTN-->
Expand Down
82 changes: 48 additions & 34 deletions src/components/ipcheck.vue
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,15 @@
<li>
<hr class="dropdown-divider">
</li>
<li v-for="source in sources" :key="source.value">
<a class="dropdown-item" :class="{ active: ipGeoSource === source.value }"
@click="selectIPGeoSource(source.value)">
<li v-for="source in sources" :key="source.id">
<a class="dropdown-item" :class="{ active: ipGeoSource === source.id, disabled: !source.enabled }"
@click="source.enabled ? selectIPGeoSource(source.id) : null" :disabled="!source.enabled"
:aria-disabled="!source.enabled">
{{ source.text }}
<i class="bi bi-check2-circle" v-if="ipGeoSource === source.value"></i>
<i class="bi bi-check2-circle" v-if="ipGeoSource === source.id"></i>
</a>
</li>

</ul>
</div>

Expand Down Expand Up @@ -175,26 +177,26 @@ export default {
const store = useStore();
const isDarkMode = computed(() => store.state.isDarkMode);
const isMobile = computed(() => store.state.isMobile);
const ipGeoSource = computed(() => store.state.ipGeoSource);
return {
isDarkMode,
isMobile,
ipGeoSource,
};
},
data() {
return {
isCardsCollapsed: JSON.parse(localStorage.getItem('isCardsCollapsed')) || false,
placeholderSizes: [12, 8, 6, 8, 4, 8],
ipGeoSource: 'maxmind',
sources: [
{ value: 'maxmind', text: 'MaxMind' },
{ value: 'ipsb', text: 'IP.SB' },
{ value: 'ipinfo', text: 'IPinfo.io' },
{ value: 'ipapicom', text: 'IP-API.com' },
{ value: 'ipapi', text: 'IPAPI.co' },
{ value: 'keycdn', text: 'KeyCDN' },
{ id: 0, text: 'IPCheck.ing', enabled: true },
{ id: 1, text: 'IP.SB', enabled: true },
{ id: 2, text: 'IPinfo.io', enabled: true },
{ id: 3, text: 'IP-API.com', enabled: true },
{ id: 4, text: 'IPAPI.co', enabled: true },
{ id: 5, text: 'KeyCDN', enabled: true },
],
ipDataCards: [
{
Expand Down Expand Up @@ -484,7 +486,7 @@ export default {
},
// 从 IP 地址获取 IP 详细信息
async fetchIPDetails(cardIndex, ip, sourceName = null) {
async fetchIPDetails(cardIndex, ip, sourceID = null) {
const card = this.ipDataCards[cardIndex];
card.ip = ip;
let lang = this.$Lang;
Expand All @@ -502,17 +504,20 @@ export default {
// 不同的源
const sources = [
{ name: "maxmind", url: `/api/maxmind?ip=${ip}&lang=${lang}`, transform: this.transformDataFromIPapi },
{ name: "ipsb", url: `/api/ipsb?ip=${ip}`, transform: this.transformDataFromIPapi },
{ name: "ipinfo", url: `/api/ipinfo?ip=${ip}`, transform: this.transformDataFromIPapi },
{ name: "ipapicom", url: `/api/ipapicom?ip=${ip}&lang=${lang}`, transform: this.transformDataFromIPapi },
{ name: "ipapi", url: `https://ipapi.co/${ip}/json/`, transform: this.transformDataFromIPapi },
{ name: "keycdn", url: `api/keycdn?ip=${ip}`, transform: this.transformDataFromIPapi },
{ id: 0, url: `/api/ipchecking?ip=${ip}&lang=${lang}`, transform: this.transformDataFromIPapi },
{ id: 1, url: `/api/ipinfo?ip=${ip}`, transform: this.transformDataFromIPapi },
{ id: 2, url: `/api/ipsb?ip=${ip}`, transform: this.transformDataFromIPapi },
{ id: 3, url: `/api/ipapicom?ip=${ip}&lang=${lang}`, transform: this.transformDataFromIPapi },
{ id: 4, url: `https://ipapi.co/${ip}/json/`, transform: this.transformDataFromIPapi },
{ id: 5, url: `api/keycdn?ip=${ip}`, transform: this.transformDataFromIPapi },
];
let OrignalSourceID = sourceID;
let retryCount = 0;
// 根据指定的源获取数据
for (const source of sources) {
if (sourceName && source.name !== sourceName) {
if (sourceID && source.id !== sourceID) {
continue;
}
Expand All @@ -529,20 +534,31 @@ export default {
break;
}
} catch (error) {
console.error("Error fetching IP details:", error);
// 失败时改为使用 MaxMind 数据源
this.selectIPGeoSource("maxmind");
this.sources[OrignalSourceID].enabled = false;
if (retryCount < 5) {
if (OrignalSourceID === 5) {
OrignalSourceID = 0;
} else {
OrignalSourceID++;
}
this.selectIPGeoSource(OrignalSourceID);
retryCount++;
} else {
console.error("Error fetching IP details:", error);
}
}
}
},
// 选择 IP 数据源,并保存到本地存储
selectIPGeoSource(source) {
if (this.ipGeoSource === source) {
selectIPGeoSource(sourceID) {
if (this.ipGeoSource === sourceID) {
return;
}
this.ipGeoSource = source;
localStorage.setItem("ipGeoSource", source);
this.$store.commit('SET_IP_GEO_SOURCE', sourceID);
localStorage.setItem("ipGeoSource", parseInt(sourceID));
// 清空部分数据
this.ipDataCards.forEach((card) => {
card.country_name = "";
Expand All @@ -562,8 +578,8 @@ export default {
const interval = setInterval(() => {
if (index < this.ipDataCards.length) {
const card = this.ipDataCards[index];
if (card.ip) {
this.fetchIPDetails(index, card.ip, source);
if (this.isValidIP(card.ip)) {
this.fetchIPDetails(index, card.ip, sourceID);
}
index++;
} else {
Expand Down Expand Up @@ -701,9 +717,6 @@ export default {
isCardsCollapsed(newVal) {
localStorage.setItem('isCardsCollapsed', JSON.stringify(newVal));
},
ipGeoSource(newVal) {
localStorage.setItem('ipGeoSource', newVal);
},
ipDataCards: {
handler(newValue) {
this.$store.commit('updateGlobalIpDataCards', newValue);
Expand All @@ -714,10 +727,11 @@ export default {
mounted() {
this.checkAllIPs();
// 从本地存储中获取 ipGeoSource
const ipGeoSource = localStorage.getItem('ipGeoSource');
if (ipGeoSource) {
this.ipGeoSource = ipGeoSource;
if (localStorage.getItem('ipGeoSource')) {
this.$store.commit('SET_IP_GEO_SOURCE', parseInt(ipGeoSource));
}
},
}
Expand Down
23 changes: 14 additions & 9 deletions src/components/queryip.vue
Original file line number Diff line number Diff line change
Expand Up @@ -92,10 +92,12 @@ export default {
const store = useStore();
const isDarkMode = computed(() => store.state.isDarkMode);
const isMobile = computed(() => store.state.isMobile);
const ipGeoSource = computed(() => store.state.ipGeoSource);
return {
isDarkMode,
isMobile
isMobile,
ipGeoSource
};
},
Expand Down Expand Up @@ -158,23 +160,26 @@ export default {
},
// 获取 IP 信息
async fetchIPForModal(ip, sourceName = null) {
async fetchIPForModal(ip, sourceID = null) {
let lang = this.$Lang;
if (lang === 'zh') {
lang = 'zh-CN';
};
sourceID = this.ipGeoSource;
const sources = [
{ name: "ipinfo", url: `/api/ipinfo?ip=${ip}`, transform: this.transformDataFromIPapi },
{ name: "ipapicom", url: `/api/ipapicom?ip=${ip}&lang=${lang}`, transform: this.transformDataFromIPapi },
{ name: "ipsb", url: `/api/ipsb?ip=${ip}`, transform: this.transformDataFromIPapi },
{ name: "ipapi", url: `https://ipapi.co/${ip}/json/`, transform: this.transformDataFromIPapi },
{ name: "keycdn", url: `api/keycdn?ip=${ip}`, transform: this.transformDataFromIPapi },
{ name: "maxmind", url: `/api/maxmind?ip=${ip}&lang=${lang}`, transform: this.transformDataFromIPapi },
{ id: 0, url: `/api/ipchecking?ip=${ip}&lang=${lang}`, transform: this.transformDataFromIPapi },
{ id: 1, url: `/api/ipinfo?ip=${ip}`, transform: this.transformDataFromIPapi },
{ id: 2, url: `/api/ipsb?ip=${ip}`, transform: this.transformDataFromIPapi },
{ id: 3, url: `/api/ipapicom?ip=${ip}&lang=${lang}`, transform: this.transformDataFromIPapi },
{ id: 4, url: `https://ipapi.co/${ip}/json/`, transform: this.transformDataFromIPapi },
{ id: 5, url: `api/keycdn?ip=${ip}`, transform: this.transformDataFromIPapi },
];
// 根据指定的源获取数据
for (const source of sources) {
if (sourceName && source.name !== sourceName) {
if (sourceID && source.id !== sourceID) {
continue;
}
try {
Expand Down
Loading

0 comments on commit 9d80cb6

Please sign in to comment.