CleverDevs – Telegram
CleverDevs
8.45K subscribers
734 photos
283 videos
27 files
436 links
به کانال کلوردوز خوش اومدین

سعی میکنیم چیزایی که بنظر کاربردی هستن رو باهاتون به اشتراک بزاریم


🔥لینوکس
🔥برنامه نویسی فرانت‌اند
🔥برنامه نویسی بک‌اند
🔥اخبار تکنولوژی و...

CleverDevs are better than other Devs
💢~> @mmdrsdev
Download Telegram
🦸🏼‍♂ | Puppeteer

پاپیتیر یک کتابخانه بسیار قدرتمند که توسط توسعه دهندگان تیم گوگل ساخته شده، مناسب Web Scraping و Page Manipulation و Browser Automation هستش و کلی قابلیت های معرکه دیگه داره که میشه به تمام قابلیت های مرورگر دسترسی داشت و چیزهای خفنی باهاش توسعه داد.

🔥 چند نمونه از استفاده های پرکاربرد:

- دریافت اطلاعات از سایت‌ها بدون نیاز به API.


- انواع حملات و سو استفاده از وبسایت.


- اتومیشن کردن سناریو های مختلف.


- تست کردن وبسایت، گرفتن خروجی بصورت پی دی اف و یا اسکرینشات.


#puppeteer | #tools
@CleverDevs - @CleverDevsGp
👍23👌4🔥2
درود دوستان امروز با یه مبحثی اومدم که تقریبا 99 درصد شرکت های ایرانی در صورتی که خیلی پر اهمیت هست ولی اهمیتی بهش ندادن.

در برنامه‌نویسی شبکه‌ای و وب سرورها یه مبحثی هست با نام "graceful shutdown" یعنی kill کردن یا بستن بهینه سرویس ها با حداق تلفات در درخواست ها، میتونه در شرکت های بزرگ انقدر بهینه پیاده سازی بشه تا به هیچ عنوان درخواست یا دیتایی از دست نره.

خب حالا بیاین یه مثال ساده ازش بزنیم، برای مثال ما یه سرویسی داریم که کلی ریکوست از سمت کلاینت داره و میخوایم بنا به دلایلی چند لحظه down کنیمش اینجاس که پای "graceful shutdown" به ماجرا باز میشه، ما باید یه سیستمی پیاده کنیم تا دو مورد زیر رو تکمیل کنه و سپس process رو kill کنه.

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

یه مثال با golang اینجا میارم، به علت اینکه کد طولانی شد میزارمش تو یه فایل تکست پایین

break;

حالا اجازه بدین به زبون خودمونی توضیح بدم که چرا استفاده از Graceful Shutdown (خاموشی تمیز) مهمه و چطوری توی این کد \پایین اجرا شده.

فرض کن یه مغازه‌داری. وقتی می‌خوای مغازه‌ات رو ببندی، قبلش مطمئن میشی که همه مشتری‌ها کارشون تموم شده، حساب کتاباشون رو کردن و دارن میرن بیرون. نمیای وسط کار مشتری‌ها، کرکره رو بکشی پایین و در رو قفل کنی. اینجوری هم خودت ضرر می‌کنی، هم مشتری‌ها ناراضی میشن.

حالا همینو بیاریم توی برنامه‌نویسی سرور:

تموم کردن کارهای در حال اجرا: وقتی سرور رو یهویی خاموش نکنی، به درخواست‌هایی که در حال پردازش هستن فرصت میدی کارشون رو تموم کنن. مثلا وقتی کسی داره اطلاعاتی رو از سرور می‌گیره، اگه وسطش سرور خاموش بشه، اون شخص به اطلاعاتش نمیرسه. با Graceful Shutdown، سرور منتظر می‌مونه تا این درخواست‌ها تموم بشن.

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

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

حالا توی کد پایین، اینطوری این کار انجام شده:

