یاس

نسخه‌ها

امکانات تازه و بهبودهای هر نسخه‌ی یاس را اینجا دنبال کنید.

v0.4.1آخرین نسخه

نسخه v0.4.1

نسخهٔ 0٫4٫1

تاریخ انتشار: 1 شهریور 1405
انتشاری اصلاحی، چند ساعت پس از نسخهٔ 0٫4٫0، برای خطایی که درست از لحظهٔ انتشار آن نسخه صفحهٔ «نسخه‌ها» را از کار انداخته بود.
---

صفحهٔ «نسخه‌ها» دیگر خطای CORS نمی‌دهد

از همان لحظه‌ای که نسخهٔ 0٫4٫0 روی سرور نشست، نشانی عمومی «نسخه‌ها» به هر بازدیدکننده خطای CORS پاسخ می‌داد و پشت آن، فهرستی خالی. هر دو نشانه، یک ورودی در حافظهٔ نهان بودند.

آنچه رخ می‌داد

کتابخانهٔ مدیریت CORS بر روی هر پاسخ، سرآیند Vary: Origin را می‌گذارد؛ اما سرآیند Access-Control-Allow-Origin را تنها زمانی می‌نویسد که خودِ درخواست، سرآیند Origin را همراه داشته باشد و در غیر این صورت زودتر بازمی‌گردد. این رفتار درست است: درخواستی که Origin ندارد، به آن سرآیند هم نیازی ندارد.
این درستی دقیقاً در یک حالت از بین می‌رود: وقتی همان پاسخ Cache-Control: public هم باشد و حافظهٔ نهان مشترکی که مقابل سرور نشسته، Vary را نادیده بگیرد. شبکهٔ توزیع محتوای مقابل api.yasapp.ir دقیقاً چنین حافظه‌ای است.
نتیجه این شد که نخستین درخواست پس از بالا آمدن سرویس، تکلیف همه را روشن کرد. آن درخواست Origin نداشت — یک خزندهٔ موتور جست‌وجو، یک بررسی سلامت، یک درخواست ساده — بنابراین نسخه‌ای که در حافظهٔ نهان نشست هیچ سرآیند CORS نداشت، و از آن پس همان نسخه به تمام مرورگرها تحویل داده شد. مرورگر هم خواندن آن را مسدود کرد و صفحه، شاخهٔ «بارگذاری نشد» خودش را نشان داد.
فهرست خالی هم مسافر همان ورودی بود: آن نسخه پیش از انتشار نخستین سند ذخیره شده بود، و شبکهٔ توزیع محتوا آن را بسیار بیشتر از پنج دقیقه‌ای که به آن داده شده بود نگه داشت.

چرا این خطا فقط در محیط عملیاتی دیده می‌شد

این یک مسابقه بود که برنده‌اش هرکسی بود که زودتر به حافظهٔ نهانِ خالی می‌رسید — و تنها در محیط عملیاتی قابل باخت بود: روی دستگاه توسعه هیچ شبکهٔ توزیع محتوایی در کار نیست، و آزمودن مستقیم خودِ سرور هم از هر دو سو بی‌عیب به‌نظر می‌رسید.

راه‌حل

پاسخ این دو نشانی اصلاً به «چه کسی می‌پرسد» وابسته نیست: نه نشانهٔ دسترسی، نه کوکی، نه آموزشگاه، نه کاربر. یادداشت‌های منتشرشده و تعرفهٔ عمومی برای همه یک‌سان‌اند — و همین دلیلِ اصلی بود که از ابتدا اجازهٔ ذخیره در حافظهٔ نهان مشترک را گرفتند.
بنابراین این پاسخ‌ها دیگر بر پایهٔ Origin تغییر نمی‌کنند: سرآیند Access-Control-Allow-Origin: * را بدون قید و شرط می‌فرستند و Vary: origin از روی آن‌ها برداشته می‌شود، چون دیگر گزارهٔ درستی نیست. در نتیجه هر نسخه‌ای که در حافظهٔ نهان بنشیند، برای هر خواننده‌ای درست است، فارغ از اینکه کدام درخواست آن را پر کرده باشد.
استفاده از * تنها در نبود اطلاعات هویتی ایمن است و همین هم اجبار شده، نه فرض: پاسخی که کوکی یا سرآیند مربوط به اعتبارنامه‌ها را همراه داشته باشد، دست‌نخورده باقی می‌ماند. این رفتار همچنین برای هر نما جداگانه فعال می‌شود، نه با فهرستی از مسیرها در تنظیمات که بی‌سروصدا نشانی بعدی را هم در بر بگیرد.
نشانی عمومی تعرفه نیز دقیقاً همین ساختار و همین ایراد نهفته را داشت و در همین اصلاح پوشش داده شد؛ تا امروز صرفاً بخت با آن یار بود.
---

آنچه این نسخه حل نمی‌کند

شبکهٔ توزیع محتوا سرآیند max-age را نیز نادیده می‌گیرد. بنابراین برای آنکه سندی که همین حالا منتشر شده بی‌درنگ دیده شود، همچنان باید حافظهٔ نهان را از پنل آن سرویس پاک کرد. آموزش دادن «احترام به Vary» به آن سرویس، اصلاح درست و نهایی است — اما پس از این نسخه، انتشار نرم‌افزار دیگر به آن وابسته نیست.
---

