Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improvements #246

Merged
merged 3 commits into from
Oct 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file modified common/maxmind-db/GeoLite2-ASN.mmdb
Binary file not shown.
Binary file modified common/maxmind-db/GeoLite2-City.mmdb
Binary file not shown.
45 changes: 34 additions & 11 deletions frontend/components/DnsLeaksTest.vue
Original file line number Diff line number Diff line change
Expand Up @@ -30,15 +30,25 @@
leak.ip }}
</p>

<div class="alert" :class="{
<div class="alert d-flex flex-column" :class="{
'alert-info': leak.country === t('dnsleaktest.StatusWait'),
'alert-success': leak.country !== t('dnsleaktest.StatusWait'),
}" :data-bs-theme="isDarkMode ? 'dark' : ''">
<i class="bi"
:class="[leak.ip === t('dnsleaktest.StatusWait') || leak.ip === t('dnsleaktest.StatusError') ? 'bi-hourglass-split' : 'bi-geo-alt-fill']"></i>
{{ t('dnsleaktest.EndpointCountry') }}: <strong>{{ leak.country }}&nbsp;</strong>
<span v-if="leak.country !== t('dnsleaktest.StatusWait') && leak.country !== t('dnsleaktest.StatusError')"
:class="'jn-fl fi fi-' + leak.country_code.toLowerCase()"></span>

<span class="jn-org">
<i class="bi"
:class="[leak.org === t('dnsleaktest.StatusWait') || leak.org === t('dnsleaktest.StatusError') ? 'bi-hourglass-split' : 'bi-geo-alt-fill']"></i>
{{ t('ipInfos.ISP') }}: <span :title="leak.org">{{ leak.org }}</span>
</span>

<span class="mt-2">
<i class="bi"
:class="[leak.ip === t('dnsleaktest.StatusWait') || leak.ip === t('dnsleaktest.StatusError') ? 'bi-hourglass-split' : 'bi-geo-alt-fill']"></i>
{{ t('ipInfos.Country') }}: <span
:class="[ leak.country !== t('dnsleaktest.StatusWait') ? 'fw-bold':'']">{{ leak.country
}}&nbsp;</span>
<span v-show="leak.country_code" :class="'jn-fl fi fi-' + leak.country_code.toLowerCase()"></span>
</span>
</div>
</div>
</div>
Expand Down Expand Up @@ -67,9 +77,10 @@ const lang = computed(() => store.lang);

const createDefaultCard = () => ({
name: t('dnsleaktest.Name'),
country_code: t('dnsleaktest.StatusWait'),
country_code: '',
country: t('dnsleaktest.StatusWait'),
ip: t('dnsleaktest.StatusWait'),
org: t('dnsleaktest.StatusWait'),
});

const leakTest = reactive([
Expand Down Expand Up @@ -116,6 +127,7 @@ const fetchLeakTestIpApiCom = (index) => {
const geoSplit = data.dns.geo.split(" - ");
leakTest[index].country_code = countryLookup.byCountry(geoSplit[0]).iso2;
leakTest[index].country = getCountryName(leakTest[index].country_code, lang.value);
leakTest[index].org = geoSplit[1] || '';
leakTest[index].ip = data.dns.ip;
resolve();
} else {
Expand All @@ -126,8 +138,9 @@ const fetchLeakTestIpApiCom = (index) => {
.catch((error) => {
console.error("Error fetching leak test data:", error);
leakTest[index].country = t('dnsleaktest.StatusError');
leakTest[index].country_code = t('dnsleaktest.StatusError');
leakTest[index].ip = t('dnsleaktest.StatusError');
leakTest[index].country_code = '';
leakTest[index].org = '';
reject(error);
});
});
Expand All @@ -153,6 +166,7 @@ const fetchLeakTestSfSharkCom = (index, key) => {
if (keyEntry && keyEntry.CountryCode && keyEntry.IP) {
leakTest[index].country_code = keyEntry.CountryCode;
leakTest[index].country = getCountryName(keyEntry.CountryCode, lang.value);
leakTest[index].org = keyEntry.ISP || '';
leakTest[index].ip = keyEntry.IP;
resolve();
} else {
Expand All @@ -162,8 +176,10 @@ const fetchLeakTestSfSharkCom = (index, key) => {
})
.catch((error) => {
console.error("Error fetching leak test data:", error);
leakTest[index].geo = t('dnsleaktest.StatusError');
leakTest[index].ip = t('dnsleaktest.StatusError');
leakTest[index].country = t('dnsleaktest.StatusError');
leakTest[index].country_code = '';
leakTest[index].org = '';
reject(error);
});
});
Expand All @@ -178,7 +194,8 @@ const checkAllDNSLeakTest = async (isRefresh) => {
server.geo = t('dnsleaktest.StatusWait');
server.ip = t('dnsleaktest.StatusWait');
server.country = t('dnsleaktest.StatusWait');
server.country_code = t('dnsleaktest.StatusWait');
server.country_code = '';
server.org = t('dnsleaktest.StatusWait');
});
}