تعریف یک کانال برای دریافت سیگنال‌ها:
go
Copy code
stop := make(chan os.Signal, 1)
signal.Notify(stop, os.Interrupt, syscall.SIGTERM, syscall.SIGINT)

این قسمت میگه که وقتی سیگنال‌های مشخصی (مثل Ctrl+C زدن توی ترمینال) دریافت شد، بره سراغ خاموشی تمیز.

گوش دادن به سیگنال‌ها و شروع فرآیند خاموشی:
go
Copy code
<-stop
log.Println("Server is shutting down...")
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
if err := srv.Shutdown(ctx); err != nil {
log.Fatalf("Server Shutdown Failed:%+v", err)
}

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

صبر کردن تا تموم شدن همه کارها:
go
Copy code
wg.Wait()
log.Println("Server exited properly")


اینجا منتظر می‌مونه تا همه کارها (که با استفاده از wg.Add(1) و wg.Done() کنترل میشه) تموم بشن. وقتی همه کارها تموم شد، پیام "سرور به درستی خاموش شد" رو لاگ می‌کنه.

به طور خلاصه، استفاده از Graceful Shutdown کمک می‌کنه سرور به درستی و بدون ایجاد مشکل خاموش بشه، کارهای در حال اجرا تموم بشن و داده‌ها به درستی ذخیره بشن. این باعث میشه که سرور همیشه به صورت مطمئن و پایدار کار کنه و تجربه بهتری برای کاربرها فراهم کنه، و از خیلی چیزای دیگه مثل ذخیره شدن ناقص اطلاعت یا بسته نشدن procss ها که باعث میشن منابع الکی هدر بره جلوگیره کنه.

#Graceful_Shutdown
@CleverDevs - @CleverDevsGp
🔥19👍9🆒3👎1
دوستان ما یه تصمیمی گرفتیم که اگه بیشتر از یه تعدادی از بچه ها پایه باشید شروعش کنیم 👨‍💻

کارمون از این قراره که ما اومدیم یه اکانت گیت هاب زدیم و میخوایم تیم های مختلفی تشکیل بدیم و باهم دیگه پروژه های اوپن سورس بزنیم
اما مزیت این کار اینه که میتونید تو کار تیمی پیشرفت کنید از طرفی در زمینه اوپن سورس هم بعد ها میشه حرفی برای گفتن داشت 🔥

اگه تعداد افرادی که مشارکت کنن تو این کار به حد مناسبی برسه میایم و بچه هارو بر اساس استک های مختلف دسته بندی میکنیم و  تیم هایی میسازیم مثلا تیم ریکت کار ها یا تیم لاراول کار ها و ...
هرکدوم از این تیم ها میان توی ارگان CleverDevs  برای خودشون پروژه های کاربردی مختلفی میسازن و میتونن با تیم های دیگه هم کاری هم کنن 👌

برای هر تیم هم بر اساس رزومه و مهارت یک نفر رو به عنوان لیدر انتخاب میکنیم که تیم رو مدیریت کنه

این فعلا یه ایده اولیه از کاره و میتونید پیشنهاد هایی که دارید رو تو کامنت ها بگید و شرکت یا عدم شرکت خودتون توی این برنامه رو تو نظرسنجی پایین انتخاب کنید

@CleverDevs - @CleverDevsGp
Please open Telegram to view this post
VIEW IN TELEGRAM
👍68🔥11🆒3👎2
CleverDevs
#GN @CleverDevs - @CleverDevsGp
This media is not supported in your browser
VIEW IN TELEGRAM
سیستمی که باهاش ستاپ های خفن GN هارو میبینیم:

#fun
@CleverDevs - @CleverDevsGp
🤣58🔥3💔2
CleverDevs
دوستان ما یه تصمیمی گرفتیم که اگه بیشتر از یه تعدادی از بچه ها پایه باشید شروعش کنیم 👨‍💻 کارمون از این قراره که ما اومدیم یه اکانت گیت هاب زدیم و میخوایم تیم های مختلفی تشکیل بدیم و باهم دیگه پروژه های اوپن سورس بزنیم اما مزیت این کار اینه که میتونید تو…
خب رفقا برای اینکه بحث مربوط به تیم با فعالیت اصلی چنل قاطی نشه یه گروه دیگه برای این کار تشکیل دادیم اگه پایه بودید بیایید اونجا تا درباره جزئیات کار تصمیم بگیریم