اصلاح داخلی

مجموعه‌ای از آزمون‌های لایهٔ ذخیره‌سازی یک پوشهٔ موقت مشترک داشتند و هر کلاس در پایان کار خودش آن را پاک می‌کرد. در اجرای موازی، فرایندهای کارگر همان مسیر را به ارث می‌بردند و پاک‌سازی یک کلاس، فایل‌هایی را می‌برد که کارگر دیگری هنوز مشغول کار با آن‌ها بود.
این اشکال هر بار خود را به‌شکل آزمونی متفاوت نشان می‌داد و به همین دلیل بیشتر شبیه بی‌ثباتی تصادفی به‌نظر می‌رسید تا یک ایراد مشخص. اکنون هر کلاس پوشهٔ مخصوص خود را دارد. پیش از این اصلاح، سه اجرا از پنج اجرا شکست می‌خورد؛ پس از آن، هر پنج اجرا موفق است.

v0.4.0

نسخه v0.4.0

نسخهٔ 0٫4٫0

تاریخ انتشار: 1 شهریور 1405
بزرگ‌ترین انتشار یاس تا امروز؛ هشت مجموعه تغییر که سه سرویس تازه به سامانه می‌افزاید و چند مسیر قدیمی را از پایه بازنویسی می‌کند.
---

پیامک: الگوهای تأییدشده و خلاصهٔ گفتگو

هر پیامک، یک «الگو»ی ثبت‌شده

خط خدماتی در ایران تنها متن‌هایی را حمل می‌کند که پیش‌تر در پنل اپراتور ثبت و تأیید شده باشند؛ هر متن دیگری یا رد می‌شود یا روی خط تبلیغاتی می‌نشیند — خطی که فیلتر می‌شود، شب اجازهٔ ارسال ندارد و آخرین چیزی است که یک پیامک از آموزشگاه باید شبیه آن باشد.
این قاعده تا پیش از این تنها دربارهٔ رمز یک‌بارمصرف رعایت می‌شد و به‌اشتباه ویژگی خودِ رمز پنداشته شده بود. از این نسخه، تمام یازده متنی که سامانه ارسال می‌کند الگوی ثبت‌شده‌اند. نام پارامترها پیش از تماس با درگاه اعتبارسنجی می‌شود، بنابراین یک جای‌گذاری نادرست در همین فرایند خطا می‌دهد و دیگر با حفره‌ای خالی روی گوشی کسی نمی‌نشیند.

پنج پیام، یک پیامک

مدرّسی که پنج سطر در گفتگوی کلاس می‌نویسد، تا دیروز پنج پیامک جداگانه به تمام گوشی‌های آن کلاس می‌فرستاد: آموزشگاه پنج بار هزینه می‌داد و چهارصد نفر پنج بار برای خواندن یک حرف در پنج تکه منقطع می‌شدند.
اکنون پیام‌های یک فرستنده در یک اتاق در قالب یک بلوک گرد هم می‌آیند و سه دقیقه پس از سکوت فرستنده، یک‌جا سنجیده می‌شوند. آنچه ارسال می‌شود یک پیامک است: خلاصه‌ای در چهل نویسه، به‌همراه پیوندی به متن کامل در نشانی yasapp.ir/m/<کد>.

تصمیمِ «آیا این پیام ارزش پیامک دارد؟»

هیچ فراداده‌ای «سلام بچه‌ها» را از «کلاس فردا برگزار نمی‌شود» جدا نمی‌کند؛ فرستنده یکی است، ساعت یکی است و طول متن چیزی نمی‌گوید. آنچه این دو را از هم جدا می‌کند معنای جمله‌هاست، و این تنها کاری است که یک مدل زبانی هم ارزان و هم خوب انجام می‌دهد.
هنگامی که مدل پاسخ ندهد، پیش‌فرض «عادی» است — و این انتخاب عمدی است. دو گونه خطا در اینجا هم‌وزن نیستند: حدسِ «مهم» پول آموزشگاه را خرج می‌کند و چهارصد گوشی را برای «سلام بچه‌ها» به صدا درمی‌آورد، آن هم دقیقاً هر بار که درگاه از دسترس خارج است، یعنی همان زمانی که کسی حواسش نیست. حدسِ «عادی» اما چیزی را از دست نمی‌دهد: زنگ اعلان و بنر Push مانند همیشه و به‌ازای هر پیام ارسال می‌شوند. بلوک، کانال سوم است، نه تنها کانال.
هزینهٔ این تصمیم بر عهدهٔ اپراتور سکوست و هیچ ردیفی در صورت‌حساب آموزشگاه ندارد: این پردازش به‌نمایندگی از آموزشگاه تصمیم می‌گیرد که آیا اعتبار پیامکش خرج شود یا نه، و گرفتن هزینهٔ تصمیم در کنار هزینهٔ خودِ پیامک، سطری است که در هیچ صورت‌حسابی قابل دفاع نیست.
---

گفتگوی کلاس، حالا بخشی از پیام‌رسان است