Expand Down Expand Up @@ -220,4 +237,10 @@ defineExpose({
});
</script>

<style scoped></style>
<style scoped>
.jn-org {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
</style>
81 changes: 73 additions & 8 deletions frontend/components/WebRtcTest.vue
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,30 @@
}">
<i class="bi"
:class="[stun.ip === t('webrtc.StatusWait') ? 'bi-hourglass-split' : 'bi-pc-display-horizontal']">&nbsp;</i>
<span :class="{ 'jn-ip-font': stun.ip.length > 32 }"> {{ stun.ip }}</span>
<span :class="{ 'jn-ip-font': stun.ip.length > 32 }"> {{ stun.ip }}
</span>

</p>
<div v-if="stun.natType" class="alert" :class="{
<div v-if="stun.natType" class="alert d-flex flex-column" :class="{
'alert-info': stun.natType === t('webrtc.StatusWait'),
'alert-success': stun.natType !== t('webrtc.StatusWait'),
}" :data-bs-theme="isDarkMode ? 'dark' : ''">
<i class="bi"
:class="[stun.natType === t('webrtc.StatusWait') ? 'bi-hourglass-split' : ' bi-controller']"></i> {{
stun.natType }}
<span>
<i class="bi"
:class="[stun.natType === t('webrtc.StatusWait') ? 'bi-hourglass-split' : ' bi-controller']"></i> NAT:
{{
stun.natType }}
</span>

<span class="mt-2">
<i class="bi"
:class="[stun.country === t('webrtc.StatusWait') || stun.country === t('webrtc.StatusError') ? 'bi-hourglass-split' : 'bi-geo-alt-fill']"></i>
{{ t('ipInfos.Country') }}: <span :class="[ stun.country !== t('webrtc.StatusWait') ? 'fw-bold':'']">{{
stun.country }}&nbsp;</span>
<span v-show="stun.country_code" :class="'jn-fl fi fi-' + stun.country_code"></span>
</span>


</div>
</div>
</div>
Expand All @@ -48,12 +63,15 @@ import { ref, computed, onMounted, reactive, watch } from 'vue';
import { useMainStore } from '@/store';
import { useI18n } from 'vue-i18n';
import { trackEvent } from '@/utils/use-analytics';
import { transformDataFromIPapi } from '@/utils/transform-ip-data.js';
import getCountryName from '@/utils/country-name.js';

const { t } = useI18n();

const store = useMainStore();
const isDarkMode = computed(() => store.isDarkMode);
const isMobile = computed(() => store.isMobile);
const lang = computed(() => store.lang);


const isStarted = ref(false);
Expand All @@ -65,27 +83,35 @@ const stunServers = reactive([
url: "stun.l.google.com:19302",
ip: t('webrtc.StatusWait'),
natType: t('webrtc.StatusWait'),
country: t('webrtc.StatusWait'),
country_code: '',
},
{
id: "blackberry",
name: "BlackBerry",
url: "stun.voip.blackberry.com:3478",
ip: t('webrtc.StatusWait'),
natType: t('webrtc.StatusWait'),
country: t('webrtc.StatusWait'),
country_code: '',
},
{
id: "twilio",
name: "Twilio",
url: "global.stun.twilio.com",
ip: t('webrtc.StatusWait'),
natType: t('webrtc.StatusWait'),
country: t('webrtc.StatusWait'),
country_code: '',
},
{
id: "cloudflare",
name: "Cloudflare",
url: "stun.cloudflare.com",
ip: t('webrtc.StatusWait'),
natType: t('webrtc.StatusWait'),
country: t('webrtc.StatusWait'),
country_code: '',
},
]);

Expand All @@ -98,17 +124,28 @@ const checkSTUNServer = async (stun) => {
const pc = new RTCPeerConnection(servers);
let candidateReceived = false;

pc.onicecandidate = (event) => {
// 分别获取 STUN 服务器的 IP 地址和 NAT 类型
pc.onicecandidate = async (event) => {
if (event.candidate) {
candidateReceived = true;
const candidate = event.candidate.candidate;
const ipMatch = /([0-9a-f]{1,4}(:[0-9a-f]{1,4}){7}|[0-9a-f]{0,4}(:[0-9a-f]{1,4}){0,6}::[0-9a-f]{0,4}|::[0-9a-f]{1,4}(:[0-9a-f]{1,4}){0,6}|[0-9]{1,3}(\.[0-9]{1,3}){3})/i.exec(candidate);
if (ipMatch) {
stun.ip = ipMatch[0];
try {
let countryInfo = await fetchCountryCode(stun.ip);
stun.country_code = countryInfo[0];
stun.country = countryInfo[1];
} catch (error) {
console.error("Error fetching country code:", error);
reject(error);
pc.close();
return;
}
IPArray.value = [...IPArray.value, stun.ip];
stun.natType = determineNATType(candidate);
pc.close();
resolve(); // 成功时解析 Promise
resolve();
}
}
};
Expand All @@ -126,11 +163,12 @@ const checkSTUNServer = async (stun) => {
} catch (error) {
console.error("STUN Server Test Error:", error);
stun.ip = t('webrtc.StatusError');
reject(error); // 捕获异常时拒绝 Promise
reject(error);
}
});
};