پیام پین شده توی گروه رو هم حتما بخونید

https://news.1rj.ru/str/+mdJgJfZOBfU0ODlk


@CleverDevs - @CleverDevsGp
👍8🔥3💯1
قابلیت tag توی گیت یکی از قابلیت های خفن گیته که برای ورژن بندی کمک زیادی میکنه در واقع تگ ها توی گیت برای نشانه گذاری زمان خاصی از تاریخچه پروژه هست

مثلا ما یه سری فیچر برای پروژمون زدیم و میخوایم که به عنوان ورژن 1 علامت گذاری کنیم میایم و از tag ها استفاده میکنم و اون کامیت اخر رو با تگ v1 نشانه گذاری میکنیم و بعد تر که فیچر های دیگه ای اومد و ورژن 2 آماده بود میتونیم به اخرین کامیت اون لحظه هم تگ v2 رو اختصاص بدیم


اما ما دو نوع تگ توی گیت داریم

نوع اول Annotated tags هست که اطلاعات بیشتری درباره اون تگ مثل اسم و ایمیل کسی که تگ رو ایجاد کرده ، تاریخ ایجاد تگ و یک پیام برای تگ و یک هش مخصوص برای اون کامیتی که تگ شده ست میکنن
برای ایجاد این نوع تگ ها باید کامند زیر رو بزنید که

git tag -a v1.0.0 -m "version 1.0.0"


که سوئیچ a به Annotated اشاره میکنه و سوئیچ m هم پیام مربوط به تگه (میتونید توی کامندتون پیام رو ست نکنید در این صورت گیت ادیتور رو براتون باز میکنه تا بنویسید)

نوع دوم lightweight tags هست که هیچ اطلاعات اضافه ای درباره تگ نمیده و فقط به اون کامیتی که تگ روش زده شده اشاره میکنه که بیشتر برای نشانه گذاری های موقت بدرد بخوره برای ایجاد این تگ ها هم باید کامند زیر رو بزنید

git tag v1.0.0


تگ های lightweight دیگه سوئیچ های m و a رو نمیگیرن

اگه خواستید بیشتر دربارش بدونید پیشنهاد میکنم به لینک زیر یه نگاه بندازید

https://www.freecodecamp.org/news/git-tag-explained-how-to-add-remove/

#git
@CleverDevs - @CleverDevsGp
👍28🔥621
آسیب پذیری های امنیتی جاوا اسکریپت🖇
#part1

1⃣ آسیب‌پذیری Cross-Site Scripting (XSS):
حمله‌ای که  مهاجم کد مخربو به یک وب‌سایت وارد میکنه و این کد در مرورگر کاربرا اجرا میشه.

راهای جلوگیری:
امن‌سازی داده‌های ورودی با تابع هایی مثل encodeURIComponent.
جلوگیری از نمایش مستقیم داده‌های کاربر در صفحات وب.


2⃣ آسیب‌پذیری Cross-Site Request Forgery (CSRF)
توضیح: حمله‌کننده با استفاده از کاربر و کوکی‌های مرورگر، عملیات ناخواسته‌ای رو اجرا میکنه و وقتی اتفاق میوفته که یک سایت بدون بررسی اعتبار درخواستای ارسالی از کاربرو اجرا کنه

راهای جلوگیری:
استفاده از توکن‌های CSRF در فرم‌ها و درخواست‌های حساس و تنظیم کوکی‌ها به گونه‌ای که فقط از طریق سرور قابل دسترس باشن (HttpOnly)


3⃣ حمله DDoS (Distributed Denial of Service)
توضیح: حمله‌ای که با ارسال تعداد زیادی درخواست به یک سرور، اونو از دسترس خارج میکنه.