کلاس زنده تا پیش از این گفتگوی مخصوص خود را داشت: کانال دادهٔ LiveKit، حباب‌های پیام مخصوص خود، جعبهٔ نوشتن مخصوص خود — و هیچ‌چیز ماندگار. سه ایراد داشت که در حقیقت یک ایراد بودند: پیاده‌سازی دومی از چیزی که سامانه پیش‌تر داشت.

  • کسی که بیست دقیقه دیر می‌رسید، پنجره‌ای خالی می‌دید.
  • عکس فرستاده نمی‌شد.
  • با پایان کلاس، گفتگو از میان می‌رفت.

از این نسخه، هر جلسه اتاق گفتگوی واقعی خود را دارد: با بالا آمدن کلاس ساخته و با پایان آن بایگانی می‌شود. پنجرهٔ «گفتگو» در کلاس، همان گفتگویی است که در «اطلاع‌رسانی» باز می‌شود — همزمان در هر دو جا، با تمام تاریخچه، و هفتهٔ بعد هم سرِ جایش.

رفع یک اشکال پنهان در سوکت‌ها

لایهٔ کانال نادرست انتخاب شده بود. RedisChannelLayer تحویل پیام را با نظرسنجی دوره‌ای انجام می‌دهد و در نسخه‌های جدید کتابخانهٔ redis، هر پنج ثانیه استثنایی پرتاب می‌شد که مصرف‌کننده را از پا درمی‌آورد. نتیجه این بود که تمام سوکت‌های گفتگوی سامانه، تمام روز، تقریباً هر ده ثانیه یک‌بار قطع و دوباره وصل می‌شدند — بی‌آنکه چیزی روی صفحه آن را نشان دهد.
پیام‌ها همچنان می‌رسیدند (کارخواه با مکان‌نما خود را به‌روز می‌کرد)، اما ترافیک زودگذر از دست می‌رفت: نشانگر «در حال نوشتن»، حضور، و رسید خواندن. به همین دلیل این اشکال سال‌ها به‌شکل «نشانگر تایپ کار نمی‌کند» دیده می‌شد و هرگز به‌شکل یک مشکل سوکت.
---

«دعوت گروهی»

دعوت یک کلاس، شماره به شماره، کاری است که کسی انجام نمی‌دهد؛ به همین دلیل فهرست‌ها روی کاغذ می‌ماندند. اکنون همان فایل اکسلی که آموزشگاه از پیش نگه می‌دارد، به دعوت‌نامه تبدیل می‌شود.

فایل هرگز از مرورگر خارج نمی‌شود

از یک فهرست عضویت تنها یک ستون شمارهٔ تلفن لازم است. باقی فایل — نام‌ها، کد ملی، شمارهٔ والدین، یادداشت‌های شهریه — فهرست خصوصی یک نفر است، و فرستادن تمام آن به سرور تا سرور همه را جز یک ستون دور بریزد، یعنی این سکو آن فهرست را ذخیره می‌کند، پشتیبان می‌گیرد و باید پاسخ‌گوی آن باشد.
بنابراین فایل xlsx یا csv در همین مرورگر خوانده می‌شود، کاربر جدولِ استخراج‌شده را می‌بیند و ستون را خودش انتخاب می‌کند، و تنها شماره‌هایی که تأیید کرده ارسال می‌شوند.
پشتیبانی از کدگذاری نیز در نظر گرفته شده است: اکسلِ ویندوز فارسی فایل CSV را با windows-1256 می‌نویسد و همان فایل اگر UTF-8 خوانده شود، نام‌ها را ناخوانا و شماره‌ها را سالم تحویل می‌دهد — خطایی که در نگاه اول شبیه مشکل قلم به‌نظر می‌رسد.

کاری که با بستن مرورگر از بین نمی‌رود

چهارصد دعوت‌نامه یعنی چهارصد تراکنش اعتباری و چهارصد رفت‌وبرگشت با درگاه پیامک. انجام این کار درون همان درخواستی که آن را آغاز کرده، یعنی مرورگر منتظر می‌ماند، پراکسی منصرف می‌شود و — که مهم‌تر است — هیچ‌کس نمی‌داند کار تا کجا پیش رفته.
اکنون درخواست فهرست را ثبت می‌کند و بازمی‌گردد؛ یک کارگر آن را قطره‌قطره پیش می‌برد و هر گیرنده را به‌محض انجام علامت می‌زند؛ و صفحه شمارنده را دنبال می‌کند. بستن زبانه چیزی را از بین نمی‌برد. اگر ظرف اجرا از کار بیفتد، کاری زمان‌بندی‌شده کار نیمه‌مانده را برمی‌دارد و از روی همان فهرست ادامه می‌دهد، نه از ابتدا — بنابراین هیچ شماره‌ای دو بار دعوت نمی‌شود.
---

«سندها» و صفحهٔ عمومی «نسخه‌ها»

