No files matched your search
@@ -19,7 +19,7 @@
|
||||

|
||||
[](https://github.com/jason5ng32/MyIP/actions/workflows/ci.yml)
|
||||
|
||||
[English](README.md) | [简体中文](README_ZH.md) | [繁體中文](README_ZH-TW.md) | [Русский](README_RU.md) | [Français](README_FR.md) | [Português (BR)](README_PT-BR.md) | [Shqip](README_SQ.md)
|
||||
[English](README.md) | [简体中文](README_ZH.md) | [繁體中文](README_ZH-TW.md) | [Русский](README_RU.md) | [Français](README_FR.md) | [Português (BR)](README_PT-BR.md) | [Shqip](README_SQ.md) | [ไทย](README_TH.md)
|
||||
|
||||
An open-source, all-in-one IP toolbox: IP lookup from multiple sources, connectivity tests, WebRTC & DNS-leak detection, speed test, MTR, censorship checks, Whois, and more — self-hostable with one Docker command.
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||

|
||||
[](https://github.com/jason5ng32/MyIP/actions/workflows/ci.yml)
|
||||
|
||||
[English](README.md) | [简体中文](README_ZH.md) | [繁體中文](README_ZH-TW.md) | [Русский](README_RU.md) | [Français](README_FR.md) | [Português (BR)](README_PT-BR.md) | [Shqip](README_SQ.md)
|
||||
[English](README.md) | [简体中文](README_ZH.md) | [繁體中文](README_ZH-TW.md) | [Русский](README_RU.md) | [Français](README_FR.md) | [Português (BR)](README_PT-BR.md) | [Shqip](README_SQ.md) | [ไทย](README_TH.md)
|
||||
|
||||
Une boîte à outils IP tout-en-un et open source : recherche d'IP depuis plusieurs sources, tests de connectivité, détection WebRTC et de fuites DNS, test de vitesse, MTR, vérification de la censure, Whois et plus encore — auto-hébergeable en une seule commande Docker.
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||

|
||||
[](https://github.com/jason5ng32/MyIP/actions/workflows/ci.yml)
|
||||
|
||||
[English](README.md) | [简体中文](README_ZH.md) | [繁體中文](README_ZH-TW.md) | [Русский](README_RU.md) | [Français](README_FR.md) | [Português (BR)](README_PT-BR.md) | [Shqip](README_SQ.md)
|
||||
[English](README.md) | [简体中文](README_ZH.md) | [繁體中文](README_ZH-TW.md) | [Русский](README_RU.md) | [Français](README_FR.md) | [Português (BR)](README_PT-BR.md) | [Shqip](README_SQ.md) | [ไทย](README_TH.md)
|
||||
|
||||
Uma caixa de ferramentas de IP completa e de código aberto: consulta de IP a partir de múltiplas fontes, testes de conectividade, detecção de WebRTC e de vazamento de DNS, teste de velocidade, MTR, verificação de censura, Whois e muito mais — auto-hospedável com um único comando Docker.
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||

|
||||
[](https://github.com/jason5ng32/MyIP/actions/workflows/ci.yml)
|
||||
|
||||
[English](README.md) | [简体中文](README_ZH.md) | [繁體中文](README_ZH-TW.md) | [Русский](README_RU.md) | [Français](README_FR.md) | [Português (BR)](README_PT-BR.md) | [Shqip](README_SQ.md)
|
||||
[English](README.md) | [简体中文](README_ZH.md) | [繁體中文](README_ZH-TW.md) | [Русский](README_RU.md) | [Français](README_FR.md) | [Português (BR)](README_PT-BR.md) | [Shqip](README_SQ.md) | [ไทย](README_TH.md)
|
||||
|
||||
Универсальный набор IP-инструментов с открытым исходным кодом: определение IP по нескольким источникам, проверка доступности сайтов, обнаружение утечек WebRTC и DNS, тест скорости, MTR, проверка цензуры, Whois и многое другое — разворачивается на своём сервере одной командой Docker.
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||

|
||||
[](https://github.com/jason5ng32/MyIP/actions/workflows/ci.yml)
|
||||
|
||||
[English](README.md) | [简体中文](README_ZH.md) | [繁體中文](README_ZH-TW.md) | [Русский](README_RU.md) | [Français](README_FR.md) | [Português (BR)](README_PT-BR.md) | [Shqip](README_SQ.md)
|
||||
[English](README.md) | [简体中文](README_ZH.md) | [繁體中文](README_ZH-TW.md) | [Русский](README_RU.md) | [Français](README_FR.md) | [Português (BR)](README_PT-BR.md) | [Shqip](README_SQ.md) | [ไทย](README_TH.md)
|
||||
|
||||
Një kuti mjetesh IP gjithëpërfshirëse dhe me burim të hapur: kërkim IP nga shumë burime, teste lidhshmërie, zbulim i rrjedhjeve WebRTC dhe DNS, test shpejtësie, MTR, kontrolle censure, Whois e shumë të tjera — e vetë-strehueshme me një komandë të vetme Docker.
|
||||
|
||||
|
||||
@@ -0,0 +1,216 @@
|
||||
# 🧰 MyIP - กล่องเครื่องมือ IP ที่ดีกว่าเดิม
|
||||
|
||||
> [!NOTE]
|
||||
> นี่คือคำแปลที่ดูแลโดยชุมชน README ภาษาอังกฤษคือต้นฉบับหลัก ฉบับนี้อาจอัปเดตไม่ทันต้นฉบับ
|
||||
|
||||
<div align="center">
|
||||
|
||||

|
||||
|
||||
<a href="https://trendshift.io/repositories/5332" target="_blank"><img src="https://trendshift.io/api/badge/repositories/5332" alt="jason5ng32%2FMyIP | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a>
|
||||
|
||||
[](https://github.com/awesome-selfhosted/awesome-selfhosted)
|
||||
|
||||

|
||||

|
||||

|
||||
|
||||
[](https://ipcheck.ing)
|
||||

|
||||
|
||||

|
||||

|
||||
[](https://github.com/jason5ng32/MyIP/actions/workflows/ci.yml)
|
||||
|
||||
[English](README.md) | [简体中文](README_ZH.md) | [繁體中文](README_ZH-TW.md) | [Русский](README_RU.md) | [Français](README_FR.md) | [Português (BR)](README_PT-BR.md) | [Shqip](README_SQ.md) | [ไทย](README_TH.md)
|
||||
|
||||
กล่องเครื่องมือ IP แบบโอเพนซอร์สที่รวมทุกอย่างไว้ในที่เดียว: ตรวจ IP จากหลายแหล่ง, ทดสอบการเชื่อมต่อ, ตรวจจับ WebRTC และ DNS รั่ว, ทดสอบความเร็ว, MTR, ตรวจการเซ็นเซอร์, Whois และอีกมากมาย ติดตั้งบนเซิร์ฟเวอร์ของคุณเองได้ด้วยคำสั่ง Docker เพียงคำสั่งเดียว
|
||||
|
||||
👉 เดโม: [https://ipcheck.ing](https://ipcheck.ing)
|
||||
|
||||
จะบุ๊กมาร์กเว็บเดโมไว้ใช้ หรือติดตั้งเองก็ได้ตามสะดวก
|
||||
|
||||
[](https://hub.docker.com/r/jason5ng32/myip)
|
||||
|
||||
</div>
|
||||
|
||||
## 👀 ฟีเจอร์
|
||||
|
||||
### 🪪 IP และตัวตนของคุณ
|
||||
|
||||
* 🛜 **IP Cards**: ตรวจ IPv4 และ IPv6 ของคุณจากหลายแหล่งที่เป็นอิสระต่อกันแล้วแสดงเทียบกัน พร้อมประเทศ ภูมิภาค เมือง ASN องค์กร และเขตเวลาท้องถิ่นของ IP นั้น
|
||||
* 🔍 **ค้นหา IP**: ดูข้อมูลละเอียดแบบเดียวกันของ IP ใดก็ได้ที่คุณอยากรู้
|
||||
* 🧾 **ประวัติ IP**: บันทึก IP ที่คุณเคยใช้ไว้ในเครื่อง กรองตามประเภทและประเทศได้ ข้อมูลเก็บไว้ในเบราว์เซอร์ของคุณเท่านั้น
|
||||
* 🖥️ **Browser Fingerprint**: คำนวณลายนิ้วมือเบราว์เซอร์ของคุณด้วยหลายวิธี และแสดงว่าอะไรบ้างที่ทำให้คุณถูกระบุตัวตนได้
|
||||
|
||||
### 🕵️ การรั่วไหลและความเป็นส่วนตัว
|
||||
|
||||
* 🚥 **ตรวจ WebRTC**: เผย IP ที่หลุดออกไประหว่างการเชื่อมต่อ WebRTC รวมถึงบอกว่าเบราว์เซอร์ของคุณเปิดการป้องกันความเป็นส่วนตัวไว้หรือไม่
|
||||
* 🛑 **ทดสอบ DNS รั่ว**: แสดงว่าเซิร์ฟเวอร์ DNS ตัวไหนบ้างที่ตอบคำขอของคุณ เพื่อประเมินความเสี่ยง DNS รั่วเวลาใช้ VPN หรือพร็อกซี
|
||||
* 🛡️ **ทดสอบการบล็อกโฆษณาและตัวติดตาม**: ตรวจว่าตัวบล็อกโฆษณา เบราว์เซอร์ ตัวกรอง DNS VPN หรือเราเตอร์ของคุณบล็อกอะไรได้จริง ทั้งเครือข่ายโฆษณา ระบบวิเคราะห์ ตัวติดตามโซเชียล ระบบมอนิเตอร์ข้อผิดพลาด การส่งข้อมูลกลับของผู้ผลิตอุปกรณ์ สคริปต์ติดตาม และการซ่อนองค์ประกอบบนหน้าเว็บ
|
||||
* 📋 **เช็กลิสต์ความปลอดภัย**: เช็กลิสต์ความปลอดภัยไซเบอร์ส่วนบุคคล 258 ข้อใน 12 หมวด บันทึกความคืบหน้าไว้ในเบราว์เซอร์
|
||||
|
||||
### 📡 ทดสอบเครือข่าย
|
||||
|
||||
* 🚦 **ตรวจการเชื่อมต่อ**: ทดสอบว่าเข้าถึงเว็บที่คุณเลือกได้หรือไม่ สูงสุด 60 เว็บ วัดหลายรอบแล้วเอาค่าหน่วงต่ำสุด มีรายการเว็บสำเร็จรูปให้นำเข้า ตั้งแต่ชุดประจำประเทศไปจนถึง AI โซเชียล สตรีมมิง เกม นักพัฒนา และอื่น ๆ แล้วสรุปจากผลว่าตอนนี้คุณเข้าถึงอินเทอร์เน็ตทั่วโลกได้หรือไม่
|
||||
* 🚀 **ทดสอบความเร็ว**: วัดความเร็วดาวน์โหลด อัปโหลด และค่าหน่วงกับเครือข่าย edge
|
||||
* ⏱️ **ทดสอบค่าหน่วงทั่วโลก**: ping เป้าหมายของคุณจากจุดวัดทั่วโลก เลือกประเทศได้จากจุดวัดของ Globalping ทั้งหมด จัดกลุ่มตามทวีป
|
||||
* 🚉 **ทดสอบ MTR**: รัน MTR จากจุดวัดที่กระจายอยู่ทั่วโลก เพื่อดูเส้นทางที่แพ็กเก็ตวิ่งจริง
|
||||
* 🚧 **ตรวจการเซ็นเซอร์**: แสดงว่าเว็บไซต์ถูกบล็อกที่ไหนบ้างทั่วโลก และถูกบล็อกด้วยวิธีใด
|
||||
* 🚏 **ทดสอบกฎพร็อกซี**: ตรวจว่ากฎในโปรแกรมพร็อกซีของคุณทำงานตามที่ตั้งใจไว้จริง
|
||||
|
||||
### 🔦 ค้นหาข้อมูลและโครงสร้างพื้นฐาน
|
||||
|
||||
* 📟 **DNS Resolver**: แปลงโดเมนผ่าน resolver หลายตัวพร้อมกัน จัดกลุ่มตามประเทศ ช่วยให้เห็นได้ง่ายว่า DNS ถูกดักแก้ (hijack) หรือถูกปนเปื้อนหรือไม่
|
||||
* 📓 **ค้นหา Whois**: ค้น Whois ของชื่อโดเมน IP และหมายเลข AS
|
||||
* 🗄️ **ค้นหา MAC**: ระบุผู้ผลิตและรายละเอียดของ MAC address
|
||||
* 🧮 **IP Calculator**: คำนวณ subnet แปลงรูปแบบการเขียน และดูรายละเอียด interface ของ IPv6 สำหรับ IP, prefix, ช่วง หรือรายการ IP ใดก็ได้ คำนวณในเครื่องทั้งหมด
|
||||
* 🛰️ **ข้อมูล ASN และโทโพโลยีต้นทาง**: อยู่บน IP Card ทุกใบ มีรายละเอียด AS ประวัติการประกาศ prefix ของ IP นั้น และเส้นทางต้นทางจาก ASN ไปจนถึงแกนหลัก Tier 1
|
||||
* 🛂 **โปรไฟล์ ASN**: ภาพรวมครบถ้วนของ AS ใดก็ได้ ทั้งข้อมูลการจดทะเบียนพร้อม Whois ดิบ อันดับและขนาดระดับโลก ปริมาณทราฟฟิกและคุณภาพการเชื่อมต่อ พื้นที่ใช้งาน IPv4 ทุก prefix ที่ประกาศพร้อมสถานะ RPKI ผู้ให้บริการต้นทาง peer และลูกค้า รวมถึงจุดแลกเปลี่ยนอินเทอร์เน็ตและดาต้าเซ็นเตอร์ที่ AS นั้นตั้งอยู่
|
||||
* 📶 **สถานะบริการ**: สถานะการใช้งานแบบเรียลไทม์ของบริการดัง ๆ เช่น Claude, OpenAI, GitHub, Cloudflare และอื่น ๆ ดึงจากหน้าสถานะทางการ พร้อมเหตุขัดข้องล่าสุด
|
||||
|
||||
### ✨ แพลตฟอร์ม
|
||||
|
||||
* 📤 **รายงานที่แชร์ได้**: เปลี่ยนผลทดสอบเป็นรายงานวินิจฉัย ในรูปแบบลิงก์อ่านอย่างเดียวที่หมดอายุเอง, Markdown ที่พร้อมส่งให้ AI หรือ JSON
|
||||
* ⌨️ **Curl API**: ดู IP ของคุณจาก terminal ด้วยคำสั่ง `curl` คำสั่งเดียว
|
||||
* 🌍 **Earth Online**: แผงที่รายงานเหตุอินเทอร์เน็ตล่มทั่วโลกแบบทันที
|
||||
* 🌗 **โหมดมืด**: เปลี่ยนตามระบบอัตโนมัติ และสลับเองได้
|
||||
* 📲 **PWA**: ติดตั้งเป็นแอปบนมือถือ และเป็นแอป Chrome บนเดสก์ท็อปได้
|
||||
* ⚡ **คีย์ลัด**: ทุกฟังก์ชันมีคีย์ลัด กด `?` เพื่อดูรายการทั้งหมด
|
||||
* 🔤 **หลายภาษา**: หน้าเว็บมีให้ 6 ภาษา และเพิ่มภาษาของคุณได้ด้วยไฟล์ภาษาเพียงชุดเดียว
|
||||
|
||||
## 📕 วิธีใช้งาน
|
||||
|
||||
### ใช้ Docker
|
||||
|
||||
คำสั่งเดียวก็พร้อมใช้งาน:
|
||||
|
||||
```bash
|
||||
docker run -d -p 18966:18966 --name myip --restart always jason5ng32/myip:latest
|
||||
```
|
||||
|
||||
หรือกดปุ่ม "Deploy with Docker" ที่ด้านบนของหน้านี้
|
||||
|
||||
### ติดตั้งในสภาพแวดล้อม Node
|
||||
|
||||
ตรวจให้แน่ใจว่าติดตั้ง Node.js แล้ว จากนั้น clone โค้ด:
|
||||
|
||||
```bash
|
||||
git clone https://github.com/jason5ng32/MyIP.git
|
||||
```
|
||||
|
||||
ติดตั้งและ build โปรเจกต์นี้ใช้ pnpm ถ้ายังไม่มี ให้ติดตั้งก่อน (npm มากับ Node อยู่แล้ว คำสั่งนี้จึงใช้ได้เสมอ):
|
||||
|
||||
```bash
|
||||
npm install -g pnpm
|
||||
pnpm install && pnpm run build
|
||||
```
|
||||
|
||||
รัน:
|
||||
|
||||
```bash
|
||||
pnpm start
|
||||
```
|
||||
|
||||
โปรแกรมจะทำงานที่พอร์ต 18966
|
||||
|
||||
## ⚙️ การตั้งค่า
|
||||
|
||||
> [!IMPORTANT]
|
||||
> **ต้องมีข้อมูลบัญชี MaxMind GeoLite2** ใช้สำหรับระบุตำแหน่ง IP และค้นหา ASN ถ้าไม่มี แหล่งข้อมูล MaxMind จะตอบกลับเป็น 503 สมัครได้ฟรี → [ตั้งค่า MaxMind](https://docs.ipcheck.ing/developer/getting-started/maxmind-setup)
|
||||
|
||||
> [!WARNING]
|
||||
> **ต้องตั้ง `ALLOWED_DOMAINS` เมื่อใช้กับโดเมนจริง** เป็นรายการชื่อโฮสต์ที่อนุญาตให้เรียก API หลังบ้าน ถ้าไม่ตั้ง ทุกคำขอจากโดเมนที่ไม่ใช่ localhost จะได้ 403 → [Reverse Proxy และโดเมน](https://docs.ipcheck.ing/developer/getting-started/reverse-proxy-and-domains)
|
||||
|
||||
```bash
|
||||
docker run -d -p 18966:18966 \
|
||||
-e MAXMIND_ACCOUNT_ID="YOUR_ACCOUNT_ID" \
|
||||
-e MAXMIND_LICENSE_KEY="YOUR_LICENSE_KEY" \
|
||||
-e DATASET_AUTO_UPDATE="true" \
|
||||
-e ALLOWED_DOMAINS="your-domain.com" \
|
||||
--name myip --restart always \
|
||||
jason5ng32/myip:latest
|
||||
```
|
||||
|
||||
การตั้งค่าอื่นทั้งหมด เช่น API key เสริม ความปลอดภัยและการจำกัดอัตราคำขอ การเก็บ log, Sentry และโดเมนสำหรับ curl API มีอธิบายไว้ใน[เอกสารอ้างอิงตัวแปรสภาพแวดล้อม](https://docs.ipcheck.ing/developer/reference/environment-variables)
|
||||
|
||||
## 📖 เอกสาร
|
||||
|
||||
คู่มือฉบับเต็มอยู่ที่ MyIP Docs Center: **[docs.ipcheck.ing](https://docs.ipcheck.ing)**
|
||||
|
||||
* [คู่มือนักพัฒนา](https://docs.ipcheck.ing/developer) — การติดตั้ง การตั้งค่า สถาปัตยกรรม และการร่วมพัฒนา
|
||||
* [ฐานความรู้](https://docs.ipcheck.ing/knowledge-base) — วิธีใช้เครื่องมือทุกตัว การวินิจฉัยเครือข่ายทีละขั้น และแนวคิดด้านเครือข่าย
|
||||
|
||||
## 🤝 ร่วมพัฒนา
|
||||
|
||||
ยินดีต้อนรับทุกการมีส่วนร่วม! เราคัดงานที่เหมาะกับมือใหม่ไว้ให้ แต่ละงานระบุไฟล์ที่ต้องแก้ เกณฑ์การรับงาน และมีเทสต์คอยนำทางจนกว่าจะ build ผ่าน:
|
||||
|
||||
* 🏷️ [Good first issues](https://github.com/jason5ng32/MyIP/labels/good%20first%20issue) — เพิ่ม DNS resolver จากประเทศของคุณ เพิ่มรายการเว็บ แปล README เป็นภาษาของคุณ ขัดเกลาคำแปล และอื่น ๆ
|
||||
* 🌐 [TRANSLATING.md](TRANSLATING.md) — แปลหน้าเว็บเป็นภาษาของคุณ ใช้ไฟล์ภาษาหนึ่งชุดกับการลงทะเบียนหนึ่งบรรทัด และ**แปลแค่บางส่วนก็ส่งเป็น PR แรกได้**
|
||||
* 📄 [CONTRIBUTING.md](CONTRIBUTING.md) — การเตรียมเครื่อง ข้อตกลงต่าง ๆ และขั้นตอนของ PR (ส่งเข้า branch `dev`)
|
||||
|
||||
## 👩🏻💻 การใช้งานขั้นสูง
|
||||
|
||||
<details>
|
||||
<summary>กฎพร็อกซีสำหรับดู IP จริงกับ IP ของพร็อกซีพร้อมกัน</summary>
|
||||
|
||||
ถ้าคุณใช้พร็อกซีในการเข้าอินเทอร์เน็ต ลองเพิ่มกฎนี้ในการตั้งค่าพร็อกซี (ปรับตามโปรแกรมที่คุณใช้) จะช่วยให้ดูได้ทั้ง IP จริงและ IP ตอนใช้พร็อกซี:
|
||||
|
||||
```ini
|
||||
# IP Testing
|
||||
IP-CIDR,1.0.0.2/32,Proxy,no-resolve
|
||||
IP-CIDR6,2606:4700:4700::1111/128,Proxy,no-resolve
|
||||
DOMAIN,4.ipcheck.ing,DIRECT
|
||||
DOMAIN,6.ipcheck.ing,DIRECT
|
||||
# Rule Testing
|
||||
DOMAIN,ptest-1.ipcheck.ing,Proxy1
|
||||
DOMAIN,ptest-2.ipcheck.ing,Proxy2
|
||||
DOMAIN,ptest-3.ipcheck.ing,Proxy3
|
||||
DOMAIN,ptest-4.ipcheck.ing,Proxy4
|
||||
DOMAIN,ptest-5.ipcheck.ing,Proxy5
|
||||
DOMAIN,ptest-6.ipcheck.ing,Proxy6
|
||||
DOMAIN,ptest-7.ipcheck.ing,Proxy7
|
||||
DOMAIN,ptest-8.ipcheck.ing,Proxy8
|
||||
DOMAIN,ptest-9.ipcheck.ing,Proxy9
|
||||
DOMAIN,ptest-10.ipcheck.ing,Proxy10
|
||||
DOMAIN,ptest-11.ipcheck.ing,Proxy11
|
||||
DOMAIN,ptest-12.ipcheck.ing,Proxy12
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
## 💖 ผู้สนับสนุน
|
||||
|
||||
ในฐานะโปรเจกต์โอเพนซอร์ส ขอขอบคุณผู้สนับสนุนต่อไปนี้เป็นอย่างยิ่ง:
|
||||
|
||||
**Sponsors**
|
||||
|
||||
<a href="https://www.rapidproxy.io/?ref=myip"><img src="https://res.ipcheck.ing/img/rapid_proxy_logo.png" alt="RapidProxy" height="48" /></a><br />
|
||||
<sub>↳ **<a href="https://www.rapidproxy.io/?ref=myip">RapidProxy</a>** — พร็อกซีแบบ residential มากกว่า 90 ล้าน IP หมุนเวียนอัจฉริยะ เลือกพื้นที่ได้แม่นยำ เริ่มต้น $0.55/GB ลด 10% ด้วยโค้ด `RAPID10`</sub>
|
||||
|
||||
<a href="https://www.swiftproxy.net/?ref=myip"><img src="https://res.ipcheck.ing/img/swiftproxy_logo.png" alt="SwiftProxy" height="48" /></a><br />
|
||||
<sub>↳ **<a href="https://www.swiftproxy.net/?ref=myip">SwiftProxy</a>** — พร็อกซี residential คุณภาพสูงทั้งแบบหมุนเวียนและแบบคงที่ มี IP residential มากกว่า 90 ล้าน IP ในกว่า 220 พื้นที่ทั่วโลก รองรับ HTTP(S), SOCKS5, session แบบหมุนเวียนและแบบคงที่ แพ็กเกจ residential เริ่มต้นเพียง $0.7/GB ใช้โค้ด `PROXY90` รับส่วนลดเพิ่มอีก 10%
|
||||
</sub>
|
||||
|
||||
<a href="https://proxy-seller.com/?partner=4FEC1644D12240F4"><img src="https://res.ipcheck.ing/img/proxyseller_logo.png" alt="Proxy-Seller" height="48" /></a><br />
|
||||
<sub>↳ **<a href="https://proxy-seller.com/?partner=4FEC1644D12240F4">Proxy-Seller</a>** — ให้บริการพร็อกซีส่วนตัวแบบ residential, mobile (4G/5G/LTE), ISP และดาต้าเซ็นเตอร์ทั้ง IPv4/IPv6 รองรับ HTTP(S) และ SOCKS5 มี IP residential มากกว่า 47 ล้าน IP ในกว่า 220 พื้นที่ เลือกประเทศ/เมือง/ISP ได้แม่นยำ uptime 99.7% และซัพพอร์ตตลอด 24 ชั่วโมง ผู้ใช้ MyIP ใช้โค้ด `CHECKINGIP15` รับส่วนลด 15%
|
||||
</sub>
|
||||
|
||||
<a href="https://v.ps/?utm_source=ipcheck.ing&utm_medium=referral&utm_campaign=github_readme&utm_content=en"><img src="https://res.ipcheck.ing/img/vps_logo.png" alt="V.PS" height="48" /></a><br />
|
||||
<sub>↳ **<a href="https://v.ps/?utm_source=ipcheck.ing&utm_medium=referral&utm_campaign=github_readme&utm_content=en">V.PS</a>** — VPS แบบ KVM ประสิทธิภาพสูง มีหลายพื้นที่ทั่วโลก เชื่อมต่อเร็ว ให้ทั้ง IPv4 และ IPv6</sub>
|
||||
|
||||
----
|
||||
|
||||
**Supporters**
|
||||
|
||||
<p><a href="https://www.cloudflare.com/lp/project-alexandria/"><img src="https://res.ipcheck.ing/img/cloudflare_logo.png" alt="Cloudflare Project Alexandria" height="32" /></a></p>
|
||||
<p><a href="https://www.1password.com"><img src="https://res.ipcheck.ing/img/1password_logo.png" alt="1Password" height="32" /></a></p>
|
||||
<p><a href="https://www.greptile.com/"><img src="https://res.ipcheck.ing/img/greptile_logo.png" alt="Greptile" height="32" /></a></p>
|
||||
<p><a href="https://www.sentry.io"><img src="https://res.ipcheck.ing/img/sentry_logo.png" alt="Sentry" height="32" /></a></p>
|
||||
<p><a href="https://www.gitbook.com"><img src="https://res.ipcheck.ing/img/gitbook_logo.png" alt="GitBook" height="32" /></a></p>
|
||||
|
||||
สนใจสนับสนุน MyIP? [มาเป็นผู้สนับสนุน](https://github.com/sponsors/jason5ng32)
|
||||
|
||||
## 📄 สัญญาอนุญาต
|
||||
|
||||
[MIT](LICENSE) © Jason Ng
|
||||
@@ -22,7 +22,7 @@
|
||||

|
||||
[](https://github.com/jason5ng32/MyIP/actions/workflows/ci.yml)
|
||||
|
||||
[English](README.md) | [简体中文](README_ZH.md) | [繁體中文](README_ZH-TW.md) | [Русский](README_RU.md) | [Français](README_FR.md) | [Português (BR)](README_PT-BR.md) | [Shqip](README_SQ.md)
|
||||
[English](README.md) | [简体中文](README_ZH.md) | [繁體中文](README_ZH-TW.md) | [Русский](README_RU.md) | [Français](README_FR.md) | [Português (BR)](README_PT-BR.md) | [Shqip](README_SQ.md) | [ไทย](README_TH.md)
|
||||
|
||||
一個開源的多合一 IP 工具箱:多來源 IP 查詢、連通性測試、WebRTC 與 DNS 洩漏偵測、網速測試、MTR、審查封鎖檢查、Whois 等——一條 Docker 指令即可自行架設。
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||

|
||||
[](https://github.com/jason5ng32/MyIP/actions/workflows/ci.yml)
|
||||
|
||||
[English](README.md) | [简体中文](README_ZH.md) | [繁體中文](README_ZH-TW.md) | [Русский](README_RU.md) | [Français](README_FR.md) | [Português (BR)](README_PT-BR.md) | [Shqip](README_SQ.md)
|
||||
[English](README.md) | [简体中文](README_ZH.md) | [繁體中文](README_ZH-TW.md) | [Русский](README_RU.md) | [Français](README_FR.md) | [Português (BR)](README_PT-BR.md) | [Shqip](README_SQ.md) | [ไทย](README_TH.md)
|
||||
|
||||
一个开源的一站式 IP 工具箱:多来源 IP 查询、连通性测试、WebRTC 与 DNS 泄露检测、网速测试、MTR、封锁检测、Whois 等等 —— 一条 Docker 命令即可自行部署。
|
||||
|
||||
|
||||
@@ -126,6 +126,8 @@ behind `dns-resolver` (gated by `tests/dns-resolvers-data.test.js`).
|
||||
- `requireValidReportId()` — `/api/report/:id` route param (22-char base64url).
|
||||
|
||||
New param shape → new guard there, attached in `backend-server.js`.
|
||||
Rewrites go through `setQueryParam()` there: Express 5's `req.query` re-parses
|
||||
on every read, so a direct write is lost.
|
||||
|
||||
### Response enrichment lives in middleware too
|
||||
|
||||
|
||||
@@ -213,6 +213,10 @@ const PROVIDERS = [
|
||||
{ id: 'figma', name: 'Figma', icon: 'simple-icons:figma' },
|
||||
{ id: 'linear', name: 'Linear', icon: 'simple-icons:linear' },
|
||||
{ id: 'stripe', name: 'Stripe', icon: 'simple-icons:stripe' },
|
||||
{ id: 'npm', name: 'npm', icon: 'ri:npmjs-line' },
|
||||
{ id: 'perplexity', name: 'Perplexity', icon: 'ri:perplexity-line' },
|
||||
{ id: 'canva', name: 'Canva', icon: 'simple-icons:canva' },
|
||||
{ id: 'docker', name: 'Docker', icon: 'simple-icons:docker' },
|
||||
];
|
||||
|
||||
const placeholderSizes = [10, 7, 9, 6, 8];
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
// to http:// or in a loop: any of those fails like a block and reads as
|
||||
// "blocked". Bump ADBLOCK_PROBES_VERSION whenever the list changes.
|
||||
|
||||
export const ADBLOCK_PROBES_VERSION = '2026-10-19';
|
||||
export const ADBLOCK_PROBES_VERSION = '2026-10-23';
|
||||
|
||||
export const ADBLOCK_PROBE_GROUPS = [
|
||||
{
|
||||
@@ -49,6 +49,7 @@ export const ADBLOCK_PROBE_GROUPS = [
|
||||
{ id: 'clarity', name: 'Microsoft Clarity', targets: ['www.clarity.ms', 'c.clarity.ms'] },
|
||||
{ id: 'hotjar', name: 'Hotjar', targets: ['static.hotjar.com', 'script.hotjar.com', 'insights.hotjar.com'] },
|
||||
{ id: 'adobe-analytics', name: 'Adobe Analytics', targets: ['assets.adobedtm.com', 'dpm.demdex.net'] },
|
||||
{ id: 'hubspot', name: 'HubSpot', targets: ['js.hs-analytics.net', 'track.hubspot.com'] },
|
||||
{ id: 'mixpanel', name: 'Mixpanel', targets: ['api-js.mixpanel.com', 'cdn.mxpnl.com'] },
|
||||
{ id: 'segment', name: 'Segment', targets: ['api.segment.io', 'cdn.segment.com'] },
|
||||
{ id: 'amplitude', name: 'Amplitude', targets: ['api2.amplitude.com', 'cdn.amplitude.com'] },
|
||||
|
||||
@@ -2211,7 +2211,7 @@
|
||||
},
|
||||
{
|
||||
"version": "v8.0.0",
|
||||
"date": "Beta",
|
||||
"date": "2026-10-23",
|
||||
"content": [
|
||||
{
|
||||
"type": "add",
|
||||
@@ -2290,6 +2290,17 @@
|
||||
"pt-BR": "Melhoria da experiência PWA no iOS"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "improve",
|
||||
"change": {
|
||||
"en": "Service Status now covers more well-known internet products",
|
||||
"zh": "「服务可用性」高级工具新增更多知名互联网产品",
|
||||
"zh-TW": "「服務可用性」進階工具新增更多知名網路產品",
|
||||
"fr": "L'outil État des services couvre davantage de produits Internet connus",
|
||||
"ru": "Инструмент «Состояние сервисов» охватывает больше известных интернет-продуктов",
|
||||
"pt-BR": "A ferramenta Status dos serviços agora cobre mais produtos conhecidos da internet"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "fix",
|
||||
"change": {
|
||||
|
||||
@@ -235,6 +235,28 @@ export const IMPORT_LISTS = [
|
||||
{ id: 'keds', name: 'KEDS', url: 'https://www.keds-energy.com/favicon.ico' },
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'thailand',
|
||||
emoji: '🇹🇭',
|
||||
members: [
|
||||
{ id: 'pantip', name: 'Pantip', url: 'https://pantip.com/fav-icon.ico' },
|
||||
{ id: 'sanook', name: 'Sanook', url: 'https://www.sanook.com/favicon.ico' },
|
||||
{ id: 'thairath', name: 'Thairath', url: 'https://www.thairath.co.th/static/img/favicon/favicon-32x32.png' },
|
||||
{ id: 'thaipbs', name: 'Thai PBS', url: 'https://www.thaipbs.or.th/favicon.ico' },
|
||||
{ id: 'ch3plus', name: 'CH3Plus', url: 'https://www.ch3plus.com/favicon.ico' },
|
||||
{ id: 'shopee-th', name: 'Shopee', url: 'https://shopee.co.th/favicon.ico' },
|
||||
{ id: 'lazada-th', name: 'Lazada', url: 'https://www.lazada.co.th/favicon.ico' },
|
||||
{ id: 'wongnai', name: 'Wongnai', url: 'https://www.wongnai.com/favicon.ico' },
|
||||
{ id: 'kbank', name: 'KBank', url: 'https://www.kasikornbank.com/favicon.ico' },
|
||||
{ id: 'scb', name: 'SCB', url: 'https://www.scb.co.th/favicon.ico' },
|
||||
{ id: 'bangkokbank', name: 'Bangkok Bank', url: 'https://www.bangkokbank.com/favicon.ico' },
|
||||
{ id: 'ais', name: 'AIS', url: 'https://www.ais.th/favicon.ico' },
|
||||
{ id: 'true-th', name: 'True', url: 'https://www.true.th/favicon.ico' },
|
||||
{ id: 'rd-go-th', name: 'Revenue Department', url: 'https://www.rd.go.th/typo3conf/ext/rdinter_theme13/Resources/Public/Icons/favicon-32x32.png' },
|
||||
{ id: 'mea', name: 'MEA', url: 'https://www.mea.or.th/favicon.ico' },
|
||||
{ id: 'pea', name: 'PEA', url: 'https://www.pea.co.th/favicon.ico' },
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'ai',
|
||||
emoji: '🤖',
|
||||
|
||||
@@ -1082,7 +1082,7 @@
|
||||
"EnhancedDnsLeakTest": "Captures every recursive resolver, with ECS and DNSSEC analysis",
|
||||
"RuleTestNote": "Check the rule settings of proxy software",
|
||||
"CensorshipCheck": "Check which countries a website is blocked in",
|
||||
"ServiceStatus": "Check the live availability of well-known AI and developer services, and whether a recent incident explains the outage you're seeing",
|
||||
"ServiceStatus": "Check the live availability of well-known internet products, and whether a recent incident explains the outage you're seeing",
|
||||
"Whois": "Search for domain/IP/ASN registration information",
|
||||
"AsnProfile": "View the full profile of any AS",
|
||||
"InvisibilityTest": "Check if you are using a proxy or VPN",
|
||||
@@ -1760,7 +1760,7 @@
|
||||
},
|
||||
"serviceStatus": {
|
||||
"Title": "Service Status",
|
||||
"Note": "When a service won't open, it might not be your problem — the service itself could be down. Currently, people are using a lot of AI products, and when the AI product doesn't reply, they don't know if it's their problem or the service's problem. This section shows the live availability of well-known AI services; expand a card to see each sub-service's status and recent incidents.",
|
||||
"Note": "When a service won't open, the problem might not be on your end — the service itself could be down. This section shows the live availability of well-known internet products, from AI assistants to developer platforms; expand a card to see each sub-service's status and recent incidents.",
|
||||
"Refresh": "Refresh Service Status",
|
||||
"LastChecked": "Last checked",
|
||||
"AllOperational": "All Systems Operational",
|
||||
@@ -1867,6 +1867,7 @@
|
||||
"germany": "Germany Essentials",
|
||||
"albania": "Albania Essentials",
|
||||
"kosovo": "Kosovo Essentials",
|
||||
"thailand": "Thailand Essentials",
|
||||
"ai": "AI Services",
|
||||
"social": "Social & Messaging",
|
||||
"productivity": "Work & Productivity",
|
||||
|
||||
@@ -1082,7 +1082,7 @@
|
||||
"EnhancedDnsLeakTest": "Capture tous les résolveurs récursifs avec analyse ECS et DNSSEC",
|
||||
"RuleTestNote": "Vérifiez les paramètres de règles du logiciel de proxy",
|
||||
"CensorshipCheck": "Vérifier dans quels pays un site est bloqué",
|
||||
"ServiceStatus": "Vérifiez la disponibilité en temps réel des services d'IA et de développement connus, et si un incident récent explique la panne que vous constatez",
|
||||
"ServiceStatus": "Vérifiez la disponibilité en temps réel de produits Internet connus, et si un incident récent explique la panne que vous constatez",
|
||||
"Whois": "Recherche d'informations sur l'enregistrement de domaine/IP/AS",
|
||||
"AsnProfile": "Consulter le profil complet de n'importe quel AS",
|
||||
"InvisibilityTest": "Vérifiez si vous utilisez un proxy ou un VPN",
|
||||
@@ -1760,7 +1760,7 @@
|
||||
},
|
||||
"serviceStatus": {
|
||||
"Title": "État des services",
|
||||
"Note": "Quand un service ne s'ouvre pas, le problème ne vient pas forcément de vous — le service lui-même peut être en panne. Actuellement, les gens utilisent beaucoup de produits AI, et quand le produit AI ne répond pas, ils ne savent pas si c'est leur problème ou celui du service. Cette section affiche la disponibilité en temps réel de services AI populaires ; dépliez une carte pour voir l'état de chaque sous-service et les incidents récents.",
|
||||
"Note": "Quand un service ne s'ouvre pas, le problème ne vient pas forcément de vous — le service lui-même peut être en panne. Cette section affiche la disponibilité en temps réel de produits Internet connus, des assistants IA aux plateformes pour développeurs ; dépliez une carte pour voir l'état de chaque sous-service et les incidents récents.",
|
||||
"Refresh": "Actualiser l'état des services",
|
||||
"LastChecked": "Dernière vérification",
|
||||
"AllOperational": "Tous les systèmes sont opérationnels",
|
||||
@@ -1867,6 +1867,7 @@
|
||||
"germany": "Essentiels Allemagne",
|
||||
"albania": "Essentiels Albanie",
|
||||
"kosovo": "Essentiels Kosovo",
|
||||
"thailand": "Essentiels Thaïlande",
|
||||
"ai": "Services IA",
|
||||
"social": "Réseaux sociaux",
|
||||
"productivity": "Travail et productivité",
|
||||
|
||||
@@ -1082,7 +1082,7 @@
|
||||
"EnhancedDnsLeakTest": "Captura todos os resolvedores recursivos, com análise de ECS e DNSSEC",
|
||||
"RuleTestNote": "Verifique as regras do software de proxy",
|
||||
"CensorshipCheck": "Verifique em quais países um site está bloqueado",
|
||||
"ServiceStatus": "Verifique a disponibilidade ao vivo de serviços conhecidos de IA e desenvolvimento, e se um incidente recente explica a falha que você está vendo",
|
||||
"ServiceStatus": "Verifique a disponibilidade ao vivo de produtos conhecidos da internet, e se um incidente recente explica a falha que você está vendo",
|
||||
"Whois": "Pesquise informações de registro de domínio/IP/AS",
|
||||
"AsnProfile": "Veja o perfil completo de qualquer AS",
|
||||
"InvisibilityTest": "Verifique se você está usando proxy ou VPN",
|
||||
@@ -1760,7 +1760,7 @@
|
||||
},
|
||||
"serviceStatus": {
|
||||
"Title": "Status dos serviços",
|
||||
"Note": "Quando um serviço não abre, talvez o problema não seja seu — o próprio serviço pode estar fora do ar. Atualmente, muitas pessoas usam produtos de IA, e quando o produto de IA não responde, elas não sabem se é problema delas ou do serviço. Esta seção mostra a disponibilidade ao vivo de serviços conhecidos de IA; expanda um cartão para ver o status de cada subserviço e incidentes recentes.",
|
||||
"Note": "Quando um serviço não abre, talvez o problema não seja seu — o próprio serviço pode estar fora do ar. Esta seção mostra a disponibilidade ao vivo de produtos conhecidos da internet, de assistentes de IA a plataformas para desenvolvedores; expanda um cartão para ver o status de cada subserviço e incidentes recentes.",
|
||||
"Refresh": "Atualizar status dos serviços",
|
||||
"LastChecked": "Última verificação",
|
||||
"AllOperational": "Sistemas operando normalmente",
|
||||
@@ -1867,6 +1867,7 @@
|
||||
"germany": "Essenciais da Alemanha",
|
||||
"albania": "Essenciais da Albânia",
|
||||
"kosovo": "Essenciais do Kosovo",
|
||||
"thailand": "Essenciais da Tailândia",
|
||||
"ai": "Serviços de IA",
|
||||
"social": "Redes sociais e mensagens",
|
||||
"productivity": "Trabalho e produtividade",
|
||||
|
||||
@@ -1082,7 +1082,7 @@
|
||||
"EnhancedDnsLeakTest": "Обнаруживает все рекурсивные резолверы и анализирует ECS и DNSSEC",
|
||||
"RuleTestNote": "Проверка правил прокси-программы",
|
||||
"CensorshipCheck": "Проверка блокировки сайта в каких странах",
|
||||
"ServiceStatus": "Проверка доступности известных сервисов AI и разработчиков, а также недавних инцидентов, которые могут объяснить сбой",
|
||||
"ServiceStatus": "Проверка доступности известных интернет-продуктов, а также недавних инцидентов, которые могут объяснить сбой",
|
||||
"Whois": "Поиск регистрационных сведений о домене, IP или AS",
|
||||
"AsnProfile": "Полный профиль любой AS",
|
||||
"InvisibilityTest": "Проверка использования прокси или VPN",
|
||||
@@ -1760,7 +1760,7 @@
|
||||
},
|
||||
"serviceStatus": {
|
||||
"Title": "Состояние сервисов",
|
||||
"Note": "Если сервис не открывается, проблема может быть не у вас — возможно, он сам недоступен. Люди активно используют продукты AI и, когда продукт AI не отвечает, не всегда понимают, где возник сбой. Здесь показана текущая доступность известных AI-сервисов. Разверните карточку, чтобы увидеть состояние подсервисов и недавние инциденты.",
|
||||
"Note": "Если сервис не открывается, проблема может быть не у вас — возможно, он сам недоступен. Здесь показана текущая доступность известных интернет-продуктов — от AI-ассистентов до платформ для разработчиков. Разверните карточку, чтобы увидеть состояние подсервисов и недавние инциденты.",
|
||||
"Refresh": "Обновить состояние сервисов",
|
||||
"LastChecked": "Последняя проверка",
|
||||
"AllOperational": "Все системы работают",
|
||||
@@ -1867,6 +1867,7 @@
|
||||
"germany": "Германия: основные",
|
||||
"albania": "Албания: основные",
|
||||
"kosovo": "Косово: основные",
|
||||
"thailand": "Таиланд: основные",
|
||||
"ai": "ИИ-сервисы",
|
||||
"social": "Соцсети",
|
||||
"productivity": "Работа и продуктивность",
|
||||
|
||||
@@ -1082,7 +1082,7 @@
|
||||
"EnhancedDnsLeakTest": "找出所有正在使用的遞迴 DNS 解析器,並分析 ECS 與 DNSSEC",
|
||||
"RuleTestNote": "檢查代理軟體的規則設定",
|
||||
"CensorshipCheck": "檢查一個網站在全球哪些國家被封鎖",
|
||||
"ServiceStatus": "檢視知名 AI 與開發者服務的即時可用性,確認你遇到的中斷是不是對方的故障",
|
||||
"ServiceStatus": "檢視知名網路產品的即時可用性,確認你遇到的中斷是不是對方的故障",
|
||||
"Whois": "對網域名稱、IP 或 AS 號碼進行 Whois 查詢",
|
||||
"AsnProfile": "查看任意 AS 的完整檔案",
|
||||
"InvisibilityTest": "猜猜看我是否知道你掛了代理",
|
||||
@@ -1760,7 +1760,7 @@
|
||||
},
|
||||
"serviceStatus": {
|
||||
"Title": "服務可用性",
|
||||
"Note": "當你打不開某些服務的時候,可能並不是你的問題,而是這些服務出故障了。目前,人們大量使用 AI 產品,並在 AI 產品沒有回覆的時候,不確定是自己的問題還是服務的問題。本板塊展示一些知名 AI 服務的即時可用狀態,展開卡片可檢視各子服務的狀態與最近的事故。",
|
||||
"Note": "當你打不開某些服務的時候,問題可能並不在你這邊,而是服務本身出了故障。本板塊展示知名網路產品(從 AI 助理到開發者平台)的即時可用狀態;展開卡片可檢視各子服務的狀態與最近的事故。",
|
||||
"Refresh": "重新整理服務狀態",
|
||||
"LastChecked": "最後檢查",
|
||||
"AllOperational": "所有系統正常",
|
||||
@@ -1867,6 +1867,7 @@
|
||||
"germany": "德國常用",
|
||||
"albania": "阿爾巴尼亞常用",
|
||||
"kosovo": "科索沃常用",
|
||||
"thailand": "泰國常用",
|
||||
"ai": "AI 服務",
|
||||
"social": "社交通訊",
|
||||
"productivity": "工作與生產力",
|
||||
|
||||
@@ -1082,7 +1082,7 @@
|
||||
"EnhancedDnsLeakTest": "捕获所有递归解析器,并分析 ECS 与 DNSSEC",
|
||||
"RuleTestNote": "检查代理软件的规则设置",
|
||||
"CensorshipCheck": "检查一个网站在全球哪些国家被封锁",
|
||||
"ServiceStatus": "查看知名 AI 与开发者服务的实时可用性,确认你遇到的中断是不是对方的故障",
|
||||
"ServiceStatus": "查看知名互联网产品的实时可用性,确认你遇到的中断是不是对方的故障",
|
||||
"Whois": "对域名、IP 或 AS 号进行 Whois 查询",
|
||||
"AsnProfile": "查看任意 AS 的完整档案",
|
||||
"InvisibilityTest": "猜猜看我是否知道你挂了代理",
|
||||
@@ -1760,7 +1760,7 @@
|
||||
},
|
||||
"serviceStatus": {
|
||||
"Title": "服务可用性",
|
||||
"Note": "当你打不开某些服务的时候,可能并不是你的问题,而是这些服务出故障了。当前,人们大量使用 AI 产品,并在 AI 产品没有回复的时候,不确定是自己的问题还是服务的问题。本板块展示一些知名 AI 服务的实时可用状态,展开卡片可查看各子服务的状态与最近的事故。",
|
||||
"Note": "当你打不开某些服务的时候,问题可能并不在你这边,而是服务本身出了故障。本板块展示知名互联网产品(从 AI 助手到开发者平台)的实时可用状态;展开卡片可查看各子服务的状态与最近的事故。",
|
||||
"Refresh": "刷新服务状态",
|
||||
"LastChecked": "最后检查",
|
||||
"AllOperational": "所有系统正常",
|
||||
@@ -1867,6 +1867,7 @@
|
||||
"germany": "德国常用",
|
||||
"albania": "阿尔巴尼亚常用",
|
||||
"kosovo": "科索沃常用",
|
||||
"thailand": "泰国常用",
|
||||
"ai": "AI 服务",
|
||||
"social": "社交通讯",
|
||||
"productivity": "工作与生产力",
|
||||
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 936 B |
|
After Width: | Height: | Size: 936 B |
|
After Width: | Height: | Size: 4.2 KiB |
|
After Width: | Height: | Size: 7.1 KiB |
|
After Width: | Height: | Size: 8.5 KiB |
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 6.2 KiB |
|
After Width: | Height: | Size: 3.4 KiB |
|
After Width: | Height: | Size: 566 B |
|
After Width: | Height: | Size: 934 B |
|
After Width: | Height: | Size: 1021 B |
|
After Width: | Height: | Size: 912 B |
|
After Width: | Height: | Size: 620 B |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 913 B |
@@ -11,6 +11,18 @@ import { STATUS_PROVIDER_IDS } from './service-status-providers.js';
|
||||
import { DNS_RECORD_TYPE_SET } from '../common/dns-record-types.js';
|
||||
import { parseAsnInput } from '../common/asn-input.js';
|
||||
|
||||
// Express 5's req.query is a prototype getter that re-parses the URL on every
|
||||
// read, so a direct write is lost. Pin the parsed object as an own property
|
||||
// first; later guards, cacheable resolvers and the handler then share it.
|
||||
const setQueryParam = (req, name, value) => {
|
||||
if (!Object.hasOwn(req, 'query')) {
|
||||
Object.defineProperty(req, 'query', {
|
||||
value: req.query, writable: true, configurable: true, enumerable: true,
|
||||
});
|
||||
}
|
||||
req.query[name] = value;
|
||||
};
|
||||
|
||||
// Reject requests without an allowed referer. The error message variant
|
||||
// preserves the existing user-facing wording.
|
||||
export const requireReferer = (req, res, next) => {
|
||||
@@ -57,7 +69,7 @@ export const requireValidDomain = (paramName = 'domain') => (req, res, next) =>
|
||||
if (!isValidDomain(domain)) {
|
||||
return res.status(400).json({ error: 'Invalid domain' });
|
||||
}
|
||||
req.query[paramName] = domain;
|
||||
setQueryParam(req, paramName, domain);
|
||||
next();
|
||||
};
|
||||
|
||||
@@ -89,7 +101,7 @@ export const requireValidASN = (paramName = 'asn') => (req, res, next) => {
|
||||
if (asn === null) {
|
||||
return res.status(400).json({ error: 'Invalid ASN' });
|
||||
}
|
||||
req.query[paramName] = String(asn);
|
||||
setQueryParam(req, paramName, String(asn));
|
||||
next();
|
||||
};
|
||||
|
||||
@@ -104,7 +116,7 @@ export const normalizeAsnQuery = (paramName = 'q') => (req, res, next) => {
|
||||
if (asn === null) {
|
||||
return res.status(400).json({ error: 'Invalid ASN' });
|
||||
}
|
||||
req.query[paramName] = `AS${asn}`;
|
||||
setQueryParam(req, paramName, `AS${asn}`);
|
||||
next();
|
||||
};
|
||||
|
||||
@@ -119,7 +131,7 @@ export const requireValidCountry = (paramName = 'country') => (req, res, next) =
|
||||
if (!/^[A-Za-z]{2}$/.test(raw)) {
|
||||
return res.status(400).json({ error: 'Invalid country' });
|
||||
}
|
||||
req.query[paramName] = String(raw).toUpperCase();
|
||||
setQueryParam(req, paramName, String(raw).toUpperCase());
|
||||
next();
|
||||
};
|
||||
|
||||
@@ -145,7 +157,7 @@ export const requireValidRecordType = (paramName = 'type') => (req, res, next) =
|
||||
if (!DNS_RECORD_TYPE_SET.has(String(type).toUpperCase())) {
|
||||
return res.status(400).json({ error: 'Invalid record type' });
|
||||
}
|
||||
req.query[paramName] = String(type).toUpperCase();
|
||||
setQueryParam(req, paramName, String(type).toUpperCase());
|
||||
next();
|
||||
};
|
||||
|
||||
|
||||
@@ -11,6 +11,8 @@
|
||||
// `page` — public status page; also the base for incident detail links
|
||||
// (`${page}/incidents/${incidentId}`), since incident.io's feed omits
|
||||
// a usable shortlink.
|
||||
// `components` — optional row selector (include / exclude / flatten / rename /
|
||||
// indicator); see selectComponents in service-status-transform.js.
|
||||
// `platform` — 'incident.io': components come from the native `/proxy/<host>`
|
||||
// structure, since the compat summary flattens groups away.
|
||||
//
|
||||
@@ -56,6 +58,14 @@ export const STATUS_PROVIDERS = [
|
||||
// non-Statuspage schema; www.stripestatus.com is its Statuspage mirror,
|
||||
// which exposes the /api/v2 endpoints this poller needs.
|
||||
{ id: 'stripe', name: 'Stripe', api: 'https://www.stripestatus.com', page: 'https://www.stripestatus.com' },
|
||||
// rename: "www.npmjs.com website" repeats the domain inside the npm card.
|
||||
{
|
||||
id: 'npm', name: 'npm', api: 'https://status.npmjs.org', page: 'https://status.npmjs.org',
|
||||
components: { rename: { mvm98gtxvb9b: 'Official Website' } },
|
||||
},
|
||||
{ id: 'perplexity', name: 'Perplexity', api: 'https://status.perplexity.com', page: 'https://status.perplexity.com', platform: 'incident.io' },
|
||||
{ id: 'canva', name: 'Canva', api: 'https://www.canvastatus.com', page: 'https://www.canvastatus.com' },
|
||||
{ id: 'docker', name: 'Docker', api: 'https://www.dockerstatus.com', page: 'https://www.dockerstatus.com', platform: 'incident.io' },
|
||||
];
|
||||
|
||||
// Whitelist used by requireValidProviderId() — the per-provider detail
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
// Rather than fetch upstream status pages on each visitor request, a single
|
||||
// background timer refreshes all providers every REFRESH_INTERVAL and keeps
|
||||
// the latest normalized snapshot in memory. Request handlers just read the
|
||||
// snapshot — upstream load is constant (16 providers × 2 endpoints / 5 min,
|
||||
// snapshot — upstream load is constant (20 providers × 2 endpoints / 5 min,
|
||||
// +1 per incident.io page) no matter how much traffic the site gets.
|
||||
//
|
||||
// Mirrors the bootstrap-at-boot + start-scheduler shape of the MaxMind / CAIDA
|
||||
|
||||
@@ -16,6 +16,7 @@ const COMPONENT_LIMIT = 40;
|
||||
// { include: [names] } ordered whitelist by component name
|
||||
// { exclude: [id|name] } final filter on top of any mode (drops junk rows)
|
||||
// { indicator: 'components' } card light from the selected rows, not the page's
|
||||
// { rename: { id: label } } display label by component id, after selection
|
||||
function selectComponents(raw, selector = {}) {
|
||||
let picked;
|
||||
if (Array.isArray(selector.include)) {
|
||||
@@ -31,6 +32,12 @@ function selectComponents(raw, selector = {}) {
|
||||
const blocked = new Set(selector.exclude);
|
||||
picked = picked.filter((c) => !blocked.has(c?.id) && !blocked.has(c?.name));
|
||||
}
|
||||
|
||||
const rename = selector.rename;
|
||||
if (rename && typeof rename === 'object') {
|
||||
// Copies, so the upstream rows stay untouched.
|
||||
picked = picked.map((c) => (Object.hasOwn(rename, c?.id) ? { ...c, name: rename[c.id] } : c));
|
||||
}
|
||||
return picked;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import assert from 'node:assert/strict';
|
||||
import { describe, it } from 'node:test';
|
||||
import express from 'express';
|
||||
|
||||
import { requireReferer, requirePublicIP, requireValidPrefix, requireValidDomain, requireValidProviderId, requireValidRecordType, requireValidReportId, requireValidCountry, requireValidASN, normalizeAsnQuery } from '../server/guards.js';
|
||||
|
||||
@@ -407,3 +408,47 @@ describe('normalizeAsnQuery', () => {
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Express 5 defines req.query as a prototype getter that re-parses the URL
|
||||
// on every read; a real request (never listening) catches rewrites that a
|
||||
// plain-object stub would keep.
|
||||
describe('guard rewrites on a real Express request', () => {
|
||||
const app = express();
|
||||
const makeExpressReq = (url) => {
|
||||
const req = Object.create(app.request);
|
||||
req.url = url;
|
||||
req.headers = {};
|
||||
return req;
|
||||
};
|
||||
|
||||
it('reproduces the re-parsing getter', () => {
|
||||
const req = makeExpressReq('/x?type=caa');
|
||||
assert.notEqual(req.query, req.query);
|
||||
});
|
||||
|
||||
for (const [name, guard, url, param, expected] of [
|
||||
['requireValidRecordType', requireValidRecordType(), '/x?type=caa', 'type', 'CAA'],
|
||||
['requireValidDomain', requireValidDomain(), '/x?domain=Example.COM', 'domain', 'example.com'],
|
||||
['requireValidASN', requireValidASN(), '/x?asn=AS013335', 'asn', '13335'],
|
||||
['normalizeAsnQuery', normalizeAsnQuery(), '/x?q=as13335', 'q', 'AS13335'],
|
||||
['requireValidCountry', requireValidCountry(), '/x?country=us', 'country', 'US'],
|
||||
]) {
|
||||
it(`${name} rewrite survives a later req.query read`, () => {
|
||||
const req = makeExpressReq(url);
|
||||
let nextCalled = false;
|
||||
guard(req, makeRes(), () => { nextCalled = true; });
|
||||
assert.equal(nextCalled, true);
|
||||
assert.equal(req.query[param], expected);
|
||||
});
|
||||
}
|
||||
|
||||
it('keeps both rewrites when chained like /api/dnsresolver', () => {
|
||||
const req = makeExpressReq('/x?type=caa&hostname=Example.COM');
|
||||
let calls = 0;
|
||||
requireValidDomain('hostname')(req, makeRes(), () => { calls++; });
|
||||
requireValidRecordType()(req, makeRes(), () => { calls++; });
|
||||
assert.equal(calls, 2);
|
||||
assert.deepEqual({ ...req.query }, { type: 'CAA', hostname: 'example.com' });
|
||||
assert.equal(req.query, req.query);
|
||||
});
|
||||
});
|
||||
@@ -65,6 +65,22 @@ describe('normalizeSummary', () => {
|
||||
assert.deepEqual(byName.components.map((c) => c.name), ['Login', 'Standalone']);
|
||||
});
|
||||
|
||||
it('rename relabels rows by id after selection, leaving the input untouched', () => {
|
||||
const snapshot = structuredClone(GROUPED);
|
||||
const out = normalizeSummary(GROUPED, { rename: { s1: 'Solo', nope: 'Ignored' } });
|
||||
assert.deepEqual(out.components.map((c) => c.name), ['Core', 'Solo']);
|
||||
assert.deepEqual(GROUPED, snapshot);
|
||||
});
|
||||
|
||||
it('include / exclude still match the upstream names when rename is set', () => {
|
||||
const excluded = normalizeSummary(GROUPED, {
|
||||
flatten: true, exclude: ['Search'], rename: { c1: 'Sign-in', c2: 'Find' },
|
||||
});
|
||||
assert.deepEqual(excluded.components.map((c) => c.name), ['Sign-in', 'Standalone']);
|
||||
const included = normalizeSummary(GROUPED, { include: ['Search'], rename: { c2: 'Find' } });
|
||||
assert.deepEqual(included.components, [{ name: 'Find', status: 'partial_outage' }]);
|
||||
});
|
||||
|
||||
it('caps the component list at the internal limit', () => {
|
||||
const components = Array.from({ length: 50 }, (_, i) => ({
|
||||
id: `c${i}`, name: `svc${i}`, status: 'operational',
|
||||
@@ -311,6 +327,11 @@ describe('assembleProvider', () => {
|
||||
assert.deepEqual(out.components.map((c) => c.name), ['Codex']);
|
||||
});
|
||||
|
||||
it('applies rename to structure rows by id', () => {
|
||||
const out = assembleProvider({ ...provider, components: { rename: { g1: 'Platform APIs' } } }, summaryJson, null, structureJson);
|
||||
assert.deepEqual(out.components.map((c) => c.name), ['Platform APIs', 'Codex']);
|
||||
});
|
||||
|
||||
it('falls back to the summary components when the structure yields no rows', () => {
|
||||
const out = assembleProvider(provider, summaryJson, null, { summary: {} });
|
||||
assert.deepEqual(out.components.map((c) => c.name), ['Flattened leaf']);
|
||||
|
||||