Repository navigation
FA Design Notes
زبان: English · فارسی
pulsecheck چطور ساخته شده، چرا به این شکل، و در راه اجرا شدن روی پنج محیط مختلف چه چیزهایی دستمان آمد. اگر میخواهید در پروژه مشارکت کنید یا مشکلی را ردیابی کنید به کارتان میآید.
| ماژول | کار | کجا استفاده میشود |
|---|---|---|
config.py |
dataclass برای هر کلید، پر کردن ${ENV}، بررسی نوعها و قالب پیامها |
همهجا |
result.py |
Result یک بررسی، و limit() برای max_response_ms
|
همهجا |
state.py |
ماشین حالت up/down (step)، Store روی SQLite و BlobStore روی یک سند JSON |
CLI و سرورلس |
engine.py |
یک اجرا: انتخاب سایتهای سررسیده، بررسی همزمان، تصمیم برای هشدار، ذخیرهی وضعیت و ارسال تلمتری؛ و status()
|
همهجا |
otel.py |
ساخت بار OTLP/HTTP JSON و ارسال آن | همهجا |
httpnotify.py |
تبدیل آدرسهای همقالب shoutrrr به درخواست HTTPS ساده برای ۱۲ سرویس | Vercel و Cloudflare |
checks.py |
HTTP (httpx)، TCP، ping و انقضای TLS | CLI و Vercel |
notify.py |
اجرای shoutrrr (image داکر یا فایل اجرایی)، تأیید آدرس، دانلود فایل اجرایی (با SHA-256) | CLI و Vercel |
setup.py، cli.py
|
دستورها، پیدا کردن فایل تنظیم، .env، init، بلوک cron و update
|
CLI |
web.py و app.py (ریشهی مخزن) |
Vercel: برنامهی WSGI و وضعیت در Upstash Redis | Vercel |
deploy/cloudflare/ |
build.py (Build Output)، src/entry.py (Worker) و deploy.sh
|
Cloudflare |
engine.run(cfg, store, check, send, post=...) خودش به شبکه، دیسک یا ساعتی که نشود جایش را عوض کرد دست نمیزند. هر چه لازم دارد را فراخواننده میدهد:
-
check(site)که یکResultبرمیگرداند (در CLI: thread با httpx، socket و ping؛ در Cloudflare:fetch)، -
send(urls, text)(در CLI: shoutrrr؛ در سرورلس: ارسالکنندههای HTTP)، -
post(url, headers, body)برای OpenTelemetry، - یک
store(فایل SQLite، یا یک سند JSON در Redis یا Workers KV).
برای همین یک موتور از cron، GitHub Actions، GitLab CI، Vercel و Cloudflare Worker اجرا میشود و بیشتر رفتارها را میشود بدون شبکه تست کرد.
-
SQLite (
Store): هر بررسی یک ردیف است (۴۰۰ روز نگه داشته میشود) و وضعیت فعلی هر سایت هم جدا ذخیره میشود. p95 دقیق میدهد. -
سند JSON (
BlobStore): وضعیت فعلی بهاضافهی جمع روزانهی هر سایت (تعداد بررسی، تعداد موفق، جمع تأخیر) برای ۹۰ روز. بنابراین در هر اجرا فقط یک سند کوچک خوانده و نوشته میشود. p95 ندارد. اگر دو اجرا روی هم بیفتند ممکن است یک بهروزرسانی از دست برود (آخرین نوشتن میماند)، ولی با فاصلهی cron این تقریباً پیش نمیآید. - وضعیت هر سایت اینها را دارد:
status، تعداد شکستهای پشتسرهمfails،since، زمان آخرین بررسیlastو یکmetaکوچک (زمان آخرین هشدار و آخرین هشدار گواهی، برای یادآوریها).
-
هشدار فقط هنگام تغییر.
step()تابعی خالص با تست است. یادآوری و هشدار گواهی در موتور روی آن سوار شدهاند. -
dataclass بهجای pydantic. Worker کلودفلر باید بدون هیچ بستهی شخص ثالثی اجرا شود. نمایهی بستههای Pyodide از بعضی شبکهها در دسترس نبود و بستهی کمتر یعنی cold start سریعتر.
config.build()نوعها را بررسی میکند و خطاهایی مثلconfig.sites[0].timeout: expected a numberمیدهد. -
YAML فقط در لبهها. Worker هنگام build یک JSON میگیرد و
ruamel.yamlفقط در لحظهی نیاز import میشود. -
یک shoutrrr، دو راه. CLI از shoutrrr واقعی استفاده میکند. جایی که اجرا نمیشود (Cloudflare) یا شاید وجود نداشته باشد (Vercel)، ارسالکنندههای HTTP داخلی سرویسهای رایج را پوشش میدهند. Vercel برای بقیه، فایل اجرایی واقعی را در
/tmpدانلود میکند و SHA-256 آن را باchecksums.txtهمان release مقایسه میکند. -
رمزها در خروجی نمیآیند. کانالها به شکل
telegram://***چاپ میشوند. endpointهای Vercel و Cloudflare توکن را باhmac.compare_digestمقایسه میکنند. فایل.envبا دسترسی ۶۰۰ ساخته میشود و اگرCRON_SECRETتنظیم نشده باشد، endpoint مربوط به cron در Vercel هر درخواستی را رد میکند. -
initرا میشود چندبار اجرا کرد. تنظیمات موجود را هرگز بازنویسی نمیکند و cron job داخل یک بلوک علامتدار است که جایگزین میشود و تکرار نمیشود.
| پلتفرم | یافته | نتیجه |
|---|---|---|
CLI به اسم cf در کلودفلر |
بتا است و بستهی dev server پایتونش منتشر نشده، پس نمیتواند Python Worker بسازد | Build Output را خودمان مینویسیم و cf deploy --prebuilt را اجرا میکنیم |
| Python Worker | فقط ماژول اصلی import میشود (بقیهی ماژولها و فایلهای داده mount نمیشوند) |
build.py پکیج را در یک entry.py میگذارد و ماژولهای pulsecheck.* را هنگام import ثبت میکند |
| Python Worker | در scheduled(self, controller, env, ctx) مقدار env برابر None است |
از self.env استفاده کنید |
| Python Worker | خط CONFIG_TEXT = None در سطح ماژول، مقدار تعبیهشده را بیصدا پاک کرد |
entry از globals().get(...) استفاده میکند و یک تست، bundle ساختهشده را اجرا میکند |
| ntfy.sh روی Cloudflare | به IP خروجیِ مشترک Worker کد 429 داد | خطای هشدار لاگ میشود و اجرا را متوقف نمیکند |
| build در Vercel |
.python-version با مقدار 3.15 خطا میدهد (No interpreter found) |
build.env.UV_PYTHON=3.14 در vercel.json
|
| اجرا در Vercel | توابع روی پایتون ۳٫۱۲٫۱۴ اجرا شدند؛ ping و Docker نبود، /tmp قابل نوشتن بود، HTTPS خروجی و اجرای فایل اجرایی دانلودشده کار کرد |
shoutrrr در صورت نیاز دانلود میشود |
| deploy در Vercel | یک فایل ۲۰ مگابایتی در آپلود باعث شد deploy گیر کند | هیچ فایل اجراییای آپلود نمیکنیم؛ هنگام اجرا دانلود میشود |
| Upstash در Vercel | صاحب حساب باید یکبار شرایط Upstash را در مرورگر بپذیرد؛ بعد از آن vercel integration add upstash/upstash-kv پایگاه Redis را میسازد و KV_REST_API_URL و KV_REST_API_TOKEN را تزریق میکند |
مستند شد و با پایگاه دادهی واقعی تست شد |
| دامهای تست |
pkill -f خط فرمان خودش را هم میگرفت و شل را میکشت؛ __init__.py خالی در بعضی آپلودها حذف میشود؛ ping به 192.0.2.1 پشت بعضی شبکهها موفق میشود |
در تستها و اسکریپتها کنار گذاشته شدند |
-
خودکار (۲۸ تست، CI روی پایتون ۳٫۱۲ تا ۳٫۱۵): ماشین حالت؛ جریانهای موتور (down، up، degraded، یادآوری، گواهی، maintenance، برچسب، interval)؛ قاعدههای تنظیمات؛ ارسالکنندههای HTTP؛ بار OpenTelemetry؛
.env؛ بلوک cron؛init؛ انتخاب دستور update؛ checksum دانلود shoutrrr؛ handler مربوط به Vercel با Redis ساختگی؛ Worker و bundle ساختهشدهی Worker با ماژولهای ساختگی محیط اجرا. -
دستی روی سرویسهای واقعی: خطلولهی انتشار (نسخه، تگ، GitHub release، آپلود در PyPI و
uv tool install pulsecheckcli)، یک Worker کلودفلر (deploy، اجرای cron، وضعیت در KV، رسیدن وبهوک، لاگها) و یک تابع Vercel با Upstash Redis (deploy، احراز هویت، خواندن تنظیمات، ماندن وضعیت بین اجراها،/api/statusو اجرای shoutrrr). -
با سرویس واقعی تست نشده: endpointهای
/statusو/runدر Worker؛crontabروی یک سیستم واقعی (یک نمونهی ساختگی به کار رفت)؛ رسیدن پیام تلگرام، اسلک و … از ارسالکنندههای HTTP داخلی (فقط شکل بار وshoutrrr verifyآدرسها بررسی شد).
پروژهی PyPI اسمش pulsecheckcli است و دستورها pulsecheck و اسم کوتاه pck هستند. انتشار خودکار است و از Conventional Commits تبعیت میکند (feat نسخهی minor میسازد، fix و perf نسخهی patch). وقتی CI روی main سبز شود، publish.yml اجرا میشود و python-semantic-release مقدار version در pyproject.toml را بالا میبرد، CHANGELOG.md را بهروز میکند، تگ vX.Y.Z را میزند و GitHub release را میسازد. بعد با Trusted Publisher (environment به اسم pypi، بدون توکن) بسته را build و در PyPI منتشر میکند. هیچ مرحلهی دستیای لازم نیست؛ با Run workflow هم میشود سطح افزایش نسخه را اجباری کرد. نسخهی 0.1.0 در ۱۱ اکتبر ۲۰۲۶ همینطور منتشر شد. فایل CONTRIBUTING.md در مخزن را ببینید.