
وكيل عكسي مثل nginx، مبني على pingora، بسيط وفعّال.
# Pingap
وكيل عكسي وبوابة تطبيقات مبنية على [Cloudflare Pingora](https://github.com/cloudflare/pingora). يتم تكوينها عبر ملفات TOML أو عبر لوحة إدارة ويب مدمجة في الملف التنفيذي، وتستقبل التغييرات دون إعادة تشغيل، وتأتي مع أكثر من ثلاثين إضافة.
[中文说明](https://github.com/vicanso/pingap/blob/main/README_zh.md) | [Documentation](https://pingap.io/) · [中文文档](https://pingap.io/zh/) | [Examples](https://github.com/vicanso/pingap/blob/main/examples/README.md) | [Plugins](https://github.com/vicanso/pingap/blob/main/pingap-plugin/README.md) | [Crates](https://github.com/vicanso/pingap/blob/main/docs/README.md) | [Upgrading](https://pingap.io/guide/upgrading)

## ما الذي يميزها
- **لوحة إدارة ويب مدمجة في الملف التنفيذي.** لكل إعداد نموذج خاص به. يتم التحقق من التغيير بنفس الطريقة التي يتحقق بها `pingap -t` قبل تخزينه، ويمكن الاحتفاظ بالإصدارات السابقة واستعادتها، وهناك حسابات للقراءة فقط ورموز API. تعرض صفحتها الرئيسية رسومًا بيانية لحركة المرور خلال الخمس عشرة دقيقة الأخيرة - المعدل والحالة وزمن الاستجابة ولكل خادم مصدر - دون الحاجة إلى Prometheus.
- **تغييرات دون توقف.** يتم استبدال الخوادم المصدر والمواقع والإضافات والشهادات داخل العملية قيد التشغيل (`--autoreload`). أما ما يحتاج إلى مستمعين جدد فيُطبَّق عبر إعادة تشغيل تُسلِّم المقابس (`--autorestart`، لخدمة خلفية على Linux)، وتستمر العملية قيد التشغيل في تقديم الخدمة إذا لم تبدأ العملية البديلة.
- **أكثر من ثلاثين إضافة**، لكل موقع وبالترتيب الذي تسرده: المصادقة (JWT، OIDC، مفتاح، أساسي، HMAC، مصادقة تمريرية)، قواعد الوصول (IP، المُحيل، وكيل المستخدم، المسارات المحظورة)، حركة المرور (حدود المعدل وعرض النطاق، التخزين المؤقت، النسخ المتطابق، تقسيم حركة المرور)، المحتوى (الضغط، استبدال النص، إعادة التوجيه، الملفات الثابتة، تحسين الصور) والعمليات (وضع الصيانة، نقطة نهاية الحالة الصحية، صفحات الأخطاء). [جميع الإضافات](https://github.com/vicanso/pingap/blob/main/pingap-plugin/README.md)
- **شهادات دون مهمة مجدولة.** Let's Encrypt أو أي جهة ACME أخرى، عبر HTTP-01 أو DNS-01 (Alibaba Cloud، Cloudflare، Tencent Cloud، Huawei Cloud)، وRSA وECDSA جنبًا إلى جنب، وتثبيت OCSP، وTLS متبادل تجاه العملاء وتجاه الخوادم المصدر.
- **تكوين يتم التحقق منه قبل التشغيل.** يبني `-t` كل ما يبنيه التشغيل، ويرفض `--strict` المفاتيح التي لا يعرفها، ويعرض `--diff` ما سيفعله التغيير. TOML أو HCL أو KDL، في ملفات أو في etcd، مع أسرار مأخوذة من البيئة أو من الملفات.
- **مصممة للمراقبة.** مقاييس Prometheus (سحب ودفع)، وتتبعات OpenTelemetry، وسجلات الوصول كنص أو JSON بأكثر من ثلاثين حقلًا، وبصمات JA4 TLS للعملاء.
بقية ما يفعله الوكيل العكسي موجودة أيضًا: HTTP/1.1 وHTTP/2 وWebSocket وgRPC-web؛ التوجيه حسب المضيف والمسار والتعبير النمطي؛ round robin وأقل الاتصالات والتجزئة المتسقة والجلسات اللاصقة، مع فحوصات الحالة وإعادة المحاولات وقاطع الدائرة؛ خوادم خلفية من DNS (A/AAAA، SRV) أو من تسميات Docker؛ بروتوكول PROXY على المستمعين وتجاه الخوادم المصدر؛ OpenSSL أو rustls.
المقاييس والتتبع وتحسين الصور موجودة في إصدارات `-full`.
## 🚀 البدء
### Docker Compose
```yaml
# docker-compose.yml
services:
pingap:
image: vicanso/pingap:latest # for production, pin a release: vicanso/pingap:<version>-full
container_name: pingap-instance
restart: always
ports:
- "80:80"
- "443:443"
volumes:
# everything Pingap keeps: configuration, certificates, cache
- ./pingap_data:/opt/pingap
environment:
- PINGAP_CONF=/opt/pingap/conf
- PINGAP_ADMIN_ADDR=0.0.0.0:80/pingap
- PINGAP_ADMIN_USER=pingap
- PINGAP_ADMIN_PASSWORD=<YourSecurePassword> # change this
command:
- pingap
- --autoreload
```
```bash
mkdir pingap_data
docker compose up -d
```
المسؤول موجود على http://localhost/pingap. الصور موسومة بـ `latest` و`full` و`rustls-full`، وبـ `<version>` و`<version>-full` و`<version>-rustls-full` لإصدار ما. ولكل منها نسخة توأم `-distroless` بدون shell، حيث يجب أن يبدأ `command` بـ `pingap` كما هو الحال أعلاه.
### Binary
```bash
curl -sSL https://raw.githubusercontent.com/vicanso/pingap/main/install.sh | sh
```
يثبّت أحدث إصدار إلى `/usr/local/bin/pingap` على Linux وmacOS (x86_64 وarm64). المتغيرات التي تسبق `sh` تحدد ما يتم تثبيته:
| المتغير | التأثير |
| --- | --- |
| `PINGAP_FULL=1` | بناء `-full`، مع كل الميزات الاختيارية |
| `PINGAP_TLS=rustls` | Linux: بناء `-rustls-full`، بدون OpenSSL (انظر [TLS backend](#tls-backend)) |
| `PINGAP_LIBC=gnu` | Linux: بناء glibc بدلاً من بناء musl الثابت |
| `PINGAP_SERVICE=1` | Linux مع systemd: أيضاً خدمة `pingap` وإعداد ابتدائي في `/etc/pingap/conf`؛ لا يتم تفعيلها أو تشغيلها |
| `PINGAP_VERSION=0.15.0` | ذلك الإصدار بدلاً من الأحدث |
جميع الملفات موجودة على [صفحة الإصدارات](https://github.com/vicanso/pingap/releases).
### أمر واحد، بدون ملف إعداد
```bash
# the certificate comes from Let's Encrypt
pingap --domain=pingap.io --upstream=192.168.1.1:3000
# or bring your own
pingap --domain=pingap.io --upstream=192.168.1.1:3000 --cert=/etc/ssl/pingap.io
```
- بدون `--cert` يتم طلب الشهادة عبر HTTP-01: يجب أن يُحلَّ النطاق إلى هذا المضيف، مع إمكانية الوصول إلى المنفذ 80. تُحفظ في `~/.pingap/acme/<domains>.toml` وتُستخدم مجددًا عند التشغيل التالي. لا تحذفها، فالإصدار محدود المعدل.
- `--cert` هو الشهادة أو المجلد الذي توجد فيه (`fullchain.pem` / `privkey.pem`، و`cert.pem` / `key.pem` و`tls.crt` / `tls.key` يتم العثور عليها؛ ويُسمّي `--key` أي شيء آخر). يأخذ `--upstream` و`--domain` قوائم مفصولة بفواصل، ويأخذ `--addr` العنوان المراد الاستماع عليه.
- يُعاد إنشاء التكوين عند كل تشغيل، لذا لا يمكن للمشرف تعديله. لاستخدام أكثر من خادم واحد استخدم `--conf`، الذي لا يمكن الجمع بينه وبين هذه الأعلام.
## 📝 التكوين
```toml
[upstreams.api]
addrs = ["api.github.com:443"]
discovery = "dns"
sni = "api.github.com"
[plugins.staticServe]
category = "directory"
path = "~/Downloads"
[locations.github-api]
upstream = "api"
path = "/api"
proxy_set_headers = ["Host:api.github.com"]
rewrite = "^/api/(?<path>.+)$ /$1"
[locations.static]
plugins = ["staticServe"]
[servers.test]
addr = "127.0.0.1:6118"
locations = ["github-api", "static"]
```
يمكن كتابة الشيء نفسه بصيغة HCL أو KDL. يُوصف كل مفتاح في [مرجع الإعدادات](https://pingap.io/crates/config).
| | |
| --- | --- |
| `--autoreload` | يُطبَّق التغيير دون إعادة تشغيل: خلال عشر ثوانٍ من الملفات، وفورًا من etcd. ويُبلَّغ عمّا يتطلب إعادة تشغيل، وينتظرها. الوضع المناسب للحاويات |
| `-a`، `--autorestart` | الشيء نفسه، مع إعادة تشغيل لا تُسقط أي طلب لما لا يمكن أن يغيّره إعادة التحميل، كعنوان استماع مثلًا. لخدمة خلفية (`-d`) على Linux؛ وفي أي مكان آخر يعيد التحميل، ويبلّغ عمّا ينتظر |
| `-t` | يبني كل upstream وlocation وإضافة وشهادة وخادم كما يفعل بدء التشغيل، دون ربط أي شيء |
| `--strict` | المفتاح الذي لا يعرفه Pingap، أو الذي لا يملك هذا البناء ميزة له، يُعدّ خطأً، حيث يكون تحذيرًا في غير ذلك. لـ `-t` في CI |
| `--diff <other>` | يطبع ما سيتغيّر إذا حلّ `<other>` محل الإعدادات، مع عرض بيانات الاعتماد كمجاميع تحقّق |
| `$ENV:NAME`، `$FILE:/path` | كقيمة كاملة: يُستبدل بالمتغيّر أو بالملف عند تحميل الإعدادات |
## 🔄 كيف تُعالَج الطلبات

1. يقبل **الخادم** الاتصال ويختار **الموقع** الذي يتطابق مضيفه ومساره على أفضل وجه. والطلب الذي لا يطابق أيًّا منها يُردّ عليه بـ `404`.
2. تعمل **إضافات** الموقع بالترتيب الذي هي مدرجة به، كلٌّ منها في خطوته الخاصة: في طريق الدخول، أو في طريق العودة. ويمكن للإضافة أن تردّ على الطلب بنفسها - إصابة في الذاكرة المؤقتة، أو فشل مصادقة، أو إعادة توجيه، أو ملف ثابت - وعندها لا يُسأل الـ upstream إطلاقًا.
3. وإلا فإن **upstream** الموقع يختار خادمًا خلفيًا سليمًا ويُمرَّر الطلب إليه. ومع `max_retries`، يُجرَّب الاتصال الذي تعذّر إنشاؤه على خادم خلفي آخر؛ ومع `fallback_upstream`، يُوجَّه الطلب الذي لا يستطيع الـ upstream تلقّيه إلى upstream ثانٍ.
4. تعود الاستجابة عبر إضافات الاستجابة إلى العميل، ويُسجَّل الطلب: سجل الوصول والمقاييس والتتبّعات.
## 📊 الأداء
نحو 147,000 طلب في الثانية لـ `/ping` على خيط واحد من M4 Pro، مع مئين 99 قدره 116µs (`wrk`، 10 اتصالات، دون سجل وصول).
<details>
<summary>التشغيل</summary>
```
wrk 'http://127.0.0.1:6118/ping' --latency
Running 10s test @ http://127.0.0.1:6118/ping
2 threads and 10 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 66.41us 23.67us 1.11ms 76.54%
Req/Sec 73.99k 2.88k 79.77k 68.81%
Latency Distribution
50% 67.00us
75% 80.00us
90% 91.00us
99% 116.00us
1487330 requests in 10.10s, 194.32MB read
Requests/sec: 147260.15
Transfer/sec: 19.24MB
```
</details>
## 🔧 التطوير
```bash
make dev # needs bacon: cargo install bacon
make build-web # after a change under web/, needs Node.js
make fmt && make lint && make test
```
`make dev` يبني باستخدام ميزات `full`، ويشغّل Pingap على الإعدادات الموجودة في `~/tmp/pingap` مع `--autoreload`، ويقدّم لوحة الإدارة على `127.0.0.1:3018` (`pingap` / `123123`) ويعيد البناء عند تغيّر المصادر. تُضمَّن لوحة الإدارة في الملف التنفيذي من `dist/`، الذي يملؤه `make build-web`. الحد الأدنى لإصدار Rust المدعوم هو 1.96.
### خلفية TLS
يستخدم البناء الافتراضي OpenSSL، المُجمَّع من المصدر. أما rustls:
```bash
cargo build --release --no-default-features --features tls-rustls,full
```
يبني rustls دائمًا TLS 1.2 و1.3 مع مجموعات التشفير الخاصة بـ rustls، لذلك يتم رفض `tls_min_version` و`tls_max_version` و`tls_cipher_list` و`tls_ciphersuites` الخاصة بكل خادم عند التحقق من التكوين. كل شيء آخر يتصرف بالطريقة نفسها؛ وتُدرج الاختلافات مع [حزمة الشهادات](https://pingap.io/crates/certificate). يوضح `pingap --version` أي خلفية يمتلكها الملف التنفيذي.
## 🤝 المساهمة
طلبات السحب مرحّب بها.
- بالنسبة لميزة جديدة، يُرجى فتح مشكلة أولاً، حتى يمكن مناقشتها قبل كتابة أي شيفرة.
- يُرجى عدم فتح طلب سحب فقط لإصلاح خطأ مطبعي أو تنسيق تعليق؛ فهذه تُصلح على دفعات.
- شغّل `make fmt` و`make lint` و`make test` قبل الدفع. تشغّل CI الفحوصات نفسها على كل طلب سحب.
- تُقدَّم المساهمة بموجب [اتفاقية ترخيص المساهم](https://github.com/vicanso/pingap/blob/main/CLA.md)، والتي تُقبل بتحديد مربعها في قالب طلب السحب: يبقى العمل ملكًا لك، ويُرخَّص للمشروع بموجب ترخيصه.
## 📄 الترخيص
[Apache License, Version 2.0](https://github.com/vicanso/pingap/blob/main/LICENSE).