فهرست تغییراتی که درون بستهٔ برنامه ساخته شده باشد، هرگز نمی‌تواند انتشاری را توصیف کند که خودش با آن منتشر شده است.
بنابراین «نسخه‌ها» داده است، نه فایل: اپراتور در کنسول مدیریت متن Markdown می‌نویسد، دکمهٔ انتشار را می‌زند، و نشانی yasapp.ir/releases همان لحظه آن را نشان می‌دهد — بدون ساخت، بدون تگ، بدون استقرار.
یک جدول برای همه‌چیز، چون یادداشت انتشار، دستورالعمل داخلی و پیش‌نویس صفحهٔ «دربارهٔ ما» یک چیزند: عنوان، متن Markdown، و اینکه آیا عموم اجازهٔ دیدنش را دارند. آنچه آن‌ها را از هم جدا می‌کند برچسب است. امروز تنها برچسب version نقش‌آفرین است و صفحهٔ عمومی بر همان صافی می‌گذارد؛ بنابراین یادداشت‌های شخصی اپراتور در همان ویرایشگر می‌مانند و هرگز به صفحهٔ عمومی راه نمی‌یابند.
متن دقیقاً همان‌گونه که نوشته شده ذخیره می‌شود و هیچ HTML‌ای در سرور تولید نمی‌گردد؛ صفحهٔ عمومی آن را به‌صورت گره‌های React نمایش می‌دهد. بنابراین پرسش «اگر اپراتور یک <script> بچسباند چه؟» اصلاً پاسخی برای اشتباه‌شدن ندارد — برچسب در متن، متن است و متن هم بیرون می‌آید.
---

کنسول مدیریت روی گوشی

تمام صفحه‌های کنسول اپراتور دسکتاپ را فرض می‌گرفتند: ریلی ثابت در کنار صفحه، جدولی که باریک نمی‌شد، و بدنه‌ای که زیر 768 پیکسل به‌پهنا می‌لغزید. کنسول اما جایی است که سکو در ساعات غیراداری از آن مراقبت می‌شود — یعنی دقیقاً همان وقتی که تنها صفحهٔ در دسترس، گوشی است.
منو اکنون یک بار نوشته و دو بار نمایش داده می‌شود: ریل در دسکتاپ و کشوی همبرگری در گوشی. نقطهٔ شکست در هر دو جهت در CSS تعریف شده است، نه با اندازه‌گیری پهنا در جاوااسکریپت — روشی که در نخستین ترسیم، برای یک فریم، نسخهٔ نادرست را نشان می‌دهد.
---

فرم‌های عمومی ثبت‌نام

کسی که کد QR چسبانده‌شده روی تابلوی اعلانات را می‌خواند، تا دیروز مستقیم روی صفحهٔ ورود سکویی می‌نشست که نامش را نشنیده بود، آن هم متصل به آموزشگاهی که صفحه نامش را نمی‌برد. رایج‌ترین پاسخ به این وضعیت، بستن زبانه است.
اکنون پیش از هر چیز یک صفحه به او پاسخ می‌دهد: نام و نوع آموزشگاه، تصاویر چند دورهٔ آن، و سه عدد کلیدی. تنها دوره‌های قابل‌عرضه نمایش داده می‌شوند — کلاس بایگانی‌شده یا داخلی چیزی برای تبلیغ نیست.
در صفحهٔ مدیریت فرم‌ها نیز نشانی چاپ‌شده حذف شد. کلیدی مبهم و شصت‌نویسه‌ای در یک قاب، نه خوانده می‌شود، نه به خاطر می‌ماند و نه کسی آن را تایپ می‌کند؛ همه فقط رونوشت می‌گیرند. پیوند اکنون درون کد QR است و خودِ قاب یک دکمهٔ بزرگ رونوشت‌گیری است.
---

ظاهر و تجربهٔ کاربری

  • ردیف عملیات دیگر پایین صفحه گم نمی‌شود. دکمهٔ «ذخیره» تنها چیزی است که کاربر برای آن به صفحه آمده، و در صفحه‌ای بلندتر از نمایشگر هرجا که آخرین پرسش تمام می‌شد می‌نشست — در صفحهٔ ساخت نقش، یک صفحه‌ونیم پایین‌تر از دید، بی‌آنکه چیزی از وجودش خبر دهد. اکنون نواری ثابت در پای صفحه است. ترتیب درون آن قطعی است: راه خروج نخست، عملِ نهایی آخر.
  • بریدگی بالای نمایشگر و نوار خانهٔ گوشی، دقیقاً یک بار محاسبه می‌شوند. پیش از این در کلاس زنده، دکمه‌های «قطع میکروفون» و «خروج از کلاس» زیر نوار خانه پنهان می‌شدند.
  • قلم Poppins برای بخش لاتین رابط اضافه شد و پیش از Peyda تعریف شده است تا متن انگلیسی آن را بگیرد و فارسی — که Poppins نویسه‌ای برایش ندارد — به قلم قبلی برسد. مجموعهٔ آیکون‌های برنامهٔ نصب‌شده نیز بازتولید شد.
  • گام پایانی فرم‌های چندمرحله‌ای، پاسخ‌ها را به‌شکل کارت‌هایی نمایش می‌دهد که با فشردن هرکدام می‌توان همان پاسخ را اصلاح کرد.
  • کارت هر نقش دوازده دسترسی را نشان می‌دهد و باقی را پشت یک تراشه جمع می‌کند.
  • برگه‌های حضور و غیاب، «کلاس» یا «جلسه» را همان‌طور می‌نویسند که در سرفصل ثبت شده است.

---

یاس‌یار

