.. _webapp-config-inspector: Config Inspector (סקירת משתני סביבה) ===================================== מה זה Config Inspector? ------------------------ **Config Inspector** הוא כלי אדמין שמציג תמונת מצב של הקונפיגורציה: אילו משתני סביבה מוגדרים, מה הערך הפעיל שלהם, מה ברירת המחדל בקוד, והאם הערך שונה מברירת המחדל. הכלי נותן מענה לשאלות שקשה לענות עליהן מול Render Dashboard: - האם המשתנה הזה בכלל מוגדר, או שאנחנו רצים על ברירת מחדל? - מה ברירת המחדל שבקוד, ובמה הערך שברנדר שונה ממנה? - לאיזה שירות שייך המשתנה, ואיפה צריך להגדיר אותו? - אילו משתנים הכרחיים חסרים? איך נכנסים? ----------- **דרך ה-UI:** דף **Settings** ← קטגוריית **כלי אדמין** ← **Config Inspector** **ישירות:** .. code-block:: text GET /admin/config-inspector .. note:: הדף זמין **רק לאדמינים** (``@admin_required`` ב-``webapp/app.py``). ערך מוצג ממוסך (``********``) **רק אם הוא מסווג כרגיש** — לפי שם המשתנה או לפי סימון מפורש ``sensitive=True``; כל שאר הערכים מוצגים כמות שהם. הסיווג מפורט ב- :ref:`config-inspector-sensitive`, וחשוב לקרוא אותו לפני הוספת משתנה שהוא סוד. .. _config-inspector-two-pages: שני עמודים — ולמה ------------------ הדף מחולק לשני טאבים, וההפרדה ביניהם אינה קוסמטית אלא נובעת ממגבלה אמיתית. .. _config-inspector-page1: עמוד 1: שירות ה-Webapp (עם Status וערך פעיל) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ העמוד הראשון מציג **רק משתנים ששייכים (גם) לשירות ה-Webapp**, ורק הם מקבלים עמודות ``Status`` ו-``Active Value``. **הסיבה:** ה-Config Inspector הוא קוד שרץ **בתוך תהליך ה-Webapp**. כשהוא קורא ``os.environ`` הוא רואה אך ורק את משתני הסביבה של אותו תהליך. הבוט, שרת ה-MCP וה-webserver הם **שירותי Render נפרדים**, כל אחד עם ENV משלו — ותהליך ה-Webapp פשוט אינו יכול לראות אותם. לכן, אילו היינו מציגים ``Status`` למשתנה של הבוט, הוא היה מוצג כ-*Default* או *Missing* גם כשהוא מוגדר מצוין בשירות הבוט. **מידע שגוי גרוע מהיעדר מידע** — ולכן העמוד הראשון מסונן, והסינון נאכף בקוד: .. code-block:: python # services/config_inspector_service.py — get_config_overview for definition in self.CONFIG_DEFINITIONS.values(): # עמוד ראשי: רק משתנים ששייכים (גם) לשירות ה-webapp if "webapp" not in definition.services: continue בראש העמוד מוצגים כרטיסי סיכום (סה"כ / שונו מדיפולט / הוגדרו בסביבה / חסרים / ברירת מחדל), שורת סינון (קטגוריה + סטטוס), פירוט לפי קטגוריות, וטבלת המשתנים המלאה. כפתור **"העתק הכל"** מייצא את השורות המוצגות בפורמט ``KEY=VALUE`` (שורות ``.env``). עמוד 2: שירותים אחרים — Bot / MCP / Webserver ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ העמוד השני מציג את כל המשתנים שיש להם שירות **שאינו** webapp — כולל משתנים משותפים שמופיעים גם בעמוד הראשון. מה מוצג: ``Key`` / ``שירות`` / ``Default Value`` / ``תיאור``. מה **לא** מוצג, ולמה: **אין כאן ``Status`` ואין ``Active Value``** — מאותה סיבה בדיוק שתוארה למעלה. הערכים חיים בתהליכים אחרים ואינם נגישים מכאן. לערכים בפועל יש לבדוק ב-Render Dashboard של השירות הרלוונטי. - שורה של משתנה שמוגדר גם בוובאפ מסומנת בתגית **"גם Webapp"** — הערך שלה מופיע בעמוד הראשון. - שורת הסינון בעמוד זה היא לפי **קטגוריה** ולפי **שירות** (ולא לפי סטטוס — אין כאן סטטוס). - "העתק הכל" בעמוד זה מעתיק ``KEY=<ברירת מחדל>`` ומציין זאת מפורשות, כדי שלא ייווצר רושם שאלו הערכים החיים. .. _config-inspector-status: ארבעת הסטטוסים — ואיך נמנעים מ-"Modified" שגוי ------------------------------------------------ .. list-table:: :header-rows: 1 :widths: 15 45 40 * - סטטוס - מתי מתקבל - משמעות * - ``Default`` - אין ערך בסביבה ויש ברירת מחדל בקוד — או שהערך בסביבה **זהה** לברירת המחדל - רצים על ברירת המחדל. אין מה לעשות * - ``Set`` - יש ערך בסביבה ו**אין ברירת מחדל בקוד** - המשתנה הוגדר (למשל ברנדר). זו **אינה** סטייה — אין דיפולט שממנו אפשר לסטות * - ``Modified`` - יש ערך בסביבה, יש ברירת מחדל, והם **שונים** - סטייה אמיתית מברירת המחדל — כאן צריך להסתכל * - ``Missing`` - אין ערך בסביבה, אין ברירת מחדל, והמשתנה מסומן ``required=True`` - משתנה הכרחי חסר. מוצג גם כאזהרה בראש הדף הלוגיקה ממומשת ב-``ConfigService.determine_status``. .. warning:: **הכלל השורשי:** ה-``default`` שרשום ב-``ConfigDefinition`` חייב להיות **זהה תו-בתו** לברירת המחדל האמיתית בקוד. אחרת מתקבל סטטוס ``Modified`` שקרי — משתנה שמוגדר נכון ייראה כאילו מישהו שינה אותו, ואי אפשר יהיה לסמוך על העמודה הזו. שתי הטעויות שמייצרות ``Modified`` שגוי: **1. דיפולט משוער במקום זה שבקוד.** אם הקוד עושה ``os.getenv("X", "60")`` אבל בהגדרה נרשם ``default="30"`` — משתנה שמוגדר ל-60 ברנדר יוצג כ-``Modified``, למרות שהוא זהה לברירת המחדל בפועל. **2. המצאת דיפולט למשתנה שאין לו דיפולט בקוד.** אם בקוד יש ``os.getenv("TOKEN")`` בלי ערך שני, אין ברירת מחדל — וההגדרה צריכה להשאיר ``default`` ריק. אז הסטטוס יהיה ``Set`` (נכון), ולא ``Modified`` (מטעה). **הבדיקה לפני הוספה** — לאתר את ברירת המחדל האמיתית ולהעתיק אותה כמות שהיא: .. code-block:: bash rg -n -w 'MY_VAR' -t py -g '!tests/**' ``-w`` מחפש את שם המשתנה כמילה שלמה, ולכן תופס גם ``os.getenv("MY_VAR")``, גם ``os.getenv('MY_VAR')`` וגם ``config.MY_VAR``, בלי להיתפס ל-``MY_VAR_OTHER``. .. _config-inspector-services: לאיזה שירות שייך המשתנה? — לבדוק, לא לנחש -------------------------------------------- השדה ``services`` בהגדרה קובע באיזה עמוד המשתנה יופיע. שיוך שגוי מייצר רעש: משתנה של הוובאפ בלבד שמסומן כשייך לכולם מופיע בעמוד 2 כאילו צריך להגדיר אותו בארבעה מקומות. .. important:: **אל תסיקו את השיוך משם המשתנה.** שם שנשמע גלובלי (``PUSH_*``, ``UPTIME_*``, ``MAINTENANCE_*``) לא אומר שהמשתנה נצרך בכל השירותים. בסבב ניקוי אחד הוסרו 68 שיוכים שגויים שנקבעו לפי תחושה. **הנוהל (שלושה צעדים):** 1. **למצוא איפה המשתנה נצרך בפועל** — חיפוש אחד תופס גם ``os.getenv`` (בכל סוג גרשיים) וגם גישה דרך אובייקט הקונפיג (``config.MY_VAR``): .. code-block:: bash rg -n -w 'MY_VAR' -t py -g '!tests/**' 2. **לזהות לאיזה שירות שייך הקובץ שנמצא** — לפי נקודות הכניסה: .. list-table:: :header-rows: 1 :widths: 40 25 35 * - קובץ / תיקייה - שירות - נקודת כניסה * - ``main.py``, ``handlers/``, ``*_handler.py`` - ``bot`` - ``main.py`` * - ``webapp/`` - ``webapp`` - ``webapp/app.py`` * - ``mcp_server/`` - ``mcp`` - ``mcp_server/app.py`` * - ``services/webserver.py`` - ``webserver`` - ``services/webserver.py`` * - ``scripts/`` - ``scripts`` - סקריפטים ידניים/CI 3. **קובץ משותף — לפי שרשרת ה-imports.** קובץ כמו ``database/``, ``services/`` או ``utils.py`` אינו שייך לשירות מסוים בפני עצמו: הוא שייך לשירות רק אם הוא **נטען** בשרשרת ה-imports של אותה נקודת כניסה. משתנה שנצרך רק ב-``webapp/app.py`` **אינו** שייך ל-bot/mcp/webserver, נקודה. .. tip:: ``PORT`` הוא חריג מוצדק: הוא לא בהכרח מופיע בקוד של השירות, אבל כל שירות web צריך אותו בפקודת ההרצה. חריגים כאלה — לתעד בהערה ליד ההגדרה. .. _config-inspector-multi-service: משתנה שמשרת כמה שירותים — לתעד בכל המקומות -------------------------------------------- משתנה יכול להיות מוגדר בכמה שירותי Render במקביל (למשל ``MONGODB_URL``). במקרה כזה מתעדים אותו **בכל המקומות הרלוונטיים, כל אחד במקום שלו** — ולא בוחרים "בית" אחד: 1. **``services`` בהגדרה** — כל השירותים, לא רק העיקרי: .. code-block:: python "MONGODB_URL": ConfigDefinition( key="MONGODB_URL", services=("webapp", "bot", "mcp", "webserver"), ... ) 2. **``docs/environment-variables.rst``** — עמודת **"רכיב"** בטבלה המרכזית חייבת לשקף את **אותה** רשימת שירותים. השורה הזו היא הרפרנס לאנשי DevOps. 3. **תיאור תואם בשני המקומות** — ה-``description`` שב-``ConfigDefinition`` (מה שמוצג בטבלת ה-Config Inspector) וההסבר שב-``environment-variables.rst`` צריכים לומר את אותו דבר. שני התיאורים נקראים זה לצד זה כשמדבגים תקלת קונפיגורציה, ותיאורים סותרים גרועים מהיעדר תיאור. .. _config-inspector-sensitive: מיסוך ערכים רגישים ------------------- ערך ממוסך (``********``) בשני מקרים: - **לפי שם המשתנה** — ``SENSITIVE_PATTERNS`` (``TOKEN``, ``KEY``, ``PASSWORD``, ``SECRET``, ``URI``, ``CREDENTIALS``, ``AUTH``, ``PRIVATE``, ``CERT``, ``DSN``, ``CONNECTION_STRING``). - **לפי סימון מפורש** — ``sensitive=True`` בהגדרה. .. note:: ``URL`` הוסר מהרשימה **בכוונה**: כתובת ציבורית (``WEBAPP_URL``, ``MCP_SERVER_URL``) אינה סוד, ומיסוכה הפך את הדף לחסר תועלת. כתובת שמכילה credentials — למשל ``MONGODB_URL`` בפורמט ``scheme://user:pass@host`` — מסומנת ``sensitive=True`` מפורשות, ובנוסף קיים זיהוי אוטומטי של תבנית ה-credentials בתוך URL. המסקנה המעשית: **משתנה חדש שהוא סוד ושמו אינו מכיל אחת מהמילים ברשימה — סמנו ``sensitive=True`` ידנית.** צ'קליסט: הוספת משתנה סביבה חדש -------------------------------- 1. **לאתר את הצריכה בפועל** — ``grep`` על שם המשתנה (:ref:`config-inspector-services`). 2. **להעתיק את ברירת המחדל מהקוד תו-בתו** — ואם אין דיפולט, להשאיר ריק כדי לקבל ``Set`` ולא ``Modified`` (:ref:`config-inspector-status`). 3. **לקבוע ``services``** לפי הצריכה שנמצאה — כל השירותים שצורכים, ורק הם. 4. **``sensitive=True``** אם זה סוד ששמו לא נתפס אוטומטית (:ref:`config-inspector-sensitive`). 5. **לעדכן את ``docs/environment-variables.rst``** — שורה בטבלה המרכזית עם עמודת "רכיב" תואמת ותיאור זהה. זו **חובה** לפי ההנחיה שבראש אותו עמוד. 6. **לוודא בדף** שהמשתנה מופיע בעמוד הנכון ושהסטטוס הגיוני (משתנה שלא נגעתם בו ברנדר אמור להיות ``Default``, לא ``Modified``). טסטים: ``tests/test_config_inspector_service.py``. ראו גם ------- - :doc:`../environment-variables` — הרפרנס המלא של משתני הסביבה - :doc:`cache-inspector` — כלי אדמין מקביל ל-Redis