Skip to content

Commit

Permalink
Maxmind DB supported
Browse files Browse the repository at this point in the history
  • Loading branch information
jason5ng32 authored Jul 29, 2024
2 parents 94f2e0a + 7b1b8e1 commit 70ee698
Show file tree
Hide file tree
Showing 15 changed files with 104 additions and 24 deletions.
13 changes: 9 additions & 4 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
# Server
BACKEND_PORT="11966"
FRONTEND_PORT="18966"
BING_MAP_API_KEY=""
ALLOWED_DOMAINS=""
# APIs
BING_MAP_API_KEY=""
IPINFO_API_TOKEN=""
KEYCDN_USER_AGENT=""
IPCHECKING_API_KEY=""
CLOUDFLARE_API=""
IPAPIIS_API_KEY=""
IPCHECKING_API_KEY=""
MAC_LOOKUP_API_KEY=""
# Security related
SECURITY_BLACKLIST_LOG_FILE_PATH=""
SECURITY_RATE_LIMIT=""
SECURITY_DELAY_AFTER=""
IPAPIIS_API_KEY=""
MAC_LOOKUP_API_KEY=""
# Google Analytics
VITE_GOOGLE_ANALYTICS_ID=""
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ You can use the program without adding any environment variables, but if you wan
| `KEYCDN_USER_AGENT` | No | `""` | The domain name when using KeyCDN, must contain https prefix. Used to obtain IP address information through KeyCDN |
| `CLOUDFLARE_API` | No | `""` | API Key for Cloudflare, used to obtain AS system information through Cloudflare |
| `MAC_LOOKUP_API_KEY` | No | `""` | API Key for MAC Lookup, used to obtain MAC address information |
| `VITE_GOOGLE_ANALYTICS_ID` | **Yes** | `""` | Google Analytics ID, used to track user behavior |

### Using Environment Variables in a Node Environment

Expand Down
1 change: 1 addition & 0 deletions README_FR.md
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ Vous pouvez utiliser le programme sans ajouter de variables d'environnement, mai
| `KEYCDN_USER_AGENT` | Non | `""` | Le nom de domaine lorsque vous utilisez KeyCDN, doit contenir le préfixe https. Utilisé pour obtenir des informations sur l'adresse IP via KeyCDN |
| `CLOUDFLARE_API` | Non | `""` | Clé API pour Cloudflare, utilisée pour obtenir des informations sur le système AS via Cloudflare |
| `MAC_LOOKUP_API_KEY` | Non | `""` | Clé API pour MAC Lookup, utilisée pour obtenir des informations sur l'adresse MAC via MAC Lookup |
| `VITE_GOOGLE_ANALYTICS_ID` | **Oui** | `""` | Identifiant Google Analytics, utilisé pour l'analyse des utilisateurs |

### Utilisation des variables d'environnement dans un environnement Node

Expand Down
1 change: 1 addition & 0 deletions README_ZH.md
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ docker run -d -p 18966:18966 --name myip --restart always jason5ng32/myip:latest
| `KEYCDN_USER_AGENT` || `""` | 使用 KeyCDN 时的域名,需包含 https 前缀。用于通过 KeyCDN 获取 IP 归属地信息 |
| `CLOUDFLARE_API` || `""` | Cloudflare 的 API Key,用于通过 Cloudflare 获取 AS 系统的信息 |
| `MAC_LOOKUP_API_KEY` || `""` | MAC 查询的 API Key,用于通过 MAC Lookup 获取 MAC 地址的归属信息 |
| `VITE_GOOGLE_ANALYTICS_ID` | **** | `""` | Google Analytics 的 ID,用于统计访问量 |

### 在 Node 环境里使用环境变量

Expand Down
2 changes: 1 addition & 1 deletion api/configs.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export default (req, res) => {
}

const hostname = referer ? new URL(referer).hostname : '';
const originalSite = hostname === 'ipcheck.ing' || hostname === 'www.ipcheck.ing';
const originalSite = hostname === 'ipcheck.ing' || hostname === 'www.ipcheck.ing' || hostname === 'localtest.ipcheck.ing';