// 分析ICE候选信息,推断NAT类型
const determineNATType = (candidate) => {
const parts = candidate.split(' ');
Expand All @@ -149,6 +187,31 @@ const determineNATType = (candidate) => {
}
};

// 通过 Maxmind 获取 IP 地区归属
const fetchCountryCode = async (ip) => {
let setLang = lang.value;
if (setLang === 'zh') {
setLang = 'zh-CN';
}
const source = store.ipDBs.find(source => source.text === "MaxMind");

try {
const url = store.getDbUrl(source.id, ip, setLang);
const response = await fetch(url);
const data = await response.json();
const ipData = transformDataFromIPapi(data, source.id, t, lang.value);

if (ipData) {
let country_code = ipData.country_code.toLowerCase();
let country = getCountryName(ipData.country_code, lang.value);
return [country_code, country];
}
} catch (error) {
console.error("Error fetching IP country code", error);
}
}


// 测试所有 STUN 服务器
const checkAllWebRTC = async (isRefresh) => {
if (isRefresh) {
Expand All @@ -158,6 +221,8 @@ const checkAllWebRTC = async (isRefresh) => {
const promises = stunServers.map((server) => {
server.ip = t('webrtc.StatusWait');
server.natType = t('webrtc.StatusWait');
server.country = t('webrtc.StatusWait');
server.country_code = '';
return checkSTUNServer(server);
});

Expand Down
10 changes: 5 additions & 5 deletions frontend/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -339,11 +339,11 @@
"StatusWait": "Awaiting Test or Connection Error",
"StatusError": "Test Error",
"NATType": {
"srflx": "Possibly Port Restricted Cone or Symmetric NAT",
"prflx": "Possibly Port Restricted Cone NAT",
"relay": "Possibly Symmetric NAT",
"host": "Possibly Full Cone NAT",
"unknown": "Unknown NAT Type"
"srflx": "Port Restricted Cone or Symmetric",
"prflx": "Port Restricted Cone",
"relay": "Symmetric",
"host": "Full Cone",
"unknown": "Unknown Type"
}
},
"dnsleaktest": {
Expand Down
10 changes: 5 additions & 5 deletions frontend/locales/fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -339,11 +339,11 @@
"StatusWait": "En attente du test ou erreur de connexion",
"StatusError": "Erreur de test",
"NATType": {
"srflx": "Possiblement Port Restricted Cone ou Symmetric NAT",
"prflx": "Possiblement Port Restricted Cone NAT",
"relay": "Possiblement Symmetric NAT",
"host": "Possiblement Full Cone NAT",
"unknown": "Type de NAT inconnu"
"srflx": "Port Restricted Cone ou Symmetric",
"prflx": "Port Restricted Cone",
"relay": "Symmetric",
"host": "Full Cone",
"unknown": "Type de inconnu"
}
},
"dnsleaktest": {
Expand Down
10 changes: 5 additions & 5 deletions frontend/locales/zh.json
Original file line number Diff line number Diff line change
Expand Up @@ -339,11 +339,11 @@
"StatusWait": "待检测或连接错误",
"StatusError": "测试出错",
"NATType": {
"srflx": "可能是端口限制型或对称型 NAT",
"prflx": "可能是端口限制型 NAT",
"relay": "可能是对称型 NAT",
"host": "可能是全锥型 NAT",
"unknown": "未知 NAT 类型"
"srflx": "端口限制型或对称型",
"prflx": "端口限制型",
"relay": "对称型",
"host": "全锥型",
"unknown": "未知类型"
}
},
"dnsleaktest": {
Expand Down
14 changes: 7 additions & 7 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,20 +22,20 @@
"code-inspector-plugin": "^0.16.1",
"concurrently": "^9.0.1",
"country-code-lookup": "^0.1.3",
"detect-gpu": "^5.0.49",
"detect-gpu": "^5.0.51",
"dotenv": "^16.4.5",
"express": "^4.21.0",
"express-rate-limit": "^7.4.0",
"express-rate-limit": "^7.4.1",
"express-slow-down": "^2.0.3",
"flag-icons": "^7.2.3",
"http-proxy-middleware": "^3.0.2",
"maxmind": "^4.3.21",
"http-proxy-middleware": "^3.0.3",
"maxmind": "^4.3.22",
"nodemon": "^3.1.7",
"pinia": "^2.2.2",
"pinia": "^2.2.4",
"svgmap": "^2.11.1",
"ua-parser-js": "^1.0.39",
"vue": "^3.5.8",
"vue-i18n": "^10.0.3",
"vue": "^3.5.11",
"vue-i18n": "^10.0.4",
"vue-router": "^4.4.5",
"whoiser": "^1.18.0"
},
Expand Down