بستهٔ دانش دستیار برای تمام آنچه در این نسخه افزوده شد بازتولید شد. همچنین فهرست تب‌های داشبورد که پیش‌تر ثابت و دستی نوشته شده بود، اکنون از خودِ منوی برنامه استخراج می‌شود — پیش از این، تب «پیام‌ها» افزوده شده بود اما آن فهرست به‌روز نشده بود و یاس‌یار دربارهٔ تبی که مقابل چشم پرسشگر بود پاسخ می‌داد «چنین تبی نداریم».

v0.3.2

نسخه v0.3.2

نسخهٔ 0٫3٫2

تاریخ انتشار: 30 مرداد 1405
انتشاری کوچک و کاملاً اصلاحی، دربارهٔ یک قابلیت که هرگز در محیط عملیاتی کار نکرده بود.
---

«ثبت چهره» بالاخره روی سرور کار می‌کند

تشخیص چهره تا امروز روی yasapp.ir هرگز کار نکرده بود. پوشهٔ مدل‌های موتور تشخیص در تنظیمات Git نادیده گرفته شده بود، بنابراین سه فایل ONNX — در مجموع 102 مگابایت — تنها روی همان دستگاهی وجود داشتند که به آن منتقل شده بودند. هیچ‌چیز آن‌ها را بازنمی‌گرداند: نه Dockerfile آن‌ها را دریافت می‌کرد، نه حجمی آن‌ها را سوار می‌کرد و نه متغیری مسیرشان را تغییر می‌داد. در نتیجه هر تلاش برای ثبت چهره با خطای 503 پاسخ می‌گرفت.

و آن خطا هرگز به کاربر نمی‌رسید

این نکته، اشکال را ماه‌ها پنهان نگه داشته بود. نشانی api.yasapp.ir پشت یک CDN قرار دارد که خطاهای سرور را با صفحهٔ HTML خودش جایگزین می‌کند — صفحه‌ای که هیچ سرآیند CORS ندارد. بنابراین مرورگر نمی‌توانست آن را بخواند، کارخواه شاخهٔ «پاسخی دریافت نشد» را برمی‌گزید، و نبودِ یک فایل مدل، با پیام «ارتباط با سرور برقرار نشد» اعلام می‌شد — یعنی تنها خطایی که به کاربر می‌گوید اتصالش را بررسی کند، همان چیزی بود که بررسی اتصال هرگز حلش نمی‌کرد.

چگونه حل شد

مدل‌ها اکنون با Git LFS نگهداری می‌شوند، نه به‌صورت فایل معمولی و نه با دریافت هنگام اجرا. دلیل این انتخاب مهم است: این‌ها وزن‌های کالیبره‌شده‌اند و آستانهٔ 0٫40 در تنظیمات موتور دقیقاً بر اساس همین فایل‌ها اندازه‌گیری شده است؛ جابه‌جا کردن یکی بدون دیگری آن عدد را بی‌معنا می‌کند.
خط ساخت نیز آن‌ها را پشت یک حافظهٔ نهان دریافت می‌کند. دریافت مستقیم در هر اجرا، یک‌دهم سهمیهٔ ماهانهٔ رایگان پهنای‌باند LFS را مصرف می‌کرد و ماهی که این سهمیه تمام می‌شد، همان ماهی بود که ساخت تصویر سرویس پشتیبان از کار می‌افتاد.
---

دو اصلاح مرتبط

فایل نشانگرِ دریافت‌نشده، یعنی «مدل موجود نیست»

بررسی موجود بودن مدل صرفاً می‌پرسید «آیا فایلی هست؟» — و نسخه‌ای که بدون git-lfs رونوشت گرفته شده باشد، دقیقاً همان‌جا یک فایل متنی 130 بایتی می‌گذارد. آن بررسی موفق می‌شد، موتور خود را آماده اعلام می‌کرد، و نخستین درخواست در عمق کتابخانهٔ اجرا با خطای تجزیهٔ ساختار داده از کار می‌افتاد؛ خطایی که نام هیچ‌چیز را نمی‌برد.
اکنون خواندن نخستین سطر فایل — که هزینه‌ای در برابر بارگذاری 90 مگابایت ندارد — این حالت را به همان مسیر ساده و آشنای «مدل‌ها نصب نیستند» بازمی‌گرداند: آزمون‌ها از آن می‌گذرند و پاسخ 503 جمله‌ای همراه دارد که اپراتور می‌تواند بر اساس آن کاری بکند.

چیزی که سامانه نمی‌تواند انجام دهد، پیشنهاد نمی‌شود

کارت ثبت چهره اکنون از نقطهٔ سلامت موتور می‌پرسد که این استقرار واقعاً چه چیزی را می‌تواند تشخیص دهد، و اگر پاسخ «هیچ» بود، همان را می‌گوید — به‌جای آنکه دکمهٔ دوربینی را نشان دهد که پایانش خطای 503 در لباس یک خطای اتصال است.
حذف چهرهٔ ثبت‌شده در هر حالت در دسترس می‌ماند: پس گرفتن رضایت نباید به همان چیزی وابسته باشد که آن رضایت را مصرف کرده است.

v0.3.0

نسخه v0.3.0

نسخهٔ 0٫3٫0

تاریخ انتشار: 30 مرداد 1405
نخستین انتشار ویژگی‌محور پس از استقرار خط انتشار تازه: جست‌وجوی سراسری، مجموعهٔ کامل آیکون‌ها و راهنمای نصب، و تصویر شاخص برای کلاس‌های ضبط‌شده.
---

جست‌وجوی سراسری

یک جعبهٔ جست‌وجو که هم‌زمان دو چیز را می‌کاود:

  • خودِ برنامه — صفحه‌ها، عملیات، و دسترسی‌هایی که کاربر واقعاً در اختیار دارد.
  • محتوا — کلاس‌ها، افراد، آزمون‌ها، تکالیف، ویدیوها و آموزشگاه‌ها.

هر پرسشی که این جعبه نتواند پاسخ دهد، به‌جای نمایش صفحهٔ خالی، به یاس‌یار سپرده می‌شود.
این جست‌وجو محدودیت نرخ مستقل خود را دارد (600 درخواست در ساعت). دلیلش ساده است: جعبه با تأخیر کوتاه به تایپ واکنش نشان می‌دهد، پس هر جست‌وجو در عمل چند درخواست است و هزینهٔ هرکدام چند پرس‌وجوی نمایه‌شده است، نه یک فراخوان پولی مدل. اگر همان سهمیهٔ 120 تایی یاس‌یار را به اشتراک می‌گذاشت، یک بعدازظهر جست‌وجو می‌توانست تمام بودجهٔ درگاه را خرج کند.
---

مجموعهٔ آیکون و راهنمای نصب

آیکون‌ها از یک منبع تولید می‌شوند

یک مجموعهٔ تولیدشده که favicon، آیکون قابل‌ماسک اندروید، آیکون لمسی اپل و گونهٔ حالت تیره را پوشش می‌دهد. همه از روی فایل SVG ساخته می‌شوند، بنابراین منبع حقیقت یک تصویر برداری است و نه هشت فایل PNG که دستی خروجی گرفته شده‌اند.

راهنمای نصب می‌گوید دقیقاً چه چیزی را لمس کنید

اعلان نصب اکنون در مرورگرهایی که خودشان چنین امکانی دارند از اعلان بومی مرورگر استفاده می‌کند و در بقیه، گام‌های نوشته‌شده را نشان می‌دهد — از جمله حالت شیائومی، که در آن رابط MIUI تا وقتی «میان‌برهای صفحهٔ اصلی» برای مرورگر مجاز نشده باشد بی‌سروصدا هیچ آیکونی نمی‌سازد؛ حالتی که از بیرون دقیقاً شبیه نصب ناموفق به‌نظر می‌رسد.

اصلاح ارتفاع در برنامهٔ نصب‌شده

اندازه‌گیری ارتفاع دیگر بر پایهٔ window.innerHeight نیست: در برنامهٔ نصب‌شده، محتوا زیر نوار خانهٔ گوشی نقاشی می‌شود و هر چیزی که با آن ارتفاع اندازه‌گذاری شود، پشت آن نوار پنهان می‌ماند.
---

تصویر شاخص برای کلاس‌های ضبط‌شده

ضبط کلاس‌ها به‌صورت مسیرهای جداگانه انجام می‌شود و فایل واحدی وجود ندارد که بتوان تصویر شاخص را از آن برید؛ به همین دلیل تمام کلاس‌های ضبط‌شده در کتابخانه یک مستطیل خاکستری یکسان نشان می‌دادند.
اکنون تصویر شاخص از نخستین مسیر دوربین گرفته می‌شود — یعنی هرکس که در لحظهٔ آغاز کلاس روی صفحه بوده — و اشتراک‌گذاری صفحه در آخرین اولویت قرار دارد: یک اسلاید هم تصویری از درس است، اما اتاقی که آدمی در آن هست، ضبط را سریع‌تر قابل‌تشخیص می‌کند.
---

اصلاحات کارایی و ظاهر

  • توقف ترسیم دوبارهٔ بی‌مورد. مقدار زمینهٔ داک شناور از روی شیئی محاسبه می‌شد که در هر حرکت اشاره‌گر از نو ساخته می‌شد؛ در نتیجه یک کشیدن ساده، تمام مصرف‌کنندگان داک را دوباره ترسیم می‌کرد.
  • مجموعه‌ای از اصلاحات چیدمان و فاصله‌گذاری: گِرد شدن تنها گوشه‌های بالایی سرصفحهٔ دوره (حالا که روی یک پنل نشسته است)، هم‌ارتفاع شدن کارت‌های انتخاب دامنه، حذف دو جداکنندهٔ اضافی از کلید تغییر پوسته، و اصلاح فاصله‌ها در نوار کناری، فرم نقش و گروه چک‌باکس‌ها.

---

زیرساخت

  • مهلت زمانی مرحلهٔ استقرار از نیم‌ساعت به یک ساعت افزایش یافت. سرعت دریافت از فضای ذخیره‌سازی مقصد، اندازه‌گیری‌شده از سرور برنامه، حدود 650 کیلوبایت بر ثانیه است و تصویر سرویس پشتیبان 1٫8 گیگابایت است؛ بنابراین تنها مرحلهٔ دریافت بیست تا سی دقیقه طول می‌کشد و مهلت پیشین در عمل یک شیر یا خط بود. لایه‌هایی که پیش از قطع شدن دریافت شده باشند در حافظهٔ نهان می‌مانند، پس اجرای دوباره ادامه می‌دهد و از نو شروع نمی‌کند.
  • مستندسازی سرور رسانه بر اساس بازرسی مستقیم ماشین بازنویسی شد؛ محدودیت اصلی آن ماشین فضای دیسک است (54 گیگابایت از 68 گیگابایت اشغال).
v0.3.1

نسخه v0.3.1

نسخهٔ 0٫3٫1

تاریخ انتشار: 30 مرداد 1405
دو اصلاح کوتاه که هر دو یک ریشه دارند: تأخیر شبکه و حافظهٔ نهان مرورگر، هرکدام به‌تنهایی، می‌توانند اصلاحی را که منتشر شده است از رسیدن به کاربر بازدارند.
---

نشانه‌های دسترسی، سی ثانیه زودتر منقضی می‌شوند

بررسی اعتبار توکن می‌پرسید «آیا منقضی شده است؟»، در حالی که پرسش سودمند این است: «آیا وقتی به مقصد برسد هنوز معتبر خواهد بود؟»
روی خط ارتباطی این مرکز داده، توکنی که چند ثانیه اعتبار دارد تازه شمرده می‌شود، ارسال می‌گردد و در مقصد رد می‌شود. روی نقاط پایانی عمومی، این رد شدن بی‌صداست — چون پاسخ 200 به‌شکل ناشناس داده می‌شود، نه 401 — و در نتیجه کاربری که کاملاً وارد شده است، صفحه را در نقش یک بازدیدکنندهٔ غریبه می‌بیند.
---

index.html در هر بارگذاری اعتبارسنجی می‌شود

فایل index.html تنها فایل بدون امضای برنامه است و در عین حال همان فایلی است که نام تمام فایل‌های امضادار را در خود دارد؛ اما هیچ سرآیند Cache-Controlای برای آن تنظیم نشده بود. در نبود این سرآیند، مرورگرها طول عمری حدوداً یک‌دهم عمر سند برای خود اختراع می‌کنند.
نتیجه این بود که بازدیدکنندهٔ بازگشته، ساعت‌ها index.html انتشار قبلی را نگه می‌داشت. آن فایل نام فایل‌هایی را می‌برد که تصویر تازه دیگر آن‌ها را ارائه نمی‌کرد، و چون آن فایل‌ها در حافظهٔ نهان با نشان «تغییرناپذیر» ثبت شده بودند، تمام برنامهٔ قدیمی همچنان اجرا می‌شد و هیچ اصلاحی نمی‌توانست به کاربر برسد تا وقتی آن طول عمر اختراعی به پایان برسد.
این دقیقاً توضیح می‌دهد که چرا اصلاحِ منتشرشده در نسخهٔ 0٫3٫0 — دربارهٔ کاربری که با وجود ورود، صفحهٔ «لطفاً وارد شوید» را می‌دید — همچنان می‌توانست برای همان کسی که گزارشش کرده بود نامرئی باشد.
سرآیند انتخاب‌شده no-cache است و نه no-store: فایل در هر بارگذاری اعتبارسنجی می‌شود، اما ETag پاسخ 304 می‌دهد؛ بنابراین هزینه یک رفت‌وبرگشت است، نه بارگیری دوباره. فایل‌های امضادار همچنان «تغییرناپذیر» می‌مانند.

v0.2.1

نسخه v0.2.1

نسخهٔ 0٫2٫1

تاریخ انتشار: 29 مرداد 1405
نخستین انتشاری که از ابتدا تا انتها از خط انتشار تازه عبور کرد، و اصلاح یک اشکال که تنها هنگام همین عبور آشکار شد.
---

ثبت بیت اجرایی فایل‌ها

سامانهٔ فایلِ دستگاه توسعه توانایی نگهداری بیت اجرایی را ندارد. نتیجه این بود که فایل‌های اسکریپت — از جمله همان اسکریپتی که استقرار را انجام می‌دهد — بدون مجوز اجرا در مخزن ثبت می‌شدند و در محیطی که این مجوز معنا دارد، اجرا نمی‌شدند.
این حالت روی دستگاهی که عامل آن است به‌هیچ‌وجه دیده نمی‌شود؛ فقط در محیط ساخت خودش را نشان می‌دهد. مجوزها اکنون مستقیماً در مخزن ثبت شده‌اند.

v0.2.0

نسخه v0.2.0

نسخهٔ 0٫2٫0

تاریخ انتشار: 29 مرداد 1405
نخستین انتشار نشان‌دار سامانه. این نسخه ویژگی تازه‌ای به کاربران نمی‌افزاید؛ آنچه می‌سازد راهی است که از این پس هر ویژگی از طریق آن به yasapp.ir می‌رسد.
---

خط انتشار تازه

مخزن خودمیزبان پیشین از دسترس خارج شد و با آن، آخرین دلیل نگهداری دو پیکربندی یکپارچه‌سازی برای سامانه‌هایی که دیگر استفاده نمی‌شدند نیز از میان رفت.