const envConfigs = {
bingMap: process.env.BING_MAP_API_KEY,
Expand Down
18 changes: 9 additions & 9 deletions api/macchecker.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,15 +64,15 @@ const modifyData = (data) => {
data.isLocal = isLocal ? true : false;
data.isGlobal = !isLocal ? true : false;
data.isUnicast = !isMulticast ? true : false;
data.macPrefix = data.macPrefix? data.macPrefix : 'N/A';
data.company = data.company? data.company : 'N/A';
data.country = data.country? data.country : 'N/A';
data.address = data.address? data.address : 'N/A';
data.updated = data.updated? data.updated : 'N/A';
data.blockStart = data.blockStart? data.blockStart : 'N/A';
data.blockEnd = data.blockEnd? data.blockEnd : 'N/A';
data.blockSize = data.blockSize? data.blockSize : 'N/A';
data.blockType = data.blockType? data.blockType : 'N/A';
data.macPrefix = data.macPrefix ? data.macPrefix.match(/.{1,2}/g).join(':') : 'N/A';
data.company = data.company ? data.company : 'N/A';
data.country = data.country ? data.country : 'N/A';
data.address = data.address ? data.address : 'N/A';
data.updated = data.updated ? data.updated : 'N/A';
data.blockStart = data.blockStart ? data.blockStart.match(/.{1,2}/g).join(':') : 'N/A';
data.blockEnd = data.blockEnd ? data.blockEnd.match(/.{1,2}/g).join(':') : 'N/A';
data.blockSize = data.blockSize ? data.blockSize : 'N/A';
data.blockType = data.blockType ? data.blockType : 'N/A';

return data;
}
61 changes: 61 additions & 0 deletions api/maxmind.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import maxmind from 'maxmind';
import { isValidIP } from '../common/valid-ip.js';
import { refererCheck } from '../common/referer-check.js';

let cityLookup, asnLookup;

// 异步初始化数据库
async function initDatabases() {
cityLookup = await maxmind.open('./common/maxmind-db/GeoLite2-City.mmdb');
asnLookup = await maxmind.open('./common/maxmind-db/GeoLite2-ASN.mmdb');
}

initDatabases();

export default (req, res) => {

// 限制只能从指定域名访问
const referer = req.headers.referer;
if (!refererCheck(referer)) {
return res.status(403).json({ error: referer ? 'Access denied' : 'What are you doing?' });
}

const ip = req.query.ip;
if (!ip) {
return res.status(400).json({ error: 'No IP address provided' });
}

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

// 获取请求语言
const lang = req.query.lang === 'zh-CN' || req.query.lang === 'en' || req.query.lang === 'fr' ? req.query.lang : 'en';

try {
const city = cityLookup.get(ip);
const asn = asnLookup.get(ip);
let result = modifyJson(ip, lang, city, asn);
res.json(result);
} catch (e) {
res.status(500).json({ error: e.message });
}
}

function modifyJson(ip, lang, city, asn) {
city = city || {};
asn = asn || {};
return {
ip,
city: city.city ? city.city.names[lang] || city.city.names.en : "N/A",
region: city.subdivisions ? city.subdivisions[0].names[lang] || city.subdivisions[0].names.en : "N/A",
country: city.country ? city.country.iso_code : "N/A",
country_name: city.country ? city.country.names[lang] : "N/A",
country_code: city.country ? city.country.iso_code : "N/A",
latitude: city.location ? city.location.latitude : "N/A",
longitude: city.location ? city.location.longitude : "N/A",
asn: asn.autonomous_system_number ? "AS" + asn.autonomous_system_number : "N/A",
org: asn.autonomous_system_organization ? asn.autonomous_system_organization : "N/A"
};
};
2 changes: 2 additions & 0 deletions backend-server.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import whois from './api/whois.js';
import ipapiisHandler from './api/ipapiis.js';
import invisibilitytestHandler from './api/invisibilitytest.js';
import macChecker from './api/macchecker.js';
import maxmindHandler from './api/maxmind.js';

dotenv.config();

Expand Down Expand Up @@ -137,6 +138,7 @@ app.get('/api/whois', whois);
app.get('/api/ipapiis', ipapiisHandler);
app.get('/api/invisibility', invisibilitytestHandler);
app.get('/api/macchecker', macChecker);
app.get('/api/maxmind', maxmindHandler);

// 使用查询参数处理所有配置请求
app.get('/api/configs', validateConfigs);
Expand Down
Binary file added common/maxmind-db/GeoLite2-ASN.mmdb
Binary file not shown.
Binary file added common/maxmind-db/GeoLite2-City.mmdb
Binary file not shown.
10 changes: 7 additions & 3 deletions frontend/components/SpeedTest.vue
Original file line number Diff line number Diff line change
Expand Up @@ -86,15 +86,15 @@
v-if="speedTestStatus === 'finished' && hasScores">
<p id="score" class="speedtest-p"><i class="bi bi-calendar2-check"></i> {{ t('speedtest.score') }}
{{ t('speedtest.videoStreaming') }}
<span :class="speedTest.streamingScore >= 50 ? 'text-success' : 'text-warning'">
<span :class="speedTest.streamingScore >= 50 ? 'text-success' : 'jn-text-warning'">
{{ speedTest.streamingScore }}
</span>
{{ t('speedtest.gaming') }}
<span :class="speedTest.gamingScore >= 50 ? 'text-success' : 'text-warning'">
<span :class="speedTest.gamingScore >= 50 ? 'text-success' : 'jn-text-warning'">
{{ speedTest.gamingScore }}
</span>
{{ t('speedtest.rtc') }}
<span :class="speedTest.rtcScore >= 50 ? 'text-success' : 'text-warning'">
<span :class="speedTest.rtcScore >= 50 ? 'text-success' : 'jn-text-warning'">
{{ speedTest.rtcScore }}
</span>
{{ t('speedtest.resultNote') }}
Expand Down Expand Up @@ -365,4 +365,8 @@ defineExpose({
background-color: var(--bs-btn-hover-bg);
border-color: var(--bs-btn-hover-border-color);
}
.jn-text-warning {
--bs-text-opacity: 1;
color: #c67c14;
}
</style>
9 changes: 5 additions & 4 deletions frontend/components/advanced-tools/MacChecker.vue
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@

<div id="macCheckResult" class="row" v-if="macCheckResult.success">
<div class="col-lg-8 col-md-8 col-12 mb-4">
<div class="card jn-card" :class="{ 'dark-mode dark-mode-border': isDarkMode }">
<div class="card h-100" :class="{ 'dark-mode dark-mode-border': isDarkMode }">
<div class="card-body row">
<h3 class="mb-4">{{ t('macchecker.manufacturer') }}</h3>
<div class="col-lg-6 col-md-6 col-12">
Expand Down Expand Up @@ -84,7 +84,7 @@
</div>

<div class="col-lg-4 col-md-4 col-12 mb-4">
<div class="card jn-card" :class="{ 'dark-mode dark-mode-border': isDarkMode}">
<div class="card h-100" :class="{ 'dark-mode dark-mode-border': isDarkMode}">
<div class="card-body">
<h3 class="mb-4">{{ t('macchecker.property') }}</h3>
<div class="table-responsive text-nowrap">
Expand Down Expand Up @@ -162,8 +162,9 @@ const tableItems = computed(() => {
// 检查 MAC 是否有效
const validateInput = (input) => {
if (!input) return null;
// 清理所有的分隔符
const normalizedInput = input.replace(/[:-]/g, '');
// 清理所有的分隔符和空格
const normalizedInput = input.replace(/[:-]/g, '')
.replace(/\s+/g, '');
// 检查长度和格式
if (normalizedInput.length < 6 || normalizedInput.length > 12 || !/^[0-9A-Fa-f]+$/.test(normalizedInput)) {
errorMsg.value = t('macchecker.invalidMAC');
Expand Down
1 change: 1 addition & 0 deletions frontend/store.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ export const useMainStore = defineStore('main', {
{ id: 4, text: 'KeyCDN', url: '/api/keycdn?ip={{ip}}', enabled: true },
{ id: 5, text: 'IP.SB', url: '/api/ipsb?ip={{ip}}', enabled: true },
{ id: 6, text: 'IPAPI.is', url: '/api/ipapiis?ip={{ip}}', enabled: true },
{ id: 7, text: 'MaxMind', url: '/api/maxmind?ip={{ip}}&lang={{lang}}', enabled: true },
],
}),

Expand Down
4 changes: 3 additions & 1 deletion frontend/utils/use-analytics.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import Analytics from 'analytics';
import googleAnalytics from '@analytics/google-analytics';

const analyticsID = import.meta.env.VITE_GOOGLE_ANALYTICS_ID || '';

// Google Analytics 配置
const analytics = Analytics({
app: 'MyIP',
plugins: [
googleAnalytics({
measurementIds: ['G-TEYKKD81TL'],
measurementIds: [analyticsID],
})
]
});
Expand Down
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,17 +27,18 @@
"express-slow-down": "^2.0.3",
"flag-icons": "^7.2.3",
"http-proxy-middleware": "^3.0.0",
"maxmind": "^4.3.20",
"nodemon": "^3.1.4",
"pinia": "^2.1.7",
"svgmap": "^2.10.1",
"vue": "^3.4.29",
"vue": "^3.4.31",
"vue-i18n": "^9.13.1",
"vue-router": "^4.4.0",
"whoiser": "^1.17.3"
},
"devDependencies": {
"@vitejs/plugin-vue": "^5.0.5",
"vite": "^5.3.1",
"vite": "^5.3.3",
"vite-plugin-pwa": "^0.20.0"
}
}

0 comments on commit 70ee698

Please sign in to comment.