Telegram
Hicte Bridge in HICTE Group
#مهندسی_نرم_افزار
یکی از مشکلاتی که پروتکل grpc دارد این است که هنوز به طور کامل توسط مرورگرها پشتیبانی نمی شود و برای راه اندازی اش یکی از روش های مرسوم استفاده از api gateway است تا قسمتی که هنوز درخواست کاربر به اپلیکشن نرسیده با http هندل می شود و باقی قسمت های داخلی اپ مایکروسرویس ما با grpc به عنوان مثال بیایم با یک مثال «سفارش تاکسی مثل اوبر» تصویرش کنیم:
سناریو: درخواست سفر
کاربر بیرونی چه میزند؟ (HTTP/REST)
اپ موبایل کاربر یک درخواست سادهی HTTP میفرستد به آدرس عمومی:
این درخواست فقط «متنِ معمولی وب» است؛ همان چیزی که همهی اپها و مرورگرها بلدند.
این API Gateway چه میکند؟
گِیتوی جلوی ورودی نشسته؛ هویت کاربر را چک میکند (توکن، محدودیت سرعت، لاگبرداری).
بعد طبق مسیر /v1/rides میفهمد این درخواست مربوط به «سرویس سفر» است.
دادهی سادهی کاربر را برمیدارد و آن را برای داخل شرکت تبدیل و ارسال میکند.
داخل شرکت چه میشود؟ (گفتوگوی سریع سرویسها)
گِیتوی به «سرویس سفر» میگوید: «یک سفر جدید لازم است» (سرویسها با زبان سریع و مخصوص خودشان حرف میزنند، همان gRPC، ولی لازم نیست جزئیاتش را بدانیم).
«سرویس سفر» برای کارهای مختلف با چند سرویس داخلی حرف میزند:
با «سرویس رانندهها» هماهنگ میکند ببیند چه رانندهای نزدیک است.
با «سرویس نقشه/مسیر» فاصله و زمان تقریبی را میپرسد.
با «سرویس پرداخت» چک میکند کارت معتبر است یا نه.
هر کدام از این مکالمات داخلی خیلی سریع، مستقیم و کمحجم انجام میشوند (همان gRPC داخلی).
پاسخ نهایی به کاربر
وقتی سرویس سفر از سرویسهای داخلی جوابها را گرفت (راننده پیدا شد، قیمت محاسبه شد، پرداخت اوکی شد)، نتیجه را به API Gateway برمیگرداند.
گِیتوی همان نتیجه را به شکل سادهی وب (JSON) به اپ موبایل برمیگرداند:
چرا این مدل خوب است؟
بیرون ساده میماند: همهی مشتریها فقط یک آدرس عمومی و JSON ساده میبینند.
داخل تند و مقرونبهصرفه است: سرویسهای داخلی با زبان سریعتر و کمحجمتر با هم حرف میزنند، تا تأخیر و هزینه پایین باشد.
مقیاسپذیر: میتوانی سرویسهای داخلی را مستقل زیاد/کم کنی، بدون اینکه به اپ مشتری دست بزنی.
امنیت و کنترل: همه چیز از گِیتوی میگذرد؛ پس ورود، سهمیه، لاگ و رصد یکجا انجام میشود.
یک مثال دیگر کوتاه (پیگیری سفر)
اپ کاربر هر چند ثانیه یک بار میزند:
گِیتوی درخواست را به «سرویس سفر» پاس میدهد؛ «سرویس سفر» وضعیت را از «سرویس راننده/نقشه» داخلی میگیرد (داخلی سریع).
پاسخ ساده به کاربر برمیگردد:
این دقیقاً همان الگویی است که شرکتهای بزرگی مثل Uber/Lyft/… هم به شکلهای مختلف از آن استفاده میکنند: بیرون REST، داخل گفتوگوی سریع سرویسها.
🚁 Hicte Blog | (Nariman Tj)
یکی از مشکلاتی که پروتکل grpc دارد این است که هنوز به طور کامل توسط مرورگرها پشتیبانی نمی شود و برای راه اندازی اش یکی از روش های مرسوم استفاده از api gateway است تا قسمتی که هنوز درخواست کاربر به اپلیکشن نرسیده با http هندل می شود و باقی قسمت های داخلی اپ مایکروسرویس ما با grpc به عنوان مثال بیایم با یک مثال «سفارش تاکسی مثل اوبر» تصویرش کنیم:
سناریو: درخواست سفر
کاربر بیرونی چه میزند؟ (HTTP/REST)
اپ موبایل کاربر یک درخواست سادهی HTTP میفرستد به آدرس عمومی:
POST https://api.example.com/v1/rides
Body: { "pickup": "...", "dropoff": "...", "payment": "visa" }این درخواست فقط «متنِ معمولی وب» است؛ همان چیزی که همهی اپها و مرورگرها بلدند.
این API Gateway چه میکند؟
گِیتوی جلوی ورودی نشسته؛ هویت کاربر را چک میکند (توکن، محدودیت سرعت، لاگبرداری).
بعد طبق مسیر /v1/rides میفهمد این درخواست مربوط به «سرویس سفر» است.
دادهی سادهی کاربر را برمیدارد و آن را برای داخل شرکت تبدیل و ارسال میکند.
داخل شرکت چه میشود؟ (گفتوگوی سریع سرویسها)
گِیتوی به «سرویس سفر» میگوید: «یک سفر جدید لازم است» (سرویسها با زبان سریع و مخصوص خودشان حرف میزنند، همان gRPC، ولی لازم نیست جزئیاتش را بدانیم).
«سرویس سفر» برای کارهای مختلف با چند سرویس داخلی حرف میزند:
با «سرویس رانندهها» هماهنگ میکند ببیند چه رانندهای نزدیک است.
با «سرویس نقشه/مسیر» فاصله و زمان تقریبی را میپرسد.
با «سرویس پرداخت» چک میکند کارت معتبر است یا نه.
هر کدام از این مکالمات داخلی خیلی سریع، مستقیم و کمحجم انجام میشوند (همان gRPC داخلی).
پاسخ نهایی به کاربر
وقتی سرویس سفر از سرویسهای داخلی جوابها را گرفت (راننده پیدا شد، قیمت محاسبه شد، پرداخت اوکی شد)، نتیجه را به API Gateway برمیگرداند.
گِیتوی همان نتیجه را به شکل سادهی وب (JSON) به اپ موبایل برمیگرداند:
200 OK
{
"rideId": "abc123",
"driver": { "name": "Sara", "car": "Prius" },
"eta": "3 min",
"price": "€8.40"
}
چرا این مدل خوب است؟
بیرون ساده میماند: همهی مشتریها فقط یک آدرس عمومی و JSON ساده میبینند.
داخل تند و مقرونبهصرفه است: سرویسهای داخلی با زبان سریعتر و کمحجمتر با هم حرف میزنند، تا تأخیر و هزینه پایین باشد.
مقیاسپذیر: میتوانی سرویسهای داخلی را مستقل زیاد/کم کنی، بدون اینکه به اپ مشتری دست بزنی.
امنیت و کنترل: همه چیز از گِیتوی میگذرد؛ پس ورود، سهمیه، لاگ و رصد یکجا انجام میشود.
یک مثال دیگر کوتاه (پیگیری سفر)
اپ کاربر هر چند ثانیه یک بار میزند:
GET https://api.example.com/v1/rides/abc123/statusگِیتوی درخواست را به «سرویس سفر» پاس میدهد؛ «سرویس سفر» وضعیت را از «سرویس راننده/نقشه» داخلی میگیرد (داخلی سریع).
پاسخ ساده به کاربر برمیگردد:
{"status":"driver_arriving","eta":"2 min"}این دقیقاً همان الگویی است که شرکتهای بزرگی مثل Uber/Lyft/… هم به شکلهای مختلف از آن استفاده میکنند: بیرون REST، داخل گفتوگوی سریع سرویسها.
🚁 Hicte Blog | (Nariman Tj)
❤2👍2🔥1
[ Source >> @srfirouzi_channel ]
#مهندسی_نرم_افزار
عملگرایی و تحلیلگرایی در توسعه نرمافزار
(دو روی یک سکه برای موفقیت تیمی)
در دنیای توسعه نرمافزار، اغلب دو رویکرد دیده میشود: عملگرایی و تحلیلگرایی.
اما نکته کلیدی این است که این دو هیچگاه در تقابل نیستند؛ بلکه یکدیگر را تکمیل میکنند.
در واقع، عملگرایی و تحلیلگرایی یک طیف هستند، نه دو مسیر جدا؛ تیمها و پروژهها میتوانند در هر نقطهای از این طیف قرار بگیرند و بسته به شرایط، تعادل مناسب را انتخاب کنند.
عملگراها عاشق حرکت و اجرا هستند.
آنها سریع دست به کد میزنند، پروتوتایپ میسازند و ایدهها را به واقعیت تبدیل میکنند.
برای آنها تجربه عملی و بازخورد سریع، موتور پیشرفت پروژههاست.
اما اگر بیش از حد عملگرا باشیم، خطر بدهی فنی و تصمیمات عجولانه وجود دارد؛ یعنی کدی نوشته میشود که شاید پایدار نباشد و نیازمند اصلاحات مداوم شود.
تحلیلگراها عاشق بررسی دقیق و جزئیات هستند.
آنها معماری و طراحی سیستم را به دقت میکاوند، ریسکها را شناسایی میکنند و قبل از اجرای کد، مشکلات احتمالی را پیشبینی میکنند.
این دقت باعث میشود پروژهها پایدار و قابل توسعه باقی بمانند.
اما اگر بیش از حد تحلیلگرا باشیم، ممکن است تیم دچار فلج تحلیلی شود؛ یعنی آنقدر در طراحی و بررسی جزئیات گرفتار شویم که هیچ محصول واقعی تولید نشود.
در واقع، یک تیم موفق نرمافزاری به هر دو نوع رویکرد نیاز دارد:
عملگراها تضمین میکنند که پروژه حرکت کند و ایدهها سریع آزمایش شوند.
تحلیلگراها تضمین میکنند که حرکت، با کیفیت و استحکام همراه باشد.
وقتی این دو با هم کار کنند، نتیجه چیزی فراتر از جمع تواناییهایشان است:
پروژهای که هم سریع پیش میرود و هم مستحکم و پایدار است.
یادمان باشد: در توسعه نرمافزار، عمل و تحلیل دو روی یک سکه هستند و یک طیف پیوسته را تشکیل میدهند، نه دو مسیر مخالف.
تعادل بین این دو، کلید جلوگیری از بدهی فنی و فلج تحلیلی و راز ساخت تیمهای موفق و پروژههای پایدار است.
🚁 Hicte Blog | (smm)
#مهندسی_نرم_افزار
عملگرایی و تحلیلگرایی در توسعه نرمافزار
(دو روی یک سکه برای موفقیت تیمی)
در دنیای توسعه نرمافزار، اغلب دو رویکرد دیده میشود: عملگرایی و تحلیلگرایی.
اما نکته کلیدی این است که این دو هیچگاه در تقابل نیستند؛ بلکه یکدیگر را تکمیل میکنند.
در واقع، عملگرایی و تحلیلگرایی یک طیف هستند، نه دو مسیر جدا؛ تیمها و پروژهها میتوانند در هر نقطهای از این طیف قرار بگیرند و بسته به شرایط، تعادل مناسب را انتخاب کنند.
عملگراها عاشق حرکت و اجرا هستند.
آنها سریع دست به کد میزنند، پروتوتایپ میسازند و ایدهها را به واقعیت تبدیل میکنند.
برای آنها تجربه عملی و بازخورد سریع، موتور پیشرفت پروژههاست.
اما اگر بیش از حد عملگرا باشیم، خطر بدهی فنی و تصمیمات عجولانه وجود دارد؛ یعنی کدی نوشته میشود که شاید پایدار نباشد و نیازمند اصلاحات مداوم شود.
تحلیلگراها عاشق بررسی دقیق و جزئیات هستند.
آنها معماری و طراحی سیستم را به دقت میکاوند، ریسکها را شناسایی میکنند و قبل از اجرای کد، مشکلات احتمالی را پیشبینی میکنند.
این دقت باعث میشود پروژهها پایدار و قابل توسعه باقی بمانند.
اما اگر بیش از حد تحلیلگرا باشیم، ممکن است تیم دچار فلج تحلیلی شود؛ یعنی آنقدر در طراحی و بررسی جزئیات گرفتار شویم که هیچ محصول واقعی تولید نشود.
در واقع، یک تیم موفق نرمافزاری به هر دو نوع رویکرد نیاز دارد:
عملگراها تضمین میکنند که پروژه حرکت کند و ایدهها سریع آزمایش شوند.
تحلیلگراها تضمین میکنند که حرکت، با کیفیت و استحکام همراه باشد.
وقتی این دو با هم کار کنند، نتیجه چیزی فراتر از جمع تواناییهایشان است:
پروژهای که هم سریع پیش میرود و هم مستحکم و پایدار است.
یادمان باشد: در توسعه نرمافزار، عمل و تحلیل دو روی یک سکه هستند و یک طیف پیوسته را تشکیل میدهند، نه دو مسیر مخالف.
تعادل بین این دو، کلید جلوگیری از بدهی فنی و فلج تحلیلی و راز ساخت تیمهای موفق و پروژههای پایدار است.
🚁 Hicte Blog | (smm)
👍4🔥2
[ Source >> @srfirouzi_channel ]
#مهندسی_نرم_افزار
آنتیپترنها در برنامهنویسی
(الگوهای که باید از آنها دوری کرد)
در دنیای توسعه نرمافزار، الگوهای طراحی (Design Patterns) راهحلهایی اثباتشده برای مشکلات تکراری هستند. در مقابل، آنتیپترن (Anti-Pattern) ظاهراً راهحلی منطقی ارائه میدهد، اما در عمل به مشکلات جدی و هزینههای بلندمدت منجر میشود.
ویژگی کلیدی آنتیپترن این است که همیشه یک راهحل بهتر و جایگزین برای آن وجود دارد.
این مفهوم از دههی ۹۰ میلادی وارد ادبیات مهندسی نرمافزار شد و کتاب AntiPatterns (۱۹۹۸) به شناخته شدن آن کمک زیادی کرد.
نمونههای رایج آنتیپترنها
- اسپاگتی کد (Spaghetti Code): کدی درهمتنیده و بیساختار که تغییر یا توسعهاش کابوس است.
- شی خدا (God Object): کلاسی که بیش از حد مسئولیت دارد و اصل Single Responsibility را زیر پا میگذارد.
- چکش طلایی (Golden Hammer): استفاده از یک ابزار، متد یا تکنولوژی برای همهچیز، فقط به خاطر آشنایی یا راحتی.
- بهینهسازی زودهنگام (Premature Optimization): بهینهسازیای که اغلب پیچیدگی بیهوده ایجاد میکند.
- کپی و پیست (Copy-and-Paste Programming): تکرار کد در بخشهای مختلف به جای استفاده از انتزاع و ماژولار کردن.
- جریان مذاب (Lava Flow): باقی ماندن کدهای قدیمی و بلااستفاده که فهم و نگهداری پروژه را دشوار میکند.
- طراحی بیش از حد (Overdesign): افزودن پیچیدگی و امکانات غیرضروری برای نیازهایی که هنوز وجود ندارند
- و ...
چگونه از آنتیپترنها دوری کنیم؟
- از همه مهمتر آشنایی با آنتی پترن های مطرح
- بازبینی مستمر کد (Code Review).
- بازسازی ساختار کد (Refactoring) بدون تغییر رفتار.
- رعایت اصول ساده و پایهای مانند:
DRY (Don’t Repeat Yourself)
KISS (Keep It Simple, Stupid)
- انتخاب ابزار و تکنولوژی بر اساس نیاز واقعی، نه صرفاً تجربه یا سلیقه شخصی.
- تمرکز بر حل مسئلهی فعلی و پرهیز از طراحی بیشازحد برای آیندهای نامعلوم.
آنتیپترنها صرفاً «اشتباهات فردی» نیستند، بلکه تلههایی رایج در مسیر توسعه نرمافزارند. آنها در کوتاهمدت سودمند به نظر میرسند اما در بلندمدت به پیچیدگی و بدهی فنی منجر میشوند.
شناخت و پرهیز از این الگوها، یکی از مهمترین گامها برای ساخت نرمافزاری پایدار، قابلاعتماد و توسعهپذیر است.
🚁 Hicte Blog | (smm)
#مهندسی_نرم_افزار
آنتیپترنها در برنامهنویسی
(الگوهای که باید از آنها دوری کرد)
در دنیای توسعه نرمافزار، الگوهای طراحی (Design Patterns) راهحلهایی اثباتشده برای مشکلات تکراری هستند. در مقابل، آنتیپترن (Anti-Pattern) ظاهراً راهحلی منطقی ارائه میدهد، اما در عمل به مشکلات جدی و هزینههای بلندمدت منجر میشود.
ویژگی کلیدی آنتیپترن این است که همیشه یک راهحل بهتر و جایگزین برای آن وجود دارد.
این مفهوم از دههی ۹۰ میلادی وارد ادبیات مهندسی نرمافزار شد و کتاب AntiPatterns (۱۹۹۸) به شناخته شدن آن کمک زیادی کرد.
نمونههای رایج آنتیپترنها
- اسپاگتی کد (Spaghetti Code): کدی درهمتنیده و بیساختار که تغییر یا توسعهاش کابوس است.
- شی خدا (God Object): کلاسی که بیش از حد مسئولیت دارد و اصل Single Responsibility را زیر پا میگذارد.
- چکش طلایی (Golden Hammer): استفاده از یک ابزار، متد یا تکنولوژی برای همهچیز، فقط به خاطر آشنایی یا راحتی.
- بهینهسازی زودهنگام (Premature Optimization): بهینهسازیای که اغلب پیچیدگی بیهوده ایجاد میکند.
- کپی و پیست (Copy-and-Paste Programming): تکرار کد در بخشهای مختلف به جای استفاده از انتزاع و ماژولار کردن.
- جریان مذاب (Lava Flow): باقی ماندن کدهای قدیمی و بلااستفاده که فهم و نگهداری پروژه را دشوار میکند.
- طراحی بیش از حد (Overdesign): افزودن پیچیدگی و امکانات غیرضروری برای نیازهایی که هنوز وجود ندارند
- و ...
چگونه از آنتیپترنها دوری کنیم؟
- از همه مهمتر آشنایی با آنتی پترن های مطرح
- بازبینی مستمر کد (Code Review).
- بازسازی ساختار کد (Refactoring) بدون تغییر رفتار.
- رعایت اصول ساده و پایهای مانند:
DRY (Don’t Repeat Yourself)
KISS (Keep It Simple, Stupid)
- انتخاب ابزار و تکنولوژی بر اساس نیاز واقعی، نه صرفاً تجربه یا سلیقه شخصی.
- تمرکز بر حل مسئلهی فعلی و پرهیز از طراحی بیشازحد برای آیندهای نامعلوم.
آنتیپترنها صرفاً «اشتباهات فردی» نیستند، بلکه تلههایی رایج در مسیر توسعه نرمافزارند. آنها در کوتاهمدت سودمند به نظر میرسند اما در بلندمدت به پیچیدگی و بدهی فنی منجر میشوند.
شناخت و پرهیز از این الگوها، یکی از مهمترین گامها برای ساخت نرمافزاری پایدار، قابلاعتماد و توسعهپذیر است.
🚁 Hicte Blog | (smm)
👍4❤1
[ Source >> @openpcb ]
#سی
پروژه LWMalloc یه memory allocator سبک برای سیستمهای امبدده که نسبت به ptmalloc تو Glibc تا ۵۳٪ سریعتره و ۲۳٪ هم حافظه کمتری مصرف میکنه.
مشکل malloc تو امبدد اینه که به مرور حافظه رو تکهتکه میکنه و وقتی فریمور طولانیمدت بالا بمونه آخرش به کرش میرسه. بعضیا سمت garbage collection میرن، ولی روی دیوایسهای محدود خیلی وقتا عملی نیست. به همین خاطر خیلیا ترجیح میدن حافظه رو استاتیک یا با memory pool مدیریت کنن (که به نظر من بهترین راهه). یه گزینه دیگه هم نوشتن allocator اختصاصیه (که از نظر من بدترین راهه!)، و این دقیقاً کاریه که LWMalloc کرده.
طبق مقاله “LWMalloc: A Lightweight Dynamic Memory Allocator for Resource-Constrained Environments”، این لایبرری از ساختار داده خیلی سبک، سیاست deferred coalescing و استخرهای جدا برای chunkهای کوچیک استفاده میکنه. نتیجه؟ متادیتای کمتر، عملیات ادغام بهموقع به جای وسط کار، و پاسخ O(1) برای درخواستهای کوچیک.
تستهای دانشگاه SEOULTECH نشون داده LWMalloc نسبت به ptmalloc حدود ۵۳٪ سریعتره و ۲۳٪ کمتر حافظه میخوره. کل کدش ۵۳۰ خط و footprint حدود ۲۰ کیلوبایته، در حالی که ptmalloc نزدیک ۴۸۳۸ خط و ۱۱۶ کیلوبایته. تو اطلاعیهشون هم اشاره کردن که allocatorهایی مثل jemalloc، tcmalloc و mimalloc هستن ولی به خاطر مصرف حافظه بالا و پیچیدگی آخرش افت کارایی دارن.
کد C و برنامه تستش روی گیتهاب هست و چون همون malloc/calloc/realloc/free استاندارد رو پیادهسازی کرده، میشه مستقیم جاش استفاده کرد یا حتی با LD_PRELOAD بدون تغییر اپلیکیشن جایگزینش کرد.
کاربرد اصلیش تو سیستمهای امبدد و IoT با محدودیت حافظه و کاراییه: از تلویزیون هوشمند و ستتاپباکس گرفته تا پوشیدنیها، سیستمهای خودرویی real-time و کامپیوترهای edge برای AI.
ولی راستش رو بخواید، من همچنان روشهای استاتیک یا memory pool رو پیشنهاد میکنم، مگر اینکه اسلحه رو سرتون باشه :)
اگه دوست داشتید اصل مقاله رو مطالعه کنید اینجا میتونید پیداش کنید.
ریپوی پروژه رو هم اینجا میتونید بررسی کنید.
🚁 Hicte Blog | (smm)
#سی
پروژه LWMalloc یه memory allocator سبک برای سیستمهای امبدده که نسبت به ptmalloc تو Glibc تا ۵۳٪ سریعتره و ۲۳٪ هم حافظه کمتری مصرف میکنه.
مشکل malloc تو امبدد اینه که به مرور حافظه رو تکهتکه میکنه و وقتی فریمور طولانیمدت بالا بمونه آخرش به کرش میرسه. بعضیا سمت garbage collection میرن، ولی روی دیوایسهای محدود خیلی وقتا عملی نیست. به همین خاطر خیلیا ترجیح میدن حافظه رو استاتیک یا با memory pool مدیریت کنن (که به نظر من بهترین راهه). یه گزینه دیگه هم نوشتن allocator اختصاصیه (که از نظر من بدترین راهه!)، و این دقیقاً کاریه که LWMalloc کرده.
طبق مقاله “LWMalloc: A Lightweight Dynamic Memory Allocator for Resource-Constrained Environments”، این لایبرری از ساختار داده خیلی سبک، سیاست deferred coalescing و استخرهای جدا برای chunkهای کوچیک استفاده میکنه. نتیجه؟ متادیتای کمتر، عملیات ادغام بهموقع به جای وسط کار، و پاسخ O(1) برای درخواستهای کوچیک.
تستهای دانشگاه SEOULTECH نشون داده LWMalloc نسبت به ptmalloc حدود ۵۳٪ سریعتره و ۲۳٪ کمتر حافظه میخوره. کل کدش ۵۳۰ خط و footprint حدود ۲۰ کیلوبایته، در حالی که ptmalloc نزدیک ۴۸۳۸ خط و ۱۱۶ کیلوبایته. تو اطلاعیهشون هم اشاره کردن که allocatorهایی مثل jemalloc، tcmalloc و mimalloc هستن ولی به خاطر مصرف حافظه بالا و پیچیدگی آخرش افت کارایی دارن.
کد C و برنامه تستش روی گیتهاب هست و چون همون malloc/calloc/realloc/free استاندارد رو پیادهسازی کرده، میشه مستقیم جاش استفاده کرد یا حتی با LD_PRELOAD بدون تغییر اپلیکیشن جایگزینش کرد.
کاربرد اصلیش تو سیستمهای امبدد و IoT با محدودیت حافظه و کاراییه: از تلویزیون هوشمند و ستتاپباکس گرفته تا پوشیدنیها، سیستمهای خودرویی real-time و کامپیوترهای edge برای AI.
ولی راستش رو بخواید، من همچنان روشهای استاتیک یا memory pool رو پیشنهاد میکنم، مگر اینکه اسلحه رو سرتون باشه :)
اگه دوست داشتید اصل مقاله رو مطالعه کنید اینجا میتونید پیداش کنید.
ریپوی پروژه رو هم اینجا میتونید بررسی کنید.
🚁 Hicte Blog | (smm)
👍3❤1
🤯8😁3😐2😡1