ساختن افزونه
این صفحه برای توسعهدهندههایی است که میخواهند برای نکسورا افزونه بنویسند: برنامهای جدا که از راه شبکه با پنل کار میکند، با یک توکن API محدود، رویدادهای امضاشدهٔ پنل و یک مانیفست که میگوید به چه چیزی نیاز دارد. این صفحه بخشهای یک افزونه و ابزارهایی را که بیشترشان را برایتان مینویسند مرور میکند. فیلدهای مانیفست در مانیفست افزونه آمدهاند.
چه چیزی میسازید
افزونه برنامهٔ مستقلی است: سرویس یا کانتینر خودش، رابط خودش، پایگاه دادهٔ خودش. پنل هرگز کدش را اجرا نمیکند و هیچکدام از دادههایش را ذخیره نمیکند. افزونه چهار بخش دارد که پنل با آنها سروکار دارد:
| بخش | چه میکند |
|---|---|
| مانیفست | nexora-addon.json: افزونه کیست، چه میخواهد، چطور نصب میشود. افزونهٔ در حال اجرا آن را در /.well-known/nexora-addon.json سرو میکند. |
| مسیر setup | جایی که پنل هنگام ثبت، توکن و کلید وبهوک را همراه با کد ادعا تحویل میدهد. |
| مسیر webhook | جایی که پنل رویدادهای درخواستی افزونه را، هر کدام امضاشده، میفرستد. |
| مسیر health | تا وقتی افزونه سالم است 200 جواب میدهد. پنل هر دقیقه میپرسد. |
بقیه (صفحهها، API و پایگاه دادهتان) را خودتان طراحی میکنید. اینکه پنل با افزونه چطور رفتار میکند در پلتفرم افزونه آمده است.
کیت و قالب
دو مخزن عمومی بیشتر اینها را برایتان مینویسند.
- کیت افزونه، ماژول Go
github.com/nexora-vpn/addon-kit، با مجوز Apache-2.0، پس افزونهای که روی آن ساخته شود میتواند متنباز یا بسته باشد. پنل مانیفستها را با همین کد اعتبارسنجی میکند. - قالب،
github.com/nexora-vpn/addon-template: یک افزونهٔ کامل و حداقلی با فایل compose، نسخهٔ باینری و اسکریپت نصب. با کد ادعا ثبت میشود، رویداد دریافت میکند، به بررسی سلامت جواب میدهد و صفحهای با تعداد حسابهای پنل نشان میدهد.
| بستهٔ کیت | چه میکند |
|---|---|
manifest | مانیفست را میخواند، اعتبارسنجی و امضا میکند. |
addon | نیمهٔ در حال اجرا: مانیفست را سرو میکند، اطلاعات ورود را هنگام ثبت میگیرد، تحویل رویدادها را تأیید میکند، به بررسی سلامت جواب میدهد، جوابهای نصب را میخواند. |
panel | کلاینت کوچکی برای API پنل با توکن افزونه، کلیدهای idempotency و انتظار روی 429. |
auth | ورود مدیر خود افزونه: گذرواژههای هششده، نشستها، عامل دوم TOTP، محدودیت روی تلاشهای ناموفق. |
telegram | کلاینت کوچکی برای Telegram Bot API، از راه پراکسی یا آینهٔ Bot API؛ بله را هم پشتیبانی میکند. |
web | بخش مدیریت را زیر مسیر پایهٔ نصب و صفحههای عمومی را بیرون از آن سرو میکند، با HTTPS از پنل، ACME یا گواهی خودامضا. |
cmd/nexora-addon | check، keygen، sign و verify برای مانیفست؛ sign-sums و verify-sums برای checksumهای یک نسخه. |
کوچکترین افزونه:
raw, _ := os.ReadFile("nexora-addon.json")
a, err := addon.New(addon.Config{Manifest: raw, DataDir: "data"}.FromEnv())
if err != nil { log.Fatal(err) }
a.OnEvent(func(e addon.Event) { log.Printf("%s %s", e.Event, e.Data) })
mux := http.NewServeMux()
a.Mount(mux) // the manifest, setup, webhook and health paths
log.Fatal(http.ListenAndServe(":8090", mux))قالب را مال خودتان کنید
slug،name،publisher، دامنههای دسترسی و رویدادها را درnexora-addon.json، وSLUG،REPOوBINرا در بالایinstall.shتغییر دهید.- سؤالهایی را که نصبتان لازم دارد زیر
install.optionsاعلام کنید (مانیفست افزونه). - دادههای خودتان را در پایگاه دادهٔ خودتان نگه دارید، و پنل را با توکن بخوانید.
- یک نسخه تگ بزنید. workflow انتشار قالب باینریها، checksumها، نسخه و ایمیج را میسازد. بعد checksumها را امضا کنید (پایینتر).
ثبت با کد ادعا
افزونه ثبتنشده شروع میکند. نصب یک کد ادعای یکبارمصرف در NEXORA_CLAIM_CODE به آن میدهد؛ اگر دستی و بدون کد اجرا شود، کیت یک کد انتخاب و در لاگ چاپ میکند.
- پنل مانیفست را از آدرس افزونه میخواند و امضایش را بررسی میکند.
- آنچه افزونه میخواهد را به اپراتور نشان میدهد: هر دامنهٔ دسترسی با هدفش، نرخ درخواست، و هر رویداد.
- با تأیید، پنل دقیقاً همان توکن و وبهوک را میسازد و آنها را همراه با کد ادعا به مسیر
setupافزونه میفرستد. افزونه آنها را فقط با کد خودش میپذیرد، پس کد اشتباه هیچ چیزی به جا نمیگذارد.
کیت اطلاعات ورود را در پوشهٔ دادهاش نگه میدارد و وقتی رسیدند تابعی را که با OnSetup تعیین کردهاید فرا میخواند. Credentials() آنها را برمیگرداند، یا پیش از ثبت nil؛ ClaimCode() کدی را که هنوز منتظر است برمیگرداند.
پنل فقط مانیفستی را با کد ادعا ثبت میکند که با کلیدی مورد اعتمادش امضا شده باشد: کلید نکسورا، یا کلید توسعهدهندهای که دایرکتوری تضمینش میکند. در زمان توسعه، افزونهتان را به جای آن دستی ثبت کنید (افزونهها ← افزودن افزونهٔ خودتان): دسترسیهای توکن و رویدادهای وبهوک را خودتان انتخاب میکنید، و پنل توکن و کلید امضای وبهوک را یک بار برای تنظیمات برنامهتان نشان میدهد.
حذف و نصب تازه
وقتی اپراتور افزونه را حذف میکند، پنل اول panel.addon_removed را برایش میفرستد، هر رویدادی که خواسته باشد، و بعد توکن و وبهوکش را پاک میکند. آن موقع Forget() را فرا بخوانید تا بشود دوباره ثبتش کرد.
کد ادعای تازه روی دادههای یک نصب قبلی (که با نگه داشتن دادهها حذف و بعد دوباره نصب شده) یک نصب تازه است. کیت ثبت قبلی را کنار میگذارد، و NewInstall() به افزونهتان میگوید جوابهایی را که در حالت عادی فقط یک بار استفاده میکند، مثل گذرواژهٔ اولین مدیر، اعمال کند. وقتی ذخیره شدند NewInstallApplied() را فرا بخوانید. بهروزرسانی کد ادعا را نگه میدارد و هرگز نصب تازه نیست.
دریافت رویدادها
پنل هر رویدادی را که افزونه خواسته به مسیر webhook آن میفرستد. هر تحویل X-Nexora-Signature: t=<unix>,v1=<hex> دارد، که hex همان HMAC-SHA256 روی <t>.<body> با کلید وبهوک است، و یک شناسهٔ X-Nexora-Delivery که در هر تلاش دوباره یکسان میماند.
کیت امضا را بررسی میکند و تحویلی را که بیش از پنج دقیقه اختلاف زمانی دارد رد میکند، هر رویداد تأییدشده را برای هر شناسهٔ تحویل یک بار به handler شما میدهد، و به پنل جواب میدهد:
OnEventبرای handlerی که نمیتواند شکست بخورد.OnEventErrبرای handlerی که میتواند: یک خطا یا panic با500جواب داده میشود، و پنل رویداد را دوباره تحویل میدهد.
مجموعهٔ تحویلهای دیدهشده در حافظه است، پس تلاش دوباره بعد از راهاندازی مجدد دوباره به handler داده میشود. اثرهای handler را idempotent نگه دارید. نام رویدادها و محتوایشان در فهرست رویدادها آمده است؛ اینکه پنل چطور تحویلشان میدهد در گذرگاه رویداد.
هر رویداد با یک دامنهٔ دسترسی داده میشود: دامنهای که فهرست رویدادهای پنل (GET /api/events) برایش نام میبرد، یا :write همان دامنه، باید میان scopes مانیفست شما باشد، وگرنه پنل مانیفست را رد میکند.
فراخوانی پنل
توکن دقیقاً با دامنههایی که به شما داده شده عمل میکند، با نرخ درخواستی که مانیفستتان خواسته (۱۲۰ در دقیقه، مگر اینکه چیز دیگری بگوید). کلاینت panel کیت با API پنل به زبان JSON حرف میزند، اگر Idempotency-Key بدهید آن را میفرستد، و روی 429 یک بار صبر میکند:
c := a.Credentials()
client := &panel.Client{Base: c.Panel.URL, Token: c.Token}
var list struct{ Total int `json:"total"` }
err := client.Get(ctx, "/users?limit=1", &list)Me() بررسی ارزانی است که اطلاعات ورود هنوز کار میکنند. مسیرها و بدنههایشان در توضیح API پنل آمدهاند (مرجع API). پنل هیچ چیزی از شما نگه نمیدارد: دادههای خودتان، و دادههایی را که کنار کاربرها نگه میدارید، در پایگاه دادهٔ خودتان ذخیره کنید.
سلامت
به مانیفست یک مسیر health بدهید که تا وقتی افزونه میتواند کارش را انجام دهد 200 جواب دهد. کیت این جواب را برایتان میدهد. پنل هر دقیقه میپرسد؛ دو شکست پشت سر هم افزونه را ناسالم علامت میزند و panel.addon_unhealthy را منتشر میکند، و 200 بعدی panel.addon_recovered را. از افزونهٔ بدون مسیر سلامت هرگز پرسیده نمیشود.
گزینههای نصب
هر سؤال در install.options یک فیلد در فرم نصب پنل و یک flag در اسکریپت نصب شما میشود. جوابها به صورت متغیرهای محیطی NEXORA_OPT_<KEY>، کنار NEXORA_PANEL_URL و NEXORA_CLAIM_CODE، به افزونه میرسند؛ هر کدام را با addon.Option("key") بخوانید. نوع گزینهها و قاعدههایشان در مانیفست افزونه آمده است.
دو نوع، روش نصب شما توسط پنل را تغییر میدهند:
passwordگذرواژهٔ مدیر شما میشود. پنل در فرمش آن را به ۱۰ نویسه و ۷۲ بایت محدود میکند، همان قاعدهٔ بستهٔauthکیت، پس گذرواژهای که افزونهتان رد میکند هرگز به آن نمیرسد.pathمسیر پایهای است که بخش مدیریت شما زیر آن سرو میشود. پنل یک مسیر تصادفی پیشنهاد میدهد و جواب را در آدرسی که شما را با آن ثبت میکند میگذارد. بخش مدیریت، API و مسیرهایی را که پنل فرا میخواند (مانیفست،setup، وبهوک،health) زیر آن سرو کنید، و صفحههای مشتریهایتان را بیرون از آن (web.Mount).
HTTPS و گواهیها از پنل
بستهٔ web کیت آدرس عمومی شما را روی تنها پورت نصب با HTTPS سرو میکند، با یکی از اینها:
- گواهیای از پنل: گواهیای که اپراتور از انبار پنل انتخاب کرده است. افزونه آن را هر چند دقیقه از پنل میگیرد (پیش از ثبت با کد ادعا، بعد از آن با توکن) و یک نسخه نگه میدارد، پس حتی وقتی پنل از کار افتاده با HTTPS بالا میآید. به پورت ۴۴۳ جداگانهای نیاز ندارد، پس چند افزونه میتوانند با پنل روی یک سرور باشند.
- ACME با TLS-ALPN-01 روی پورت ۴۴۳، یا با HTTP-01 روی هر پورتی که مرجع صدور روی پورت ۸۰ میپرسد.
- گواهی خودامضا برای یک آدرس IP. اپراتور اثر انگشتش را در صفحهٔ تأیید پنل تأیید میکند.
همچنین بررسی میکند که آدرس عمومی به همین برنامه میرسد. اینکه اپراتور چطور میان اینها انتخاب میکند در دایرکتوری افزونهها آمده است.
نسخهها و امضا
هر نسخه باینریهایی را که در install.binary مانیفست نام برده شدهاند، ایمیج install.docker، یک SHA256SUMS از همهٔ فایلها، و SHA256SUMS.sig را منتشر میکند که با همان کلیدی امضا شده که مانیفست را امضا میکند:
go install github.com/nexora-vpn/addon-kit/cmd/nexora-addon@latest
nexora-addon check nexora-addon.json # would a panel accept it?
nexora-addon keygen -out addon.key # once: your signing key
nexora-addon sign -key addon.key nexora-addon.json > signed.json
nexora-addon sign-sums -key addon.key SHA256SUMS > SHA256SUMS.sig
gh release upload v1.2.3 SHA256SUMS.sigkeygen کلید عمومی شما را چاپ میکند و هرگز روی فایل کلید موجود نمینویسد. کلید خصوصی را بیرون از مخزن نگه دارید. امضا همهٔ فیلدهای مانیفست را پوشش میدهد، پس بعد از هر تغییری، از جمله نسخهٔ هر انتشار، دوباره امضا کنید.
پنل فقط وقتی افزونهای را با SSH نصب، بهروز یا حذف میکند که install.sh آن نسخه، و برای نصب با اسکریپت باینریاش، با یک SHA256SUMS امضاشده با کلیدی که مانیفست همان نسخه را امضا کرده مطابقت داشته باشند. نسخهٔ بدون SHA256SUMS.sig همچنان با دستورش قابل نصب است. جزئیات در مانیفست افزونه آمده است.
فهرست شدن
افزونهها در addons.nexora-panel.org به صورت رسمی، تأییدشده یا غیررسمی فهرست میشوند (دایرکتوری افزونهها). برای فهرست شدن، مانیفست را با کلید خودتان امضا کنید (nexora-addon keygen، sign) و در دایرکتوری یک pull request باز کنید. nexora-addon.json را در ریشهٔ مخزن عمومیتان نگه دارید (برای افزونهٔ بسته، مخزن عمومی توزیعش): دایرکتوری و ویزارد نصب آن را همانجا میخوانند.
