<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"><channel><title>نوشته های امیر توحیدی</title><link>https://virgool.ir/feed/@amir.tohidi</link><description>امیر توحیدی هستم. برنامه نویس و توسعه دهنده یا هر چیزی که شما بگید. :)</description><language>fa</language><pubDate>2026-11-11 10:27:09</pubDate><image><url>https://files.virgool.ir/upload/users/4139/avatar/ne3Mam.png</url><title>امیر توحیدی</title><link>https://virgool.ir/@amir.tohidi</link></image><item><title>توسعه نرم افزار با Speckit  بخش دوم</title><link>https://virgool.ir/@amir.tohidi/توسعه-نرم-افزار-با-speckit-بخش-دوم-ayitbucgoh7z</link><description>بخش اول شروع پیاده‌سازی با GitHub Spec Kitتا این مرحله، مسئله کسب‌وکار، نیازمندی‌های اصلی، محدودیت‌های زیرساخت، مدل اندازه‌گیری KPI و Goal و معماری اولیه سیستم مشخص شده بود. بنابراین وارد فاز پیاده‌سازی شدیم.برای این مرحله از GitHub Spec Kit استفاده کردیم. Spec Kit یک چارچوب برای Spec-Driven Development است؛ یعنی به‌جای این‌که مستقیماً از مدل بخواهیم کد تولید کند، ابتدا مشخص می‌کنیم چه چیزی باید ساخته شود، سپس مسیر فنی پیاده‌سازی را طراحی می‌کنیم و در نهایت آن را به Taskهای قابل اجرا تبدیل می‌کنیم.Spec Kit به ابزارهای کدنویسی مبتنی بر هوش مصنوعی که از آن پشتیبانی می‌کنند متصل می‌شود. پس از راه‌اندازی، مجموعه‌ای از Command یا Skill در اختیار Agent قرار می‌دهد تا فرایند توسعه را مرحله‌به‌مرحله پیش ببرد.نکته مهم این است که Spec Kit جای تحلیل یا تصمیم‌گیری انسانی را نمی‌گیرد. ارزش آن برای من در این بود که تصمیم‌ها، محدودیت‌ها و خروجی هر مرحله را به سندی قابل ارجاع تبدیل می‌کرد و اجازه نمی‌داد مدل از همان ابتدا بدون Context کافی وارد کدنویسی شود.چرخه توسعه در Spec Kitچرخه اصلی Spec Kit به این شکل است:Constitutionدر ابتدا اصول حاکم بر پروژه تعریف می‌شوند. این اصول شامل استانداردهای کیفیت کد، نحوه تست، محدودیت‌های معماری، قواعد امنیتی، Performance و مواردی است که باید در تمام مراحل توسعه رعایت شوند.Specifyدر این مرحله مشخص می‌شود که چه چیزی باید ساخته شود و چرا. خروجی این مرحله یک Specification است که شامل نیازمندی‌ها، User Storyها، سناریوها، محدودیت‌ها و معیارهای پذیرش است. در این بخش تمرکز بر نیاز محصول است، نه جزئیات فنی پیاده‌سازی.Clarifyپیش از طراحی فنی، بخش‌های مبهم، متناقض یا ناقص Specification بررسی می‌شوند. این مرحله کمک می‌کند پرسش‌هایی که ممکن است بعداً به تغییر طراحی یا بازنویسی کد منجر شوند، زودتر مشخص و پاسخ داده شوند.Planپس از تثبیت نیازمندی‌ها، طراحی فنی انجام می‌شود. در این مرحله مشخص می‌شود قابلیت موردنظر با چه معماری، تکنولوژی، مدل داده، API، ساختار Frontend و روش تستی پیاده‌سازی خواهد شد.TasksPlan فنی به Taskهای کوچک، قابل اجرا و قابل پیگیری تبدیل می‌شود. هر Task باید تا حد ممکن مستقل، قابل بررسی و دارای خروجی مشخص باشد.Analyzeقبل از شروع اجرا، می‌توان Specification، Plan و Taskها را از نظر تناقض، پوشش نیازمندی‌ها و رعایت اصول Constitution بررسی کرد. این مرحله برای پروژه‌های بزرگ اهمیت زیادی دارد، چون بعضی تناقض‌ها قبل از ورود به کد مشخص می‌شوند.Implementدر این مرحله Taskها به‌ترتیب اجرا می‌شوند و قابلیت موردنظر پیاده‌سازی می‌شود.Converge و Validationپس از پیاده‌سازی، کد و رفتار واقعی سیستم دوباره با Specification، Plan و Taskها مقایسه می‌شوند. اگر بخشی ناقص باشد یا با نیاز اولیه تطابق نداشته باشد، Task جدید ایجاد می‌شود و چرخه تا رسیدن به خروجی قابل‌قبول ادامه پیدا می‌کند.تبدیل مستندات قبلی به مسیر پیاده‌سازیدر پروژه ما، مستنداتی که در مراحل قبل آماده شده بودند، ورودی مرحله Specify شدند؛ شامل مسئله اصلی، نیاز تیم داده، مدل دریافت سگمنت از Data Providerها، محدودیت نبود Tracking مستقیم، مدل UTM، Goal و KPI و همچنین نیازهای مقیاس‌پذیری.سپس این نیازها به Specification تبدیل شدند تا مشخص شود سیستم دقیقاً چه قابلیت‌هایی باید داشته باشد و هر قابلیت تحت چه شرایطی قابل قبول است.بعد از آن، طراحی اولیه سیستم با جزئیات فنی‌تر در Plan قرار گرفت؛ از ساختار Backend با C# و Frontend با React.js تا ارتباط با منابع داده، مدیریت Campaign Runها، کانال‌های ارسال و ذخیره خروجی‌های لازم برای تحلیل KPI.در نهایت، Plan به مجموعه‌ای از Taskهای کوچک‌تر تبدیل شد. به‌جای این‌که از مدل بخواهیم کل سیستم را یک‌جا پیاده‌سازی کند، هر بخش به واحدهای قابل کنترل تقسیم شد؛ مانند ایجاد ساختار داده، پیاده‌سازی یک API، طراحی یک صفحه، اتصال یک Provider، افزودن تست یا بررسی یک سناریوی خطا.این نقطه، شروع واقعی توسعه نرم‌افزار بود؛ اما همان‌طور که در ادامه توضیح می‌دهم، داشتن Specification و Task به‌تنهایی تضمین نمی‌کرد که خروجی مدل همیشه دقیقاً مطابق نیاز، Constraintها و طراحی اولیه باشد</description><category>امیر توحیدی</category><author>امیر توحیدی</author><pubDate>Mon, 24 Aug 2026 16:43:29 +0330</pubDate></item><item><title>راه اندازی پروژه توسط SpecKit Github بخش اول</title><link>https://virgool.ir/@amir.tohidi/راه-اندازی-پروژه-توسط-speckit-github-بخش-اول-kkg8an6y7jac</link><description>سلام. در این مقاله قصد دارم تجربه شخصی خودم را از راه‌اندازی یک پروژه مقیاس‌پذیر از صفر با استفاده از C# و React.js شرح بدهم.پروژه موردنظر یک سیستم Engagement است که قرار است حجم زیادی از داده را مدیریت و پردازش کند. این سیستم باید امکان تعریف و اجرای کمپین‌های مختلف با ساختارهای متفاوت را داشته باشد. تعداد کاربران هر کمپین نیز می‌تواند حدود ۴۰۰ هزار نفر باشد.سگمنت کاربران از سیستم‌های مختلفی مانند Metabase و Superset دریافت می‌شود. سپس سیستم باید این داده‌ها را پردازش کرده و بر اساس تنظیمات هر کمپین، اقداماتی مانند ارسال Push Notification، پیامک یا ایمیل را انجام دهد.در ادامه، عملکرد کمپین و KPIهای مربوط به آن محاسبه می‌شود تا بتوان از نتایج به‌دست‌آمده برای برنامه‌ریزی ارسال‌های بعدی و ایجاد سگمنت‌های جدید استفاده کرد.این پروژه در پاسخ به نیاز تیم داده طراحی شده است؛ تیمی که به سیستمی برای دریافت سگمنت از منابع مختلف، اجرای کمپین، ارسال پیام و اندازه‌گیری نتایج آن نیاز داشت.از کجا شروع کردیم؟پروژه از یک نیاز مشخص در تیم داده شروع شد: نیاز به بستری که بتواند سگمنت کاربران را از منابع مختلف دریافت کند، روی حجم بالایی از داده پردازش انجام دهد، کمپین‌های ارتباطی را اجرا کند و نتیجه هر اجرا را قابل اندازه‌گیری کند.این سیستم در نهایت یک پلتفرم Engagement است. هر کمپین می‌تواند حدود ۴۰۰ هزار کاربر داشته باشد و بسته به تنظیمات خود، از کانال‌هایی مانند Push Notification، پیامک و ایمیل استفاده کند. داده کاربران نیز قرار است از منابعی مانند Metabase و Superset وارد سیستم شود.اما هدف فقط ارسال پیام نبود. بخش مهم‌تر این بود که بعد از هر ارسال بتوانیم نتیجه کمپین را بررسی کنیم، KPIها را محاسبه کنیم و بر اساس داده‌های به‌دست‌آمده، برای ارسال‌ها و سگمنت‌های بعدی تصمیم بگیریم.---مشخص‌کردن واقعیت‌ها و محدودیت‌هاپیش از بررسی Featureها، ابتدا واقعیت‌های مسئله و محدودیت‌های موجود را مشخص کردیم.از یک طرف، سیستم باید توان اجرای کمپین روی چندصد هزار کاربر را داشته باشد. از طرف دیگر، سگمنت کاربران در خود سیستم تولید نمی‌شد و باید از Data Providerهای مختلف دریافت می‌شد. همچنین هر کمپین می‌توانست ساختار، زمان‌بندی، کانال ارسال و منطق متفاوتی داشته باشد.محدودیت مهم‌تر، نبود Tracking مستقیم و یکپارچه برای تمام رفتارهای کاربر بود. بنابراین امکان این‌که صرفاً با Event Tracking مشخص کنیم هر کاربر بعد از دریافت پیام چه عملی انجام داده، همیشه وجود نداشت.این محدودیت از همان ابتدا روی طراحی سیستم اثر گذاشت و باعث شد مدل اندازه‌گیری نتیجه، یکی از تصمیم‌های اصلی پروژه باشد؛ نه صرفاً یک Feature جانبی.---بررسی محصولات مشابه و استخراج قابلیت‌هاپس از روشن شدن مسئله و محدودیت‌ها، وارد مرحله Product Discovery و Benchmarking شدیم.با کمک ابزارهای هوش مصنوعی، مدل‌های مختلف و بررسی نمونه‌های موجود، قابلیت‌هایی که یک سیستم Engagement یا Campaign Management می‌تواند داشته باشد استخراج شدند. در این مرحله، هوش مصنوعی نقش ابزار تحقیق، دسته‌بندی و چالش‌کردن فرض‌ها را داشت؛ نه تصمیم‌گیرنده نهایی.قابلیت‌های استخراج‌شده شامل مواردی مانند مدیریت کمپین، دریافت سگمنت، زمان‌بندی اجرا، مدیریت کانال‌های ارسال، قالب پیام، اجرای آزمایشی، کنترل نسخه، گزارش‌گیری، محاسبه KPI، مدیریت Goal و تحلیل نتیجه کمپین‌ها بودند.---تحلیل نیازمندی‌ها و شکاف‌هادر مرحله بعد، قابلیت‌های استخراج‌شده با نیاز واقعی تیم داده تطبیق داده شدند.هدف این نبود که تمام Featureهای محصولات دیگر را وارد سیستم کنیم. بررسی کردیم کدام قابلیت‌ها برای نسخه اول ضروری هستند، کدام‌ها باید به مراحل بعدی منتقل شوند و چه نیازهایی داریم که در نمونه‌های بررسی‌شده، دقیقاً با مدل کاری ما پوشش داده نشده‌اند.خروجی این مرحله یک تحلیل شکاف یا Gap Analysis بود: تفاوت بین قابلیت‌های متداول سیستم‌های مشابه و نیازهای اختصاصی پروژه.---طراحی روش اندازه‌گیری Goal و KPIیکی از مهم‌ترین شکاف‌ها، روش اندازه‌گیری نتیجه کمپین بود.به دلیل نبود Tracking مستقیم برای همه رفتارهای کاربران، تصمیم گرفتیم اندازه‌گیری Goal و KPI بر اساس داده‌های موجود و Queryهای تیم داده انجام شود. برای هر اجرای کمپین یا Campaign Run، یک UTM یا شناسه قابل ردیابی در نظر گرفته می‌شود تا داده‌های بعدی بتوانند به همان اجرا مرتبط شوند.سپس Goalها با Query مشخص می‌کنند که کاربر هدف، رفتار موردنظر را انجام داده است یا نه؛ برای مثال ثبت‌نام، ورود، خرید، سرمایه‌گذاری یا هر اقدام دیگری که برای کمپین تعریف شده باشد.KPIها نیز روی خروجی Goalها و داده‌های مرتبط محاسبه می‌شوند؛ مانند تعداد کاربران تبدیل‌شده، نرخ تبدیل، مجموع مبلغ، میانگین مبلغ یا مقایسه عملکرد نسخه‌های مختلف یک کمپین.این تصمیم باعث شد سیستم به یک ابزار Tracking خاص وابسته نباشد و بتواند از منابع داده و زیرساخت تحلیلی فعلی تیم داده استفاده کند.---تعیین محدوده نسخه اولدر پایان این مرحله، قابلیت‌های ضروری سیستم در قالب یک Capability Checklist سطح‌بالا جمع‌بندی شدند.این چک‌لیست مشخص می‌کرد نسخه اول سیستم باید بتواند سگمنت را از منابع داده دریافت کند، کمپین تعریف کند، Run قابل کنترل ایجاد کند، پیام را از کانال مناسب ارسال کند و نتیجه را با Goal و KPI اندازه‌گیری کند.این سند وارد جزئیات پیاده‌سازی نمی‌شد؛ وظیفه‌اش کنترل Scope بود تا در ادامه مشخص باشد هر Feature جدید واقعاً به نیاز پروژه مرتبط است یا صرفاً پیچیدگی اضافه می‌کند.---ورود به طراحی فنیبعد از تثبیت نیازمندی‌ها، قابلیت‌های ضروری و مدل اندازه‌گیری، وارد مرحله Technical Discovery و طراحی معماری اولیه شدیم.در این مرحله باید مشخص می‌شد سیستم چگونه داده را از Data Providerها دریافت می‌کند، چگونه حجم بالای کاربران را پردازش می‌کند، اجرای Campaign Runها چگونه مدیریت می‌شود، ارسال از کانال‌های مختلف چگونه توسعه‌پذیر باقی می‌ماند و داده‌های لازم برای محاسبه KPI و Goal در چه ساختاری ثبت می‌شوند.هم‌زمان، زیرساخت موجود، سرویس‌های در دسترس، APIها، محدودیت‌های دسترسی به داده و ظرفیت اجرای پردازش‌های حجیم بررسی شدند تا طراحی اولیه با واقعیت‌های محیط اجرا سازگار باشد.ادامه در پست بعدی</description><category>امیر توحیدی</category><author>امیر توحیدی</author><pubDate>Mon, 24 Aug 2026 16:35:42 +0330</pubDate></item><item><title>نحوه wipe  و غیرقابل بازگشت کردن اطلاعات لپ تاپ</title><link>https://virgool.ir/@amir.tohidi/نحوه-wipe-و-غیرقابل-بازگشت-کردن-اطلاعات-لپ-تاپ-sn3n7zr8mdqu</link><description>سلام وقت بخیر من کاری که می خواستم کنم برای فروختن لپ تاپ کل هارد های لپ تاپ بیام کاری کنم که غیر قابل ریکاوری باشه و با خیال راحت بفروشمش خیلی نرم افزار های مختلف و فلش bootable  اینارو تست کردم یه جورایی شدنی نبود به خاطر مدل bios  به مشکلات مختلفی می خورد راه حل اخری که رسیدم ساخت فلش bootable ubuntu  و استفاده Shared در کامند و حذف اطلاعات هارد ها مرحله 1 ساخت فلش bootable https://www.zoomit.ir/computer-learning/388244-install-linux-on-usb-flash/#3af9b2e8-b826-4ec5-969a-877537b3a931مرحله دوم پیدا کردن هارد با دستور زیر هارد مورد نظر را پیدا می کنیم lsblkهارد مورد نظر پیدا می کنیم از میزان حجم هارد ها میتونید این پیدا کنید معمولا هارد اصلی sdb sda  میتونید پیدا کنید مرحله سوم اجرا دستور غیرقابل بازگشت کردن اطلاعات هاردsudo shred -v -n 3 -z /dev/sdaبا این دستور این کار ها انجام می شود توضیح آپشن‌ها:-v → نمایش پیشرفت-n 3 → سه بار بازنویسی-z → در آخر یکبار با صفر پر می‌کنهبرای اطلاعات بیشتر هم از chatgpt  میتونید استفاده کنید خیلی زمان بر هست ممکنه 2 یا 3 ساعت بر اساس حجم هارد زمان ببره کلیاتش بگم این دستور سه بار رندم روی هارد مینویسه و در مرحله اخر همش با صفر پر می کنه </description><category>امیر توحیدی</category><author>امیر توحیدی</author><pubDate>Tue, 19 Aug 2025 10:17:28 +0330</pubDate></item><item><title>تجربه استفاده از redis در تعداد درخواست بالا در NODEJS</title><link>https://virgool.ir/@amir.tohidi/تجربه-استفاده-از-redis-در-تعداد-درخواست-بالا-در-nodejs-natrb2fqpndg</link><description>سلام وقت بخیر من می خوام از تجربه خودم در استفاده از Redis در تعداد درخواست های بالا  یک سری توضیحات بدم در حالتی که تعداد نوشتن و خواندن به مقدار نمایی در لحظه افزایش پیدا می کند.در تجربه ای که بدست اومد ما در یکی از سرویس ها که با NODEJS و typescript توسعه میدیم از یک package  به نام ioredis برای ارتباط با ردیس استفاده کردیم . https://www.npmjs.com/package/ioredis چالشی که در استفاده از این برای پیش اومد در زمینه مدیریت ارتباط با Redis  ایجاد کرد.در حالت عادی این پکیج و یا درایور برای استفاده از ردیس به سادگی کار می کند و نوشتن و خواندن از ردیس را مدیریت می کند. چالش زماین ایجاد می شود در یک لحظه به طور مثال درخواست به مقدار حدود 15 هزار درخواست خواندن و نوشتن در لحظه برای ردیس ایجاد می شود. به علت مدیریت کانکشن به این صورت که برای هر درخواست جدید یک کانکشن باز می کند با رسیدن این سقف به 5 هزار کانکشن ردیس دچار مشکل می شود و نمی تواند درخواست به درستی هندل کند. const Redis = require(&#34;ioredis&#34;);&#xA;&#xA;const redis = new Redis();&#xA;&#xA;redis.set(&#34;mykey&#34;, &#34;value&#34;); &#xA;&#xA;redis.get(&#34;mykey&#34;, (err, result) =&gt; {&#xA;  if (err) {&#xA;    console.error(err);&#xA;  } else {&#xA;    console.log(result); // Prints &#34;value&#34;&#xA;  }&#xA;});&#xA;&#xA;redis.get(&#34;mykey&#34;).then((result) =&gt; {&#xA;  console.log(result); // Prints &#34;value&#34;&#xA;});&#xA; در قسمت بالا نمونه ای که خود نویسنده پکیج ارائه کرده قرار دادم . البته در استفاده از این باید در تنظیمات ردیس بسیار دقت شود تا مدت بسته شدن کانکشن ردیس در صورت idle بودن با دقت مناسبی تنظیم شود چون در واقع هر ارتباط به صورت یکبار مصرف استفاده می شود و لزومی به نگهداری idle ها نیست بعد از بررسی پکیج های موجود تقریبا 4 پکیج اصلی وجود دارد که این  پکیج  سایت رسمی Redis معرفی کرده است. https://github.com/redis/node-redis نمونه نحوه استفاده از این درایور می تونید مشاهده کنید const client = createClient();&#xA;&#xA;client.on(&#39;error&#39;, err =&gt; console.log(&#39;Redis Client Error&#39;, err));&#xA;&#xA;await client.connect();&#xA;&#xA;await client.set(&#39;key&#39;, &#39;value&#39;);&#xA;const value = await client.get(&#39;key&#39;);&#xA;await client.disconnect();در استفاده از این پکیج مدیریت ارتباط با ردیس در واقع در اختیار ما قرا رمیگیره با توجه به ساختار پروژه می توانید در موقع استارت پروژه یک connection با ردیس باز کنید.و درخواست ها از اون طریق با ردیس رد و بدل کنید . و با مدیریت و استفاده از event ها ارتباط مدیریت کنید .</description><category>امیر توحیدی</category><author>امیر توحیدی</author><pubDate>Wed, 03 May 2023 23:43:02 +0330</pubDate></item><item><title>فعال کردن gzip بر روی یک route خاص nodejs</title><link>https://virgool.ir/@amir.tohidi/فعال-کردن-gzip-بر-روی-یک-route-خاص-nodejs-fd87t5j7mfod</link><description>nodejs gzip expressبرای فعال سازی gzip بر روی route  خاص بر روی کد nodejs و Express به روش زیر به راحتی می توانیم انجام بدیم .برای این کار بایستی از middleware  به نام compression  استفاده کنیم . به راحتی با دستور زیر می توانید این پکیج اضافه کنید npm install compressionسپس این middleware به کد خود اضافه می کنیم const express = require(&#39;express&#39;);&#xA;const compression = require(&#39;compression&#39;);&#xA;const app = express();&#xA;app.get(&#39;/my-route&#39;, compression(), (req, res) =&gt; {&#xA;  // اجرای کد دیگر&#xA;});در مثال بالا، middleware compression به عنوان یک تابع فراخوانی شده و به عنوان آخرین پارامتر تابع app.get() در خط سوم اضافه شده است. این کار باعث می‌شود که فشرده‌سازی gzip درخواست‌های ارسالی به این روت اعمال شود.اگر می‌خواهید تنظیمات بیشتری برای فشرده‌سازی gzip اعمال کنید، می‌توانید به صورت زیر این middleware را فراخوانی کنید:&#xA;const express = require(&#39;express&#39;);&#xA;const compression = require(&#39;compression&#39;);&#xA;const app = express();&#xA;app.use(compression({&#xA;  level: 6, // سطح فشرده‌سازی (1-9)&#xA;  threshold: 1000, // حداقل حجم داده برای اعمال فشرده‌سازی&#xA;}));در مثال بالا، تنظیماتی مانند سطح فشرده‌سازی و حداقل حجم داده برای اعمال فشرده‌سازی، به صورت پارامترهای ورودی به middleware compression ارسال شده‌اند. در این حالت، تمامی روت‌هایی که به app.use() اضافه شده‌اند، فشرده‌سازی gzip را استفاده می کنند.</description><category>امیر توحیدی</category><author>امیر توحیدی</author><pubDate>Wed, 01 Mar 2023 10:25:35 +0330</pubDate></item><item><title>مانیتورینگ سرویس nodejs توسط prometheus</title><link>https://virgool.ir/@amir.tohidi/مانیتورینگ-سرویس-nodejs-توسط-prometheus-qeou7l89xsz3</link><description>prometheus and nodejsیکی از موارد جذاب در کد نویسی بحث مانیتورینگ کد و نمایش آن است که همیشه برای خود من از موارد جذاب بوده. در سرویسی که در حال حاضر بر روی ان در حال کار هستم که با nodejs و typescript  پیاده سازی شده است از prometheus  برای ثبت داده های جهت ارسال به مانیتورینگ استفاده کرده است . به زبان ساده بخوام سیستم کاری آن را توضیح بدم سرویس که راه اندازی میشود به زبان ساده یه متغیر عمومی روی اون کد کامپایل شده ایجاد می کند که شما از قبل مشخص کردید یه سری متغیر بر روی ان وجود دارید و شما در قسمت های مختلف کد می توانید این مقادیر را تغییر دهید . این مقادیر می تواند از نوع Guage باشه که تنها صفر و یک را دریافت می کند. یا می تواند به صورت عدد باشد که با ساختار مختلف می توانید این مقادیر را کاهش و افزایش دهید . و توسط این ساختار می توانید یک روت توسط Express روی کد تعیین نمایید و از روی سیستم مربوطه prometheus  این روت را صدا کرده و مقادیر را برای نمایش در سیستم گرافانا جمع اوری کنید.. چالش کجاست !؟چالش وقتی جالب میشه که شما چند instance از اون سرویس دارید و یا وقتی که سرویس ریستارت میشه . و لزوما ریاستارت نیست و شما دپلوی روی اون سرویس انجام بدید و ریبیلد بشه هم این مسئله هست که مقادیر صفر و به حالت دیفالت باز می گردد. !!من خیلی در مورد استفاده از این مورد جستجو کردم که مورد اماده و روتینی برای یه جورایی persist کردن این مقادیر روی هارد یا ردیس یا هر سیستم نگهداری دیگه ای وجود ندارد . راه حل این که مقادیر prometheus   persist نمیشه کرد چیه !؟من بعد از کلی فسفر سوزوندن به یه راه حلی رسیدم که نمیشه گفت درسته یا غلط شاید برای من بهترین کار بوده ! من کاری که انجام دادم .من اومدم که داده های مهم بودن یه کاری انجام دادم یه سری روت اختصاصی درست کردم . ابتدای اون روت const registery = new prometheus.Registry();رجیستری جدید ایجاد کردم و اون موردی که می خواستم داده اش از روی بانک اطلاعاتی یا کش ردیس ایجاد کرده بودم و ذخیره کرده بودم دریافت و در خروجی جهت خوندن توسط prometheus ارسال کردم . const registery = new prometheus.Registry();&#xA;try {&#xA;  await Service.routeForCheckMonitoringSystem(&#39;2022-12-04&#39;);&#xA;  MetricsType.ExternalService.set(1);&#xA;} catch (error) {&#xA;  MetricsType.ExternalService.set(0);&#xA;} finally {&#xA;  registery.registerMetric(MetricsType.ExternalService);&#xA;  res.set(&#39;Content-Type&#39;, registery.contentType);&#xA;  res.end(await registery.metrics());&#xA;}این طوری داده چون وابسته به ساختاری prometheus اون کردیم به درستی کار می کند و تست شده است. اگر شما برای این مورد راهکاری میشناسید خوشحال میشم تو کامنت ها برای من بزارید </description><category>امیر توحیدی</category><author>امیر توحیدی</author><pubDate>Sat, 18 Feb 2023 02:06:16 +0330</pubDate></item><item><title>نوشتن Declare module  برای یک پکیج در typescript</title><link>https://virgool.ir/@amir.tohidi/نوشتن-decalare-module-برای-یک-پکیج-در-typescript-bwha66onl2qf</link><description>typescript declare moduleیکی از چالش هایی که در استفاده از زبان typescript  برای reactjs یا nodejs استفاده از پکیج های javascript  در این زبان هست معمولا برای استفاده از این پکیج ها ، پکیج های جاوا اسکریپت معروف با سرچ @types/package_nameمیتونید پیدا کنید . اما برای مواردی که پیدا نمیشه کار سخت میشه اصولابایستی از قابلیت decalare  برای این کار استفاده کرد . به طور مثال من در یه پروژه ای که در حال کار بودم نیاز بود تغییرات هر رکورد در Mongodb ثبت شود.برای این کار از کتابخانه ای به نام mongoose-history استفاده کنم البته یه کم و کسری داشت که مجبور شدم یه fork بگیرم خودم کاملش کنم https://github.com/tohidiamir/mongoose-historyبعد می خواستم پروژه nodejs که داشتم از این کتابخانه که با javascript نوشته شده بود استفاده کنم برای این کار بایستی یه فایل به این صورت در پروژه درست کنم mongoose-history-plug.d.tsو به این صورت توابع و کلاس هایی که داخل این کتابخانه پیاده سازی کرده بودم و استفاده می شد یه جورایی براشون type هاش تعیین کنم که قابل استفاده بشن . declare module &#39;mongoose-history-plug&#39; {&#xA;&#xA;    import type { Schema} from &#39;mongoose&#39;;&#xA;    import mongoose from &#34;mongoose&#34;&#xA;&#xA;    export type MetaDataObject = {&#xA;        key : string;&#xA;        value : unknown;&#xA;    }&#xA;&#xA;    export type MongoosePatchHistoryOptions = {&#xA;        indexes?: Record&lt;string , Record&lt;string, unknown&gt;&gt;;&#xA;        customCollectionName?: string;&#xA;        historyConnection? : mongoose.Connection&#xA;        diffOnly? : boolean,&#xA;        metadata? : MetaDataObject[]&#xA;        customDiffAlgo? : unknown&#xA;    };&#xA;&#xA;    export default function (&#xA;        schema: Schema,&#xA;        options: MongoosePatchHistoryOptions,&#xA;    ): void;&#xA;}و برای این که موقع compile کد ها از typescript  به جاوااسکریپت از این استفاده شود بایستی فایل tsconfig.jsonبه این صورت ادیت کنم {&#34;compilerOptions&#34;:{&#34;module&#34;:&#34;commonjs&#34;,&#34;esModuleInterop&#34;:true,&#34;allowSyntheticDefaultImports&#34;:true,&#34;target&#34;:&#34;es2019&#34;,&#34;noImplicitAny&#34;:true,&#34;moduleResolution&#34;:&#34;node&#34;,&#34;sourceMap&#34;:true,&#34;outDir&#34;:&#34;dist&#34;,&#34;baseUrl&#34;:&#34;.&#34;,&#34;paths&#34;:{&#34;*&#34;:[&#34;node_modules/*&#34;,&#34;src/types/*&#34;,&#34;src/*&#34;]}},&#34;include&#34;:[&#34;src/**/*&#34;],&#34;files&#34;:[&#34;src/types/custom.d.ts&#34;&#xA;,&#34;src/types/mongoose-history-plug.d.ts&#34;&#xA;]} این مواردی که روی نمونه توضیح دادم گفتم شاید به کار دوستان بیاد. :)اگر مشکلی بود خوشحال میشم برام بنویسید.</description><category>امیر توحیدی</category><author>امیر توحیدی</author><pubDate>Sat, 18 Feb 2023 01:39:24 +0330</pubDate></item><item><title>باگ mongoos برای استفاده از قابلیت Read روی کوئری</title><link>https://virgool.ir/@amir.tohidi/باگ-mongoos-برای-استفاده-از-قابلیت-read-روی-کوئری-vjbqdtu1e0oy</link><description>bug mongoose readوقتی که روی mongodb  از قابلیت replica  استفاده میشه و به صورت cluster راه اندازی میشه یک سری قابلیت وجود داره که بر روی درایور های mongodb  روی زبان های مختلف ایجاد میشه . محیطی که من می خوام باگش توضیح بدم شاید برای شما هم ممکنه پیش بیاد روی nodejs  و mongoose هست . &#34;node&#34;: &#34;14 .17&#34;&#34;mongoose&#34;: &#34;^5.10.9&#34;اتفاقی که میفته روی حالت Cluster  شما یک primary  برای مجموع سرور ها ایجاد می کنید و بقیه سرور های به نام secondary مشخص می گردد. در این حالت مسئله ای که پیش میاد شما دو راه برای استفاده از این قابلیت روی nodejs  دارید.یعنی تنظیمات انجام بدید خواندن از روی primary  اصلی انجام بشه یا از یکی از Secondary  ها . اول این تنظیمات مشخص کنم primary :در این حالت اتفاقی که میفته اطلاعات تنها از روی primary  خونده میشه و در صورت مشکل داشتن primary  اطلاعات از روی secondary به هیچ وجه خوانده نمی شود. primary preferred : در این حالت اطلاعات از روی primary  خونده میشه ترجیحا!! در صورت در دسترسی نبودن سراغ Secondary ها میره secondary :در این حالت اطلاعات از روی Secondary  خونده میشه ( از یکی از Secondary ها) و در صورت در دسترس نبودن هیچ کدام از Secondary ها به هیچ وجه سراغ primary  نمیره secondary preferred :در این حالت اطلاعات ترجیحا از Secondary ها خونده میشه و در صورت در دسترسی نبود از primary  خونده میشه.nearest :بر اساس مستندات mongodb  بر اساس پینگ و در دسترسی بودن سرور ها یکی از سرور هارو بدون توجه به primary , secondary  بودن انتخاب می کنه .چالش بعدی این هست که بعد از انتخاب این تنظیمات این تنظیمات در کجا به mongoose اطلاع بدیم .دو راه وجود دارد به صورت کلی readPreferrence بر روی کانشکن اصلی که تنظیم می کنیم مشخص کنیم . بر روی هر کوئری با استفاده .read() این خواندن و نوشتن را مشخص نماییم . توجه : ممکن این سوال پیش بیاد چرا روی Secondary که معمولا تعداد بیشتری هستند همیشه برای خواندن استفاده نمی کنیم به این علت که در صورتی که یه کاری در بازه زمانی کوتاه نیاز به بررسی و ثبت در بانک اطلاعات دارد دچار چالش می شویم .چون اطلاعات بر روی Secondary ها با تاخیر نوشته می شود.البته برای این چالش این که مطمئن بشیم بر روی Secondary ها نوشته شده است و بعد سراغ ادامه کار بریم هم حالتی وجود دارد که می توانیم بر روی قسمت هایی که insert , update  انجام میدیم از writeConcern استفاده کنیم که این اطمینان به ما میده که روی تمام کلاستر ها به صورت صحیح نوشته شده است اما ممکن است روند کارها را دچار اختلال و یا کنید کند. ادامه مطلب :::تا اینجا چالش خاصی نبوده همه واضح هست اما یه مشکلی که وجود داره وقتی روی mongoose شما روی کانکشن یه حالتی مانند primary  تنظیم کردید و روی کوئری مقدار Secondary  را تنظیم کردید اتفاقی که میفته با هر درخواست بر روی Secondary  یه درخواست به سمت primary  ارسال میشه . (این باگ به صورت کامل تست و راستی آزمایی شده است )راهکار :بعد از بررسی های بسیار راهکاری که تست شد و بر روی پروژه پیاده سازی شد این حالت شد که روی کانکشن اصلی روی Secondary قرار دهیم و محل های چالش بر انگیز که نیاز به اپدیت بودن دیتابیس هست بر روی کوئری مقدار primary  را تنظیم کنیم . </description><category>امیر توحیدی</category><author>امیر توحیدی</author><pubDate>Sun, 28 Aug 2022 09:46:07 +0430</pubDate></item><item><title>تست nextjs  بر روی تلویزیون webos قدیمی</title><link>https://virgool.ir/@amir.tohidi/تست-nextjs-بر-روی-تلویزیون-webos-قدیمی-qrti6qgdojpm</link><description>یک مشکل جدیدی که در پروژه جدید بهش برخوردیم در مورد بررسی کارکرد پروژه web application بر روی مرورگرهای قدیمی و رفع مشکل اون هاست. اول چیزی که به ذهن میرسه استفاده از simulation  هایی که خود lg برای تست نرم افزار ها داده است. اما راه اندازی استفاده از این شبیه ساز ها کلی دردسر داره و چیزی که می خوای برای تست و دیباگ بهمون نمیده.من از یه راه دیگه رفتم ، بررسی کردم ببین روی تلویزیون های LG که دارن از Webos استفاده می کنند دارند از چه چیزی استفاده می کنند . همین طور که میبینید داره از chromium استفاده می کند.چیزی که برای ما نیاز بود و استفاده کردیم webos 3 بود و نیاز به chromium38  بودش. برای نصب این خودش یک چالشی بود که خیلی جاهارو من گشتم بهترین راه استفاده از  choco choco install chromium --version=38.0.2125.0با این دستور روی ویندوز میتونید به راحتی مرورگر مورد نظر کرومیوم با ورژن 38 نصب و راه اندازی کنید.اولین مشکل روی تست نرم افزار این که محیطش شما داشته باشی و بتونی لاگ بگیری تست کنید.در مرحله بعد من اومدم دنبال رفع مشکل یه ساختاری از babeljs  تونستیم طراحی کنیم و بنویسیم که نمونه کد هاش روی اینترنت فراوان هست و بنا به هر پروژه متغیر هست که در موقع بیلد خروجی مورد نظر روی nextjs خام داشته باشیم . اما با اضافه کردن پلاگین های پروژه اصلی دوباره به مشکل خوردیم در این مرحله اومدیم تک به تک package های نصب شده اضافه و تست کردیم و اخر سر به این قسمت که مشکل نمایش از کدام قسمت است رسیدیمدر پروژه ما به علت استفاده از formik اومده بودیم از yup استفاده کردیم که این پکیج ناسازگار بود با این ورژن مرورگر ،  که حالا راهکارهای دیگه ای برای این کار در نظر گرفتیم.این کار بیشتر حالت تجربی داره و باید ساختار های مختلف تست کنید تا بتونید به نتیجه ای که می خواید برسید. و من توی respositoryخود  nextjs و جاهای دیگه جستجو کردم همه به این نتیجه رسیدن با آزمون و خطا و آزمایش میشه به نتیجه رسید.امیدوارم مفید بوده باشه.</description><category>امیر توحیدی</category><author>امیر توحیدی</author><pubDate>Sun, 30 Jan 2022 10:25:40 +0330</pubDate></item><item><title>راه اندازی خانه هوشمند خودتون :))</title><link>https://virgool.ir/@amir.tohidi/راه-اندازی-خانه-هوشمند-خودتون-rxxwciqgmpzh</link><description>سلام من چندین سال بود داشتم تحقیق می کردم و می گشتم به چه صورت با هزینه کم می تونم یه سری از موارد خانه به صورت هوشمند در بیارم.بعد از تحقیقات زیاد به برد WEMOS D1 MINI رسیدم.من در این پروژه کاری که می خوام انجام بدم این هست که یک پمپ و چراغ های حیاط و انباری را کنترل کنم با این سیستم . Wemos d1 miniاین قطعه دارای ESP8266 است. توسط این ماژول می توانید به راحتی به وای فای که در خونه دارید متصل بشید. (یعنی آردینو WEMOS D1 MINI می تواند به اینترنت دسترسی داشته باشد این خیلی مهمه.) نحوه راه اندازی و استفادش براتون روی گیت هاب این قسمت قرار میدم. https://github.com/tohidiamir/esp8266_web_request این قطعه در حال حاضر آذر ماه 1400 حدود 100 هزار تومان پول این برد هستش . برای راه اندازی و استفاده از این برد به یک سری وسایل دیگه نیاز دارید که در ادامه هر کدام توضیح میدم.اولین چیزی که شاید به ذهنتون برسه روشن کردن و برق دادن بهش هست .شما برای روشن کردن اون به صورت آزمایشی و کانفیگ وای فای نیاز به برق خواستی ندارید و توسط USB لپ تاپ و یه کابل میکرو یو اس بی بهش متصل بشید و توسط  Arduino IDE ارتباط بگیرید و کدی که در بالا قرار دادم بهش اضافه کنید. البته روی Arduino IDE ابتدا باید کتابخانه مربوط به ESP8266 اضافه کنید روشش زیر میتونید ببینید. https://digispark.ir/esp8266-tutorial-step-two-install-core/ الان شما در این مرحله شما ارتباط به Wemos برقرار کردید و تنظیمات مربوط به اتصال به وا فای را نیز انجام دادید. از روی گیت کد کامپایل کنید بر روی Wemos بریزید در روی کد یک سری از قسمت هارو باید تنظیم کنید const char* ssid = &#34;ssidwifi&#34;// ssid wifi ke mikhaid motasel beshidconst char* password = &#34;passwifi&#34; // password wifi ke mikhaid behesh vasl beshid //Your Domain name with URL path or IP address with pathconst String serverName = &#34;http://SERVER/arduino_recive.php&#34; // dar inja server bayad domain bashad ke mikhaid file php roie on bargozari konid تنظیمات بالارو انجام دادید و کد کامپایل کنید و در قسمت سریال مانیتور باز کنید باید اتصال به شبکه به شما نشون بده. خوب با کدهایی که داده شده به شما و تنظیماتی که انجام گرفته شما آردینو راه اندازی کردید.الان نیاز به یک سروری دارید که این اطلاعات از آردینو باید دریافت شده و اطلاعات جدید را از سرور دریافت کند.برای راه اندازی این سرور نیاز به یک هاست PHP دارید وی ک دامنه که روی هاست تنظیم شده باشه . &lt;?php&#xA;&#xA;&#xA;    $pdo = new PDO(&#39;mysql:host=localhost;dbname=DATABASE&#39;, &#39;USERNAME&#39;, &#39;PASSWORD&#39;);&#xA;&#xA;    $selectSQL = &#34;SELECT * from value_server&#34;&#xA;    $stmt = $pdo-&gt;prepare($selectSQL);&#xA;    $stmt-&gt;execute();&#xA;    $data = [];&#xA;    while($abc = $stmt-&gt;fetch())&#xA;    {&#xA;        $data[$abc[&#39;name&#39;]] = ($abc[&#39;name&#39;] != &#39;last_update&#39;)?intval($abc[&#39;value&#39;]):$abc[&#39;value&#39;];&#xA;&#xA;    }&#xA;    #echo $rowCount = count($stmt);&#xA;    #die;&#xA;&#xA;&#xA;    $selectSQL = &#34;update value_server set value = &#39;&#34;.date(&#34;Y-m-d H:i:s&#34;).&#34;&#39; where name = &#39;last_update&#39;&#34;&#xA;    //echo $selectSQL;&#xA;    $stmt = $pdo-&gt;prepare($selectSQL);&#xA;    $stmt-&gt;execute();&#xA;&#xA;    $selectSQL = &#34;update value_server set value = &#39;&#34;.$_GET[&#39;pir_anbar&#39;].&#34;&#39; where name = &#39;pir_anbar&#39;&#34;&#xA;    //echo $selectSQL;&#xA;    $stmt = $pdo-&gt;prepare($selectSQL);&#xA;    $stmt-&gt;execute();&#xA;&#xA;&#xA;&#xA;    echo json_encode($data);&#xA;کد بالا به این صورت عمل می کند درخواستی که آردینو دریافت می کند وضعیت چشم انباری را به روز می کند و همین طور اطلاعات جدید که دریافت کرده است بر میگرداند.این یه کد کوچولو بود برای ساخت یه سرور به همراه یه سری کد برای استفاده روی Wemos  کانفیگ شبکه و بقیه داستان هاش که توضیح دادم شاید به نظر گیج کننده بیاد حالا هر مشکلی داشتی می تونم راهنماییت کنم البته یک قسمت از کار که آپ فلاترش هست بعدا منتشر می کنم به این پست اضافه می کنمش :)</description><category>امیر توحیدی</category><author>امیر توحیدی</author><pubDate>Wed, 29 Dec 2021 17:36:31 +0330</pubDate></item><item><title>آموزش sync کردن دو فولدر در Centos</title><link>https://virgool.ir/@amir.tohidi/آموزش-sync-کردن-دو-فولدر-در-centos-lzjktvyqlmlp</link><description>بک آپ گیری با rsyncبرای بک اپ گیری چند سناریو قابل انجام هست اما من موردی که خودم انجام دادم به نظرم کاربردی بوده اینجا براتون توضیح میدم من یه سری فایل های مهم در مورد نرم افزار یک شرکتی که نگهداری می کنم وجود داشت که با توجه به مشکلی که برای آروان اول سال 1400 اتفاق افتاد مهم بود این نگرانی بود که ممکنه دوباره این اتفاق بیفته و همه اطلاعات از بین برود. من اومدم کاری که انجام دادم یک سرور روی یکی دیتاسنتر دیگه تهیه کردم روش یک cenots راه اندازی کردم.توی مرحله بعدی اومدم یک اسکریپت با توجه با ساختار rsync  درست کردم و کاری کردم که فولدر اطلاعات و فایل های حیاطی در بازه های مشخص با این سرور همگام سازی بشود.خوبی این دستور این هست که لازم نیست همه ی اطلاعات دوباره دریافت شود و Rsync فولدر بررسی می کند و فایل های تغییر کرده و یا جدید دوباره دریافت می کند .  rsync -avz -e &#39;ssh -p PORTSSH&#39; root@IPSERVER:/var/www /home/backupاول مقادیر بالارو توضیح بدم PORTSSH  = به صورت دیفالت مقدار 22 هست و شما می توانید در صورتی که از پورت دیفالت استفاده نمی کنید این مقدار را مشخص نماییدIPSERVER = ای پی سروری است که شما قصد دریافت اطلاعات از آن را دارید/var/www = محل فولدری که قصد کپی آن را دارید /home/backup=  محلی که فولدر کپی شده را قرار میدهید.خوب تا اینجا مورد خاصی نبوده حالا شما می خواید این موارد به صورت خودکار در بازه های زمانی مشخص انجام شود . برای این کار باز راه های مختلفی وجود دارد کاری که بنده تست کردم به این صورت بود. یک فایل bash ساختم و به این صورت نوشتم #!/usr/bin/expectrsync -avz -e &#39;ssh -p PORTSSH&#39; root@IPSERVER:/var/www /home/backupexit 1خوب این دستور وقتی اجرا کنید برای ارتباط با سرور promptبرای دریافت پسورد باز می شود و شما مجبور هستید پسورد دستی وارد کنید و به صورت خودکار اجرا نخواهد شد . برای راه حل این مشکل میتنید sshpass استفاده کنید برای نصب این پکیج از yum به این صورت عمل می کنید.yum install sshpassبرای استفاده از این دستور فایل bash ساخته شده به صورت زیر اصلاح می کنیم.#!/usr/bin/expectsshpass -p &#34;PASSWORD&#34; rsync -avz -e &#39;ssh -p PORTSSH&#39; root@IPSERVER:/var/www /home/backup&#xA;exit 1خوب حالا میتونید این طریق این دستور فایل بدون نیاز به پسورد اجرا کنید . به صورت مثال فایل ما اگر backup-script.sh باشد با دستور bash backup-script.shفایل اجرا کنیدتا اینجا ما تونستیم اسکریپت برای دریافت اطلاعات به درستی انجام بدیم و بسازیم خوب برای خودکار اجرا شدنش میتونیم از ساختار crontab بر روی سرور Centos  استفاده کنیم با دستور crontab -eبرای نوشتن دستور مورد نظر باز میشه در صورتی که فایل مورد نظر شما در فولدر home  باشد و اسم فایل bacup-script.shدستور به این صورت می شود برای اجرا روزانه ساعت 1 صبح 0 1 * * * /home/backup-script.sh &gt;&gt; backup.logsبا دستور بالا هر روز ساعت 1 صبح این دستور اجرا می شود و خروجی در فولدر backup.logs ذخیره یم کند.و بعد از وارد کردن این دستور با زدن esc و :wq می تونید از صفحه خارج بشید شما با این مراحل می توانید روزانه اطلاعات فولدر مورد نظر در سرور بک اپ به روز رسانی کنید امیدوارم به کارتون بیاد :)</description><category>امیر توحیدی</category><author>امیر توحیدی</author><pubDate>Mon, 15 Nov 2021 18:12:42 +0330</pubDate></item><item><title>به روز رسانی component در reactjs</title><link>https://virgool.ir/@amir.tohidi/به-روز-رسانی-component-در-reactjs-nvny4xdonpuw</link><description>سلام یک مطلب کوتاه در مورد reactjs می خوام بنویسم شاید برای شما هم جذاب باشه.من دنبال راهی بودم تا یک component بدون نیاز به ایجاد ref در داخل کامپوننت و پاس دادن به component والد  بشه راحت دوباره بروزرسانی کرد و componentdidmount فراخوانی کردهمین طور که گفتم راه های مختلفی هست مثل ref پاس دادن از اطن جور کارهایکی از ساده ترین روش ها که می خوام بگم شاید بدونید اما مثل من استفاده نکرده باشید.میتونید به اون کامپوننت یک key اختصاص بدید و مقدار key به صورت Math.random در یک state مقدار بدیدو هر زمان نیاز به بروز رسانی component مورد نظر بود state ابن key به روز رسانی کنید.به همین راحتی :)</description><category>امیر توحیدی</category><author>امیر توحیدی</author><pubDate>Sun, 07 Nov 2021 05:55:55 +0330</pubDate></item><item><title>راه اندازی google tag manager  در Reactjs</title><link>https://virgool.ir/@amir.tohidi/راه-اندازی-google-tag-manager-در-reactjs-rp31fkaxkvx4</link><description>google tagmanagerیکی دیگه از تجربیات جدید دیگم می خوام به اشتراک بزارم که الکی خودم چند ساعت وقتم گرفت.برای یک پروژه برای مدیریت بهتر مارکت و رفتار کاربر درخواست شد تا google tag manager راه اندازی کنیم. برای این در نوشته ها خیلی ساده بود و باید هم این طور میبود اما به خاطر مشکلات فیلترینگ جدید گوگل دسترسی به پنل خود google tag manager یا همون به مخفف gtm مشکلات داشتم .تنها راه اولیه این هست یک مرورگر بی استفاده داشته باشید که کش اطلاعات روش براتون بی اهمیت باشه.هر اطلاعاتی که روی اون مرورگر دارید حذف کنید و دستی cookie حذف کنید هر اطلاعتی که روش هست بعد از حذف اطلاعات به یک VPN که بر بستر کریو کار می کنه متصل بشید . بعد توسط همون مرورگر که اطلاعات حذف کردید متصل بشید و این هم توجه کنید location دسترسیش برای اون مرورگر مسدود کنید تا بدون گرفتن اررور 403 به پنل tagmanager متصل بشید.تا اینجا تونستیم به پنل وصل بشیم پنل توسط مسئول مربوطه باید تنظیم بشه و راه اندازی بشه در مرحله بعدی شما باید کاری که انجام بدید اتصال به google tag manager تنظیمات بر روی پروژه Reactjs انجام بدید برای این مورد به راحتی میتونید از پکیج زیر استفاده کنید. https://github.com/alinemorelli/react-gtm من این پکیج استفاده کردم و خیلی راحت و ساده و این اتصال با استفاده از کد زیر برای شما انجام میده.const tagManagerArgs = {&#xA;    gtmId: &#39;GTM-000000&#39;&#xA;}&#xA;&#xA;TagManager.initialize(tagManagerArgs) این کد بایستی در index.js  قرار بدید در پروژه reactjs در صورتی که دارید از nextjs استفاده می کنید می توانید این فایل در _app.js قرار بدید.البته توضیحاتش بیشتر روی خود پروژه می توانید داشته باشید . بعد از راه اندازی این قسمت و تنظیمات google analytiic  روی پروژه یک سری اطلاعاتی کلی در اختیار شما قرار میده . اما برای ارسال اطلاعات custom شده توسط خود شما می توانید از کد زیر استفاده کنید const tagManagerArgs = {&#xA;  dataLayer: {&#xA;    event: &#39;pageview&#39;,&#xA;    userId: &#39;002&#39;,&#xA;    userProject: &#39;project&#39;,&#xA;    page: &#39;home&#39;,&#xA;  },&#xA;};&#xA;TagManager.dataLayer(tagManagerArgs);&#xA;console.log(tagManagerArgs);توجه کنید استفاده از event ها شمارو می کنه که تا در mode preview بتونید اطلاعات ببینید و دیباگ خوبی انجام بدید. در مرحله اخر مشکلی برای من پیش اومد در مرحله اتصال پنل قسمت preview در tagmanager در صورتی که به مشکل بر خوردید بررسی کنید بر روی مرورگر بایستی Cookie 3d party فعال شده باشد.امیدوارم مفید بوده باشه.</description><category>امیر توحیدی</category><author>امیر توحیدی</author><pubDate>Sat, 18 Sep 2021 11:47:42 +0430</pubDate></item><item><title>تجربه من در استفاده از gatsbyJS</title><link>https://virgool.ir/@amir.tohidi/تجربه-من-در-استفاده-از-gatsbyjs-j2lrl6tdgb5p</link><description>سلام چون خودم موقعی که می خواستم از gatsbyJS اطلاعات در زبان فارسی خوبی پیدا نکردم گفتم تجربیاتم در استفاده از این سیستم بنویسم شاید کس دیگه ای بخواد استفاده کنه مفید باشه براش.در استفاده از gatsbyJS  یک مسئله ای که همیشه مطرح میشه تفاوت بین gatsbyJS و Nextjs هست با بررسی هایی که من کردم چیزی که من متوجه شدم خلاصه اش به این صورت است . شما در استفاده از gatsbyJS یک ساختار SSG  دارید ُssg =&gt; static site generator&#xA;ssr =&gt; server side renderهمین طوری که مخفف بالارو خلاصه توضیح دادم تفاوت این دو تا را نشان می دهد. در واقع شما با gatsbyjs میتونید یک سری صفحات پایه با REACTJS تولید مکنیم به صورت HTML که سرعت بالرگزاری بالایی دارید و داخل اون ساختار می توانید CSR اطلاعات داخل این صفحات را تغییر دهید . چند تا نکته در مورد gatsbyJS شما اطلاعات آن صفحات در موقع بیلد گرفتن از پروژه یک بار دریافت می کنید با ساختاری که در gatsbyjs  وجود دارد در پروژه استفاده کنید و بعد از BUILD گرفتن و بارگزاری بر روی سرور مورد نظر اون اطلاعات قابل به روز رسانی نیست به همین خاطر gatsby برای سایت هایی که این صفحات به صورت مداوم تغییر می کند مناسب نیست . سیستم gatsby پایه بر روی graphql نوشته شده است و برای استفاده از اون بایستی با ساختار graphql  آشنا باشید . سیستم gatsby دارای چند تا فایل اصلی می باشد که در آن ها شما می توانید اطلاعات مورد نیاز و تنظیمات gatsby انجام دهید  و نیاز است داکومنت gatsby به صورت کامل خونده شود.در این سیستم در استفاده از پکیج های جانبی خیلی باید احتیاط کنید و اکثرا در استفاده از پکیج های خارجی دچار اذیت خواهید شد.متاسفانه نسبت به nextjs  community  ضعیف تری دارد . https://medium.com/eincode/next-js-vs-gatsby-js-frameworks-all-you-need-to-know-4147f36ed915مقاله ای که بالا قرار دادم مقاله خوبی هست در مورد تفاوت این دو سیستم برای هم . نتیجه گیری من در استفاده از GATSBY اگر سایت استاتیک دارید که زیاد نیاز به بروز رسانی ندارده از gatsby  استفاده کنید . اگر دنبال دردسر های بعدی و حل مشکلات عجیب غریب بعدی هستید و مشکلی ندارید از gatsby  استفاده کنید. اگر نمی خواید از پکیج های خاصی روی ریکت استفاده کنید از gatsby استفاده کنید . اما به شخصه من خودم بعد از یک تجربه روی این سیستم سرباری که در دیباگ ایجاد می کنه ترجیحم استفاده از nextjs در پروژه است ممکنه سربار اولیه بیشتری داشته باشد اما در ادامه کار و گسترده شدن کار صد در صد با gatsby  اذیت خواهید شد و وقت زیادی ازتون می گیره. </description><category>امیر توحیدی</category><author>امیر توحیدی</author><pubDate>Tue, 10 Aug 2021 10:13:37 +0430</pubDate></item><item><title>اجرای خودکار نرم افزار پایتون بر روی رزبری پای</title><link>https://virgool.ir/@amir.tohidi/اجرای-خودکار-نرم-افزار-پایتون-بر-روی-رزبری-پای-ram9c6jxncpb</link><description>سلام من برای یک کارخانه برای جمع آوری اطلاعات اومدم یک سامانه درست کردم که اطلاعات از طریق رزبری پای از روی PLC در محیط کارخانه جمع آوری می کنه و این اطلاعات به بانک اطلاعات اصلی منقل می کنم من کاری که کردم برای خودکار اجرا شدن نرم افزار کردم در اینجا مرحله به مرحله شرح میدم اولین نکته این که بایستی کلیه package  هایی که در python بهشون احتیاج دارید به صورت global  بر روی رزبری نصب کنید شیوه نصب هم اگر از python3 استفاده می کنید به این صورت است sudo python3 pip install &lt;package-name &gt;اول ما این در نظر میگیریم که فایل پایتون ما در مسیر زیر قرار گرفته است./home/pi/Desktop/file.pyدر واقع فایل ما file.py هست که در مسیر بالا قرار گرفته است . خوب برای این کار بایستی یک فایل bash  ایجاد کنید در مسیر زیر با نام keep_running.sh/home/pi/Desktop/keep_running.shباید داخل فایل keep_running.sh  به صورت زیر ایجاد نمایید.until sudo python3 /home/pi/Desktop/file.py &gt;&gt; /home/pi/Desktop/logpy.txt 2&gt;&amp;1&#xA;do&#xA;    echo &#34;Restarting&#34;&#xA;    sleep 2&#xA;doneدر محتویات فایل keep_running.sh که در بالا قرار گرفته است بایستی در Desktop یک فایل دیگر به نام logpy.txt ایجاد نمایید در واقع این فایل برای دیباگ کردن کد خودتون بر روی رزبری پای به آن نیاز دارید . که اگر به اررور یا مشکلی خورد می توانید نمایش دهید . همین طور این فایل کاری که انجام می دهد فایل مورد نظر ما را اجرا می کند اگر به مشکل بخوره و از اجرا خارج بشه دوباره بعد از دو ثانیه دوباره فایل اجرا می کند . در مرحله بعدی بایست این فایل bash  را با بالا آمدن رزبری پای اجرا نماییم . برای این کار من خیلی جستجو کردم اما راحت ترین راهی که به آن رسیدم استفاده از crontab بودش . برای استفاده بر روی Crontab به این روش استفاده می کنیم ابتدا دستور زیر را تایپ می کنیمcrontab -eداخل این فایل به صورت زیر عمل می کنیم @reboot sudo /home/pi/Desktop/keep_running.sh &gt; /home/pi/Desktop/log.txtبا اضافه کردن دستور بالا با راه اندازی سیستم رزبری پای فایل keep_running.sh اجرا می شود و خروجی آن را در فایل log.txt ثبت می نماید . البته توجه کنید باید فایل log.txt در Desktop رزبری پای ایجاد نمایید . این چکیده 10 ساعت سرچ من در این زمینه بود این راه کار عملی هست و کمترین دستکاری روی سیستم عامل نیاز داره و به راحتی راه اندازی می شود Rassberry pi  و می توانید نرم افزار پایتونی خودتون روش با اجرای سیستم عامل استفاده کنید </description><category>امیر توحیدی</category><author>امیر توحیدی</author><pubDate>Wed, 14 Apr 2021 12:39:28 +0430</pubDate></item><item><title>اتصال Remote  به mariaDb  در رزبری پای</title><link>https://virgool.ir/@amir.tohidi/اتصال-remote-به-mariadb-در-رزبری-پای-plyj4xjnjdva</link><description>سلام این مشکلی بود که خودم چند ساعت باهاش درگیر بودمخواستون باشه وقتی mysql-server  با apt میزنید نصب میشه mariadb به جای mysql  روی سیستم شما نصب می کنه به خاطر همین یک مقدار تنظیماتش فرق داره و برای فایل کانفیگش یه سری کارهای دیگه باید انجام بدید /etc/mysql/mariadb.conf.d/50-server.cnfبه این مسیر بایستی برید که بقیه اش دیگه کاری سختی نیست باید دنبال این خط بگردیدbind-address=127.0.0.1و این خط به این صورت تغییر بدید bind-address=0.0.0.0البته برای امنیت بیشتر برای این که از هر IP نشه دسترسی گرفت می تونید به جای 0.0.0.0 از ای پی سیستمی که می خواید به اون ریموت بزنید استفاده کنید تا مشکلتون رفع بشه همین طور اگر دوست دارید پورت دیفالت 3306 برای ارتباط به mariadb یا همون mysql  تغییر بدید دنبال این خط بگیردید port= 3306و میتونید پورت 3306 به هر مقداری که می خواهید تغییر بدید و استفاده کنید . البته باید توجه داشته باشید یوزری که بر روی mysql  یا  mariadb  برای ریموت ایجاد می کنید بایستی سرور به صورت % یا ایپی سرور خودتون تنظیم بشه تا بتونید از طریق ریموت و آن کاربر به اون متصل بشید.امیدوارم مفید بوده باشه :)</description><category>امیر توحیدی</category><author>امیر توحیدی</author><pubDate>Mon, 12 Apr 2021 15:45:10 +0430</pubDate></item><item><title>مشکل Repair  نشدن جدول Innodb</title><link>https://virgool.ir/@amir.tohidi/مشکل-repair-نشدن-جدول-innodb-vx6z4kaditwt</link><description>سلام وقت بخیر در جریان اتفاقات این روز ها و مشکلاتی که هم شدن ابر آروان به وجود آورده هستید. بعد از بازیابی اطلاعات یکی از مشتریان دیتابیس که بر روی mysql طراحی شده بود دچار مشکل و اشکالات زیاد شد . برای حل این مشکل روی جدوال  myIsam عمل Repair جدول انجام شد و کار کرد . یک سری جداول به صورت Innodb انجین هاش تنظیم شده بود برای بازیابی این موارد به مشکلات متعدد خوردم و هر کاری کردم repair نشد و راه کار هایی که داده بودن به نتیجه نرسیدم بعد از بررسی جدول متوجه شدم یه تعداد رکورد دچار مشکل شدن که تا با Select زدن روی جدول مورد نظر باعث دان شدن سرور mysql میشن . برای این اتفاق چند تا راه حل بود که جدول innodb بیام به myisam تبدیل کنیم و repair کنیم که به مشکلات زیادی برخوردم و این کار شدنی نبود .  https://stackoverflow.com/questions/226172/how-do-i-repair-an-innodb-table اخر سر به این راه حل در سایت stack overflow  رسیدم . این روش به این صورت است که وقتی یه تعداد رکورد به مشکل بر می خورن میام مانند ساختار جدول اصلی یک حدول ایجاد می کنیم و مییام رکورد هارو به اون جدول منتقل می کنم و بعد جدول اصلی پاک می کنیم و اون جدول جایگزین می کنم .این در حالتی هست که در Select کردن اطلاعات دچار مشکل نباشید که من بودم یک راه کاری این جا مطرح شده بود که جالب بود به طور مثال جدول user من دچار مشکل شده است و من یک جدول به نام user_transfer ایجاد کردم برا یانتقال اطلاعات اومدم با این دستور به صورت محدود در جدول جدید اطلاعات منتقل کردم INSERT user_transfer SELECT * FROM user WHERE id NOT IN (SELECT id FROM user_transfer) LIMIT 100;با این دستور 100 تا 100 رکورد هارو به جدول جدید منتقل کردم تا جایی که به کرش خوردم اومدم اون limit به 10 تا و بعد به یکی تغییر دادم تا جایی که رکوردها مشکل دارن پیدا کنم حدود 30 رکورد مشکل داشتن (بهتر از دست دادن حدود 1200 یوزر فعال بود ) اومدن اون 30 تارو دیدم تا رکورد ای دی 810 هست به صورت مثال این اشکال ادامه دارد اومدم دستور به صورت زیر اصلاح کردم INSERT user_transfer SELECT * FROM user WHERE id NOT IN (SELECT id FROM user_transfer) and id &gt; 810 LIMIT 100;و بعد از 810 زدم رکورد ها در بانک اطلاعاتی ذخیره بشه .به این صورت جدول با حدف حداقل چند رکورد با تایم کم تونستم برگردونم این اطلاعات از دست رفته از طریق بک اپ هایی که داشتم جایگزین کردم . امیدوارم مشکلی که برای ابرآروان به وجود امده حل بشه و همه اطلاعاتشون بتونند برگردانند.</description><category>امیر توحیدی</category><author>امیر توحیدی</author><pubDate>Sat, 27 Mar 2021 17:28:00 +0430</pubDate></item><item><title>راه اندازی mongodb بر روی داکر</title><link>https://virgool.ir/@amir.tohidi/راه-اندازی-mongodb-بر-روی-داکر-hz8bootsfmhh</link><description>برای شروع کار ارتباط بایستی خود MongoDb راه اندازی کنیم داخل docker compose باید به صورت زیر اجرا کنیم به عنوان یک سرویس در Docker composeاضافه کردیم mongodb:image : mongo:latesttty: trueenvironment:MONGO_INITDB_ROOT_USERNAME: rootMONGO_INITDB_ROOT_PASSWORD: examplevolumes:- mongoDBVolume:/data/dbports:- &#34;27017-27019:27017-27019&#34;networks:- my-netبعد از انجام راه اندازی برای اتصال لاراوال باید اقدام کنیم البته قبلش برای این که بتوانیم از طریق لاراوال ارتباط برقرار کنیم نیاز هست یک کاربر به آن اضافه کنیم که بتوانیم از طریق آن متصل شویم دستورات زیر را بعد از اتصال توسط دستور زیر به سرویس Mongo راه اندازی شده انجام می دهیم .docker exec -it &lt;CONTAINER_ID&gt; bashبعد از وارد شدن به سرویس MONGO بایستی به صورت اقدام نماییم mongouse admin //نام بانک اطلاعاتی اصلی db.createUser({ user : &#34;admin&#34;, pwd : &#34;admin&#34;, roles : [&#34;root&#34;] })&#xA;در خط سوم یک نام کاربری به نام admin کلمه عبور admin با دسترسی root اضافه می کنیم البته اگر در هنگام اجرای دستور createUser به مشکل بر خوردید بایستی دستور زیر را اجرا نماییدdb.auth({ user : &#34;root&#34;, pwd : &#34;example&#34; })با این کار می توان به بانک اطلاعات متصل شد . و سپس می توان با استفاده از دستور use &lt;DATABASE_NAME&gt;بانک اطلاعاتی مورد نظر را ایجاد می کنیم.در ادامه دیگه میتونید به راحتی به مونگو از طریق اطلاعات تنظیم شده متصل بشید و ارتباط بگیرید یکی از نرم افزارهای کاربردی mongo compass برای ارتباط به بانک اطلاعاتی مونگو استفاده کنید و اطلاعات مدیریت کنید</description><category>امیر توحیدی</category><author>امیر توحیدی</author><pubDate>Mon, 22 Mar 2021 23:22:26 +0430</pubDate></item><item><title>استفاده از kafka بر روی پایتون</title><link>https://virgool.ir/@amir.tohidi/استفاده-از-kafka-بر-روی-پایتون-pflfn4p3yuse</link><description>سلام مجدد این چند وقته خیلی سرگرم پیدا کردن راه های مختلف و پیاده سازی و تست گیری در این موارد بودم متاسفانه در بلاگ های فارسی زبان خیلی کم در مورد استفاده از سیستم apache kafka گفته شده و دوست داشتم مواردی که در استفاده از این سیستم درک کردم به زبان ساده بگم چون این مطالب شاید در موارد عادی استفاده نشه و در کارهای بزرگ با حجم اطلاعات زیاد مورد استفاده قرار بگیره  صحبت کردم الان می خوام در مورد تکمیل اون بحث نحوه استفاده از کافکا توضیح بدم من در این پروژه از فریم ورک جنگو برای کار استفاده کردمدر این کار با استفاده از داکر zookepeer و apache kafka راه اندازی کردیمبر روی پروژه پکیج restframework جنگو راه اندازی کردم یه سری موارد راه اندازی کردیم رسیدیم به ذخیره اطلاعات بر روی سرور در این مرحله چند تا راه کار پیش پا بود برای حجم حدود ۱ میلیون در ساعت چه کار میتونیم کنیماین حجم داده صد در صد نمیشه در مای اسکل مستقیم ذخیره کردبرای همین از سیستم message passing میاد استفاده میشه که اصلی ترین این سرویس ها و پرکاربردترینش میشه apache kafka معرفی کرد چالش بعدی در این زمبنه نحوه استفاده در جنگ هست خیلی جستجو زیادی انجام دادم پکیج خاصی برای جنگو نیست اما سه تا لایبرری یا پکیج برای پایتون وجود داره Kafka python به راحتی در استفاده و سریع نصب معروف استPykafka به همین صورت اما لایبرری معروفی  که طبق مستندات دارای سرعت ۱۰ برابری نسبت به دو پکیج دیگست Confeluent kafka python  استاین کتابخانه با زبان  c نوشته شده و دارای سرعت فوق العاده ای استالبته توجه داشته باشید از این کتابخانه بر روی ویندوز نمی تونید استفاده کنید و باید روی لینوکس راه اندازی کنیدتقریبا یک روز روی این موضوع جستجو کردم و جواب نهایی که گرفتم این بود تا تاریخ انتشار این پستمراحل راه اندازی و استفاده از این لایبرری بر روی مستندات وجود داره و در حد راه اندازی ابتدایی به راحتی با استفاده از داکر میتونید انجام بدیدالبته توجه داشته باشید درخواستهارو میتونید با کافکا دربافت کنید اما بایستی برای هندل کردن و مدیریت درخواست ها در صورتی که نیاز به ارتباط به بانک اطلاعاتر داشته باشد راهکارهایی در نظر بگیرید ممنون </description><category>امیر توحیدی</category><author>امیر توحیدی</author><pubDate>Mon, 22 Mar 2021 23:18:10 +0430</pubDate></item><item><title>راه اندازی deep link در فلاتر</title><link>https://virgool.ir/flutter-community/راه-اندازی-deep-link-در-فلاتر-sya6qrqoy3hh</link><description>سلام امروز مسئله ای که تقریبا یک هفته درگیرش بودم می خوام شرح بدم من در حال نوشتن یک APP اندروید و بعدش انشالله IOS روی بستر فلاتر هستم به نام نظرجوبه صورت شبکه اجتماعی سوال و جواب هست اولش با امکانات خیلی ساده که در ادامه قرار بزرگ بشه انشالله این حجم زیادی از جستجو ها از طریق گوگل قرار وارد سایت نظرجو بشه به خاظر همین نیاز داریم تا در صورتی که از گوگل سرچ شد و اپ مارو نصب داشت بتونه از طریق اون وارد صفحه مورد نظر اپ بشه در زبان ساده به این کار که از طریق لینک سایت به طور خاص در اینجا بتونیم درخواست به اپ مربوطه بفرستیم و در اپ بنا به لینک صفحه مورد نظر نشون بدیم میشه دیپ لینک به طور ملموسش اپ دیجیکالا وقتی اپش دارید بر روی لینک مربوطه میزنید اگر اپ داشته باز میشه میره داخل صفحه مورد نظر. خوب تا اینجا خیلی ساده سعی کردم این توضیح بدمروی فلاتر چطوری این کار باید انجام بدیم  https://github.com/DenisovAV/deep_links_flutter در بالا repository  که قرار دادم میتونید خیلی راحت با کپی کردن کدهای مورد نظر روی پروژه فلاتر این به راحتی اجرا کنید این نمونه کد روی فلاتر 1.22 روی channel stable تست شده است . من این روی sdk 19  کار کردم به راحتی جواب داده است . خوب تا اینجای کار روتین هست و با اصلاح androidmanifest در پوشه app/src/main و اضافه کردن این کد &lt;intent-filter android:label=&#34;Nazarjo&#34;&gt;&#xA;    &lt;action android:name=&#34;android.intent.action.VIEW&#34; /&gt;&#xA;    &lt;category android:name=&#34;android.intent.category.DEFAULT&#34; /&gt;&#xA;    &lt;category android:name=&#34;android.intent.category.BROWSABLE&#34; /&gt;&#xA;    &lt;data&#xA;        android:scheme=&#34;https&#34;&#xA;        android:host=&#34;nazarjo.com&#34;&#xA;        android:pathPrefix=&#34;/&#34;&#xA;        /&gt;&#xA;    &lt;data&#xA;        android:scheme=&#34;https&#34;&#xA;        android:host=&#34;nzrj.ir&#34;&#xA;        android:pathPrefix=&#34;/&#34;&#xA;        /&gt;&#xA;&lt;/intent-filter&gt;در activity  می توانید درخواست های مربوط به یک دامنه خاص دریافت کنید . یک نمونه اش توضیح می دم خدمتتون البته فکر کنم واضح باشه. به طور مثال ما می خوایم وقتی روی این لینک کلیک شد در هر اپ و هر جایی در گوشی اندروید اپ ما باز شود به طور مثال example://nazarjo.app/testمقادیر زیر به این صورت می شود 1 - android:schemeexample2 - android:hostnazarjo.app3 - android:pathPrefixtestو این می توانید یک لینک باشد که example می تواند http یا https باشد . خوب تا اینجا کد فعال کردیم و همین طور intent filter روی androidmanifest تنظیم کردیم. من کتابخانه مورد نظری که قرار دادم توضیح ندادم چون خیلی دقیق قرار داده و مهم ترین چیزی که باید تنظیم بشه mainAcitivty است https://github.com/DenisovAV/deep_links_flutter/tree/master/android/app/src/main/kotlin/dev/flutter/deep_links_flutter با تنظیم این مورد می توانید به درستی با دریافت لینک اپ را فعال نمایید.امیدوارم کاربردی بوده باشه اگر بخواید دقیقتر در مورد MainActivity بدانید. این درواقع API صالی اندروید می باشد درخواست هایی که با سیستم خود اندروید می خواهید مستقیم کار کنید مثلا می خواهید خودتون کتابخانه ای بسازید که درصد باطری دریافت کنید یا فینگر پرینت و .... استفاده کنید بدون پکیج های از قبل آماده شده. این در واقع میاد درخواست اونجا میگیره هندل میکنه و به سمت فلاتر پاس داده میشه من سعی کردم مواردی که خودم درگیرش بودم اطلاع بدم اهان میشه</description><category>امیر توحیدی</category><author>امیر توحیدی</author><pubDate>Tue, 12 Jan 2021 12:30:40 +0330</pubDate></item></channel></rss>