تغییر چگونه به سامانهٔ عملیاتی می‌رسد

  • هر تغییری که به شاخهٔ اصلی می‌رسد، آزمون‌ها را اجرا می‌کند و هیچ‌چیزی را که کاربر ببیند تغییر نمی‌دهد. این همان چیزی است که جابه‌جایی شاخهٔ اصلی را کم‌هزینه نگه می‌دارد.
  • تنها یک نشان (tag) سامانهٔ عملیاتی را جابه‌جا می‌کند. با انتشار نشان، سه تصویر ساخته می‌شوند، با همان نام نشان در مخزن تصاویر ثبت می‌گردند، و سپس سرور از طریق اجراکنندهٔ محلی خودش به جلو غلتانده می‌شود.

بنابراین انتشار، تصمیمی است دربارهٔ زمان، نه شرط‌بندی بر سرِ اینکه آیا ساخته می‌شود یا نه.

چرا کار میان دو نوع اجراکننده تقسیم شده است

  • ساخت روی اجراکنندهٔ ابری انجام می‌شود، چون شبکهٔ آنجا سالم است.
  • استقرار روی اجراکنندهٔ محلیِ نصب‌شده روی خودِ سرور برنامه انجام می‌شود، چون آن ماشین پشت دیوارهٔ آتشی است که هیچ اجراکنندهٔ بیرونی نمی‌تواند واردش شود؛ ارتباط فقط از درون به بیرون برقرار می‌شود.

هیچ‌کدام نمی‌تواند کار دیگری را انجام دهد.

آنچه عمداً در این خط نیست

سرور رسانه. راه‌اندازی دوبارهٔ آن هر کلاس در حال برگزاری را قطع می‌کند، و درسی دوساعته را نمی‌توان «دوباره تلاش» کرد. استقرار آن دستی و در بازهٔ آرام انجام می‌شود. یک خط انتشار که همه‌چیز را با هم راه‌اندازی دوباره کند، گران‌ترین اشتباه ممکن در این سکوست.
---

اصلاح ده امضای مخدوش در فایل قفل بسته‌ها

نصب وابستگی‌ها روی اجراکنندهٔ ابری با خطای یکپارچگی شکست می‌خورد. فایل قفل دربارهٔ نسخه‌ها درست می‌گفت و دربارهٔ بایت‌ها نادرست.
سازوکار این خطا ارزش ثبت‌شدن دارد، چون دوباره رخ خواهد داد: مسیر شبکه از این نقطه تا مخزن بسته‌ها، در انتقال‌های طولانی بایت می‌اندازد. ابزار نصب متوجه می‌شود، دوباره تلاش می‌کند، سرانجام تسلیم می‌شود و امضای همان چیزی را که به‌دستش رسیده ثبت می‌کند. آن امضا از آن پس پاسخ رسمی پروژه دربارهٔ محتوای آن بسته است — بنابراین دستگاهی که شبکهٔ سالم دارد، فایل درست را دریافت می‌کند و آن را رد می‌کند.
خطا روی دستگاهی که آن را ایجاد کرده نامرئی است، چون فایل مخدوش در حافظهٔ نهان خودش نشسته است.
اکنون ابزاری هر امضا را با آنچه مخزن رسمی اعلام می‌کند می‌سنجد و موارد ناهمخوان را اصلاح می‌کند. این ابزار در خط انتشار پیش از نصب وابستگی‌ها اجرا می‌شود تا رخداد بعدی، نام بسته‌ها را بگوید به‌جای آنکه با یک امضا و سکوت شکست بخورد.
---

ساختار مخزن

برنامهٔ اصلی به پوشهٔ مخصوص خود منتقل شد، کنسول اپراتور سکو افزوده شد، و درخت استقرار در کنار آن‌ها قرار گرفت. یک نصب وابستگی در ریشه به هر دو برنامه سرویس می‌دهد؛ این همان چیزی است که باعث می‌شود هر دو یک نسخه از کتابخانهٔ رابط کاربری و در نتیجه یک زبان طراحی داشته باشند.
---

مستندسازی و یافته‌های بازرسی

سندی افزوده شد که سامانهٔ در حال اجرا را توصیف می‌کند: دو سرور و آنچه روی هرکدام اجرا می‌شود، سه گردش‌کار، ترتیب دقیق کارهای اسکریپت استقرار، نسخه‌گذاری، بازگشت به عقب، جای نگهداری هر کلید، و دستورالعمل عملیاتی.
سه یافته نیز ثبت شد که هنگام بازرسی خودِ ماشین‌ها به‌دست آمد، نه هنگام خواندن مستندات:

  • گواهی TLS تمدید خودکار نداشت و با انقضای آن، هر سه سایت پشت هشدار مرورگر از دسترس خارج می‌شدند.
  • درگاه 80 اکنون به میزبان می‌رسد و به 443 هدایت می‌شود؛ بنابراین نشانی تایپ‌شده بدون پیشوند، دیگر غیرقابل‌دسترس نیست.
  • دو نشانی عمومی مقابل این میزبان قرار دارند و هر دو به یک وب‌سرور می‌رسند — دانستنی لازم پیش از اشکال‌زدایی یک قاعدهٔ دیوارهٔ آتش روی نشانی اشتباه.