راهای جلوگیری:
با محدود کردن تعداد درخواست‌های ورودی به سرور، از بار زیاد جلوگیری میشه.
از سرویس‌های مقابله با DDoS به طور تخصصی برای کاهش اثرات حملات DDoS طراحی شدن استفاده کنید
ظرفیت شبکه رو افزایش بدید و بار ترافیک رو بین چند سرویس توزیع کنین(load balancing)


4⃣ آسیب پذیری SQL injection:
حمله‌ای که  مهاجم دستورات مخربو از طریق ورودی کاربر به سرور ارسال میکنه و این دستورات در سرور اجرا میشن

راهای جلوگیری:
استفاده از parameterized queries
به کارگیری ORM (Object-Relational Mapping) برای تعامل با پایگاه داده‌ها.

@CleverDevs
@CleverDevsGp
👍153👌3🆒21💯1
این قضیه توی نصب فیلترشکن به خاطر برنامه نویس بودن هم صادقه

#fun
@CleverDevs - @CleverDevsGp
🤣56👍5👌2
توی این پست میخوام یه دانلود منیجر خفن و کاربردی رو معرفی کنم که بچه های ایرانی نوشتن

پرسپولیس یک دانلود منیجر و رابط کاربری گرافیکی برای Aria2 میباشد که با پایتون نوشته شده

پرسپولیس یک دانلود منیجر کراس پلتفرمه که برای سیستم عامل های گنو/لینوکس ، ویندوز ، مکینتاش و بی اس دی ها توسعه یافته

یک سری از قابلیت های این دانلود منیجر هم ایناس


رابط کاربری گرافیکی برای Aria2
دانلود چند بخشی فایل
زمانبندی دانلودها
صف‌بندی دانلودها
پیدا و دانلود کردن ویدئو از DailyMotion، Vimeo، Youtube، Aparat و ...


اگه به لینوکس مهاجرت کردید و دنبال یه جایگزین برای IDM میگردید یا توی سایر سیستم عامل ها میخواید از یه دانلود منیجر آزاد و اوپن سورس استفاده کنید پرسپولیس گزینه مناسبیه

اطلاعات بیشتر و نحوه نصب و استفاده :

https://persepolisdm.github.io/fa/

#tools #openSource
@CleverDevs - @CleverDevsGp
🔥33👍10👌2👎1
$$typeof property


توی React وقتی یه کامپوننت رو لاگ میگیریم، یه پراپرتی با این اسم میبینیم و هدفش جلوگیری از حملاتی مثل Cross-Site Scripting هست؛ حالا چطور انجام میشه؟

مقدار این پراپرتی یه Symbol هست، و یکی از Primitive تایپ‌های Js هست که نمیتونه به JSON تبدیل بشه، بخاطر همین اگه هکر بخواد یه کامپوننت فیک به سایتمون ارسال کنه ریکت متوجه این قضیه میشه.
#React #Js
@CleverDevs - @CleverDevsGp
👍14🔥3👌32
تولد داریم . . .

کانال CleverDevs یکساله شد 💥

یکسال پیش چنین روزی کانال با هدف اینکه چیزایی که بلدیم رو به دیگران هم منتقل کنیم شروع به کار کرد  تو این یک سال تجربه های مختلفی داشتیم سعی کردیم کانال تا جای ممکن برای اعضا مفید باشه ❤️

تا جایی که میشد کاری کردیم که کانال دو طرفه باشه یعنی هم ما پست بزاریم و هم اعضا تو کامنت ها چیزایی که میدونن رو با بقیه به اشتراک بزارن 🤝

سعی کردیم پروژه های اوپن سورسی که بچه های خودمون توسعه دادن رو معرفی کنیم تا زحماتشون بیشتر دیده بشه 👨‍💻

اگه پیشنهاد یا نقدی در راستای بهتر شدن چنل دارید خوشحال میشم تو کامنتا مطرحش کنید

@CleverDevs - @CleverDevsGp
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥64167👍7👎1
درود🌹

بیاین امروز در مورد معماری Event-driven Architecture با یک مثال ساده صحبت کنیم.

معماری مبتنی بر رویدادها یه جور سیستم برنامه‌نویسیه که توش بخش‌های مختلف برنامه می‌تونن با هم حرف بزنن و به تغییرات و اتفاقات مختلف واکنش نشون بدن. این معماری مخصوصاً برای برنامه‌های بزرگ و پیچیده که تعاملات زیادی دارن خیلی خوبه.

اجزای اصلی این معماری عبارتند از:

فرستنده رویداد (Event Emitter): این بخش کارش اینه که رویدادها رو به بقیه بخش‌های برنامه اطلاع بده.
شنونده‌ها (Listeners): اینا فانکشن‌هایی هستن که وقتی یه رویداد خاص اتفاق می‌افته، اجرا می‌شن.
رویدادها (Events): رویدادها پیغام‌هایی هستن که نشون می‌دن یه تغییر یا اتفاق خاص رخ داده.

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

فرض کن یه برنامه داری که توش چند تا کاربر هستن. می‌خوای هر وقت یکی از کاربرا اطلاعاتش تغییر کرد یا کاربری اضافه شد، این تغییرات به بقیه بخش‌های برنامه اطلاع داده بشه. برای این کار از معماری مبتنی بر رویداد استفاده می‌کنیم.

اول یه کلاس می‌سازیم که کارش مدیریت رویدادهاست:

// این کلاس مسئولیت مدیریت رویدادها رو داره
class EventEmitter {
constructor() {
this.events = {}; // اینجا رویدادها و شنونده‌هاشون رو نگه می‌داریم
}

// این متد یه شنونده جدید برای یه رویداد خاص ثبت می‌کنه
on(event, listener) {
if (!this.events[event]) {
this.events[event] = [];
}
this.events[event].push(listener);
}

// این متد همه شنونده‌های یه رویداد خاص رو اجرا می‌کنه
emit(event, args) {
if (this.events[event]) {
this.events[event].forEach(listener => listener(args));
}
}
}


حالا یه کلاس می‌سازیم که کاربرها رو مدیریت کنه و وقتی کاربری اضافه یا تغییر کرد، رویدادهای مناسب رو صادر کنه:

// این کلاس مسئول مدیریت کاربران و رویدادهای مرتبط با اوناست
class UserManager extends EventEmitter {
constructor() {
super();
this.users = []; // لیست کاربران رو اینجا نگه می‌داریم
}

// این متد یه کاربر جدید اضافه می‌کنه
addUser(user) {
this.users.push(user);
this.emit('userAdded', user); // رویداد اضافه شدن کاربر رو صادر می‌کنیم
}

// این متد اطلاعات یه کاربر رو تغییر می‌ده
updateUser(id, newUser) {
const index = this.users.findIndex(user => user.id === id);
if (index !== -1) {
this.users[index] = { ...this.users[index], ...newUser };
this.emit('userUpdated', this.users[index]); // رویداد به‌روزرسانی کاربر رو صادر می‌کنیم
}
}
}


حالا از UserManager استفاده می‌کنیم و شنونده‌های مختلفی برای رویدادها ثبت می‌کنیم:


const userManager = new UserManager();

// ثبت یه شنونده برای وقتی که یه کاربر جدید اضافه می‌شه
userManager.on('userAdded', (user) => {
console.log(`کاربر جدید اضافه شد: ${user.name}`);
// اینجا می‌تونیم کارهای دیگه‌ای هم انجام بدیم، مثل به‌روزرسانی رابط کاربری
});

// ثبت یه شنونده برای وقتی که اطلاعات یه کاربر تغییر می‌کنه
userManager.on('userUpdated', (user) => {
console.log(`اطلاعات کاربر به‌روزرسانی شد: ${user.name}`);
// اینجا می‌تونیم کارهای دیگه‌ای هم انجام بدیم، مثل به‌روزرسانی رابط کاربری
});

// اضافه کردن یه کاربر جدید
userManager.addUser({ id: 1, name: 'Ali', age: 30 });
// نتیجه: کاربر جدید اضافه شد: Ali

// تغییر اطلاعات یه کاربر
userManager.updateUser(1, { name: 'Hassan', age: 35 });
// نتیجه: اطلاعات کاربر به‌روزرسانی شد: Hassan


حالا فرض کن می‌خوای هر وقت کاربری اضافه شد یا تغییر کرد، علاوه بر چاپ توی کنسول، یه نوتیفیکیشن هم نشون بدی:

// ثبت شنونده برای نوتیفیکیشن‌ها
userManager.on('userAdded', (user) => {
alert(`کاربر جدید اضافه شد: ${user.name}`);
});

userManager.on('userUpdated', (user) => {
alert(`اطلاعات کاربر به‌روزرسانی شد: ${user.name}`);
});



از این معماری در drawio استفاده شده.
ری اکت و ویو و همچنین Apache Kafka از این معماری استفاده میکنه، اگر علاقه داشتین مثال های بیشتری باهاش میزنیم یا تو یک برنامه ای ازش استفاده میکنیم اصلا

موفق باشید❤️

#Event_driven_Architecture
@CleverDevs - @CleverDevsGp
👍1455🔥1👌1🆒1
This media is not supported in the widget
VIEW IN TELEGRAM
1😁25💔6🔥3👍2
قابلیت دیگه ای که توی این اپدیت اومده
مینی مایز کردن وب اپ های تلگرامه که خیلی کاربردیه

یعنی وسط کار با وب اپ ها (مثل ناتکوین و ..) میتونید یه دیقه برید توی تلگرام بدون اینکه وب اپ رو کامل ببنید

#News #telegram
@CleverDevs - @CleverDevsGp
👍25🔥6👌3👎1🆒1
-اصل Bad Comments در کلین کد

این دسته که از کامنت ها که بیشتر کامنت هایی که میزاریم رو شامل میشه کامنت هایی ان که سود خاصی برامون ندارن و الکی کد رو شلوغ میکنن
این اصل چنتا زیر مجموعه داره و کامنت های بدی که میتونید بزارید رو گفته تو این پست سعی میکنم به طور خلاصه همشون رو بگم

‏1 - Mumbling
یعنی اینکه کامنتی بزاری که نامفهمومه و بیشتر از اینکه بدرد بخور باشه باعث سر در گمیه

‏2 - Redundant Comments
یعنی کامنت هایی که بدرد نخور و اضافن و خوندوشون از خوندن کد کد زمان بیشتری میبره

‏3 - Misleading Comments
یعنی یجور لقمه رو دور سر بپیچونی که کسی که کامنت رو میخونه کلا فکر و ذهنش منحرف بشه به یه سمت دیگه

‏4 - Mandated Comments
کامنت هایی که برای هر متغیری مینویسد و معمولا زیاد بدرد نمیخورن مثل javadocs

‏5 - Journal Comments
اینکه بیای و تغییرات پروژه رو هر بار تو کامنتا بزنی ، اینکار برا قبل اومدن سیستم های کنترل ورژن مثل گیت بود این نوع کامنتا الان بدرد نمیخورن

‏6 - Noise Comments
کامنت هایی که کار خاصی ندارن و فقط کد رو شلوغ کردن مثل
/** The day of the month. */

private int dayOfMonth;


‏7 - Commented-Out Code
کامنت کردن کد ها هم یکی از بدترین نوع کامنت هاست

‏8 - Too Much Information
یعنی اینکه تو کامنت اطلاعات زیادی بدی انقدر زیاد باشه خوندنش کلی وقت ببره

این فصل هم تموم شد و میتونید لیستش رو اینجا ببینید

#CleanCode
@CleverDevs - @CleverDevsGp
👍17🔥53