{html_escape(original_name)}\n"
+ "🔎 ניתן למצוא אותו תחת: '📚' ← '📝 סקילים'.",
+ parse_mode=ParseMode.HTML,
+ )
+ else:
+ await TelegramUtils.safe_edit_message_text(
+ query, "❌ שמירת הסקיל נכשלה. נסה/י שוב מאוחר יותר."
+ )
+ return
+
+ # zip_route_backup — לוגיקת הגיבוי המקורית (save_backup_bytes מזריק metadata.json בעצמו)
+ backup_id = f"upload_{user_id}_{int(time.time())}"
+ md = {
+ "backup_id": backup_id,
+ "backup_type": "generic_zip",
+ "user_id": user_id,
+ "created_at": datetime.now(timezone.utc).isoformat(),
+ "original_filename": original_name,
+ "source": "uploaded_document",
+ }
+ result_id = await asyncio.to_thread(backup_manager.save_backup_bytes, raw, md)
+ if result_id:
+ # ניקוי רק לאחר שמירה מוצלחת — בכשל שומרים את ה-token/bytes כדי לאפשר retry
+ cleanup_pending_zip(entry.get("path", ""))
+ pending.pop(token, None)
+ await TelegramUtils.safe_edit_message_text(
+ query,
+ "✅ קובץ ZIP נשמר בהצלחה לרשימת ה‑ZIP השמורים.\n"
+ "📦 ניתן למצוא אותו תחת: '📚' ← '📦 קבצי ZIP' או ב‑Batch/GitHub."
+ )
+ else:
+ await TelegramUtils.safe_edit_message_text(
+ query, "❌ שמירת הגיבוי נכשלה. נסה/י שוב מאוחר יותר."
+ )
+
+
async def finalize_zip_create(update: Update, context: ContextTypes.DEFAULT_TYPE, zip_name: Optional[str] = None) -> None:
"""בונה ZIP מהקבצים שנאספו ושולח למשתמש.
@@ -987,6 +1070,7 @@ async def show_all_files(update: Update, context: ContextTypes.DEFAULT_TYPE) ->
[InlineKeyboardButton("🔎 חפש קובץ", callback_data="search_files")],
[InlineKeyboardButton("🗂 לפי ריפו", callback_data="by_repo_menu")],
[InlineKeyboardButton("📦 קבצי ZIP", callback_data="backup_list")],
+ [InlineKeyboardButton("📝 סקילים", callback_data="skill_list")],
[InlineKeyboardButton("📂 קבצים גדולים", callback_data="show_large_files")],
[InlineKeyboardButton("📁 שאר הקבצים", callback_data="show_regular_files")],
[InlineKeyboardButton("⭐ מועדפים", callback_data="show_favorites")],
@@ -1057,6 +1141,7 @@ async def show_all_files_callback(update: Update, context: ContextTypes.DEFAULT_
keyboard = [
[InlineKeyboardButton("🗂 לפי ריפו", callback_data="by_repo_menu")],
[InlineKeyboardButton("📦 קבצי ZIP", callback_data="backup_list")],
+ [InlineKeyboardButton("📝 סקילים", callback_data="skill_list")],
[InlineKeyboardButton("📂 קבצים גדולים", callback_data="show_large_files")],
[InlineKeyboardButton("📁 שאר הקבצים", callback_data="show_regular_files")],
[InlineKeyboardButton("⭐ מועדפים", callback_data="show_favorites")],
@@ -3822,6 +3907,10 @@ async def handle_callback_query(update: Update, context: ContextTypes.DEFAULT_TY
await query.edit_message_text("⏳ יוצר ZIP…")
await finalize_zip_create(update, context, zip_name=None)
return ConversationHandler.END
+ elif data.startswith("zip_route_skill:") or data.startswith("zip_route_backup:"):
+ # בחירת יעד ל-ZIP שהועלה: סקיל (אחסון קבוע) או גיבוי (רשימת הגיבויים)
+ await _handle_zip_route(update, context, data)
+ return ConversationHandler.END
elif data.startswith("replace_") or data == "rename_file" or data == "cancel_save":
return await handle_duplicate_callback(update, context)
diff --git a/docs/environment-variables.rst b/docs/environment-variables.rst
index 91d1deab4..a91ebed05 100644
--- a/docs/environment-variables.rst
+++ b/docs/environment-variables.rst
@@ -17,11 +17,11 @@
- דוגמה
- רכיב
* - ``BOT_TOKEN``
- - טוקן הבוט מ-BotFather
+ - טוקן הבוט מ-BotFather (נדרש גם ב-WebApp לאימות Telegram Login)
- כן
- -
- ``123456:ABC-DEF...``
- - Bot
+ - Bot/WebApp
* - ``MONGODB_URL``
- חיבור ל-MongoDB
- כן
@@ -899,11 +899,11 @@
- ``srv-xxxx``
- Reporter
* - ``ENABLE_INTERNAL_SHARE_WEB``
- - הפעלת שירות שיתוף פנימי
+ - הפעלת שירות שיתוף פנימי בתוך תהליך הבוט (נקרא ב-main.py; היסטורי — ההרצה הפנימית בוטלה וה-webserver רץ כשירות נפרד)
- לא
- ``false``
- ``true``
- - WebApp
+ - Bot
* - ``PORT``
- פורט להרצת שירותים פנימיים/בדיקות
- לא
@@ -1145,13 +1145,13 @@
- לא
- "" (ריק)
- ``secret123``
- - WebApp
+ - Webserver
* - ``SENTRY_WEBHOOK_DEDUP_WINDOW_SECONDS``
- חלון דה-דופליקציה (בשניות) להתראות Sentry שמגיעות ב-Webhook כדי למנוע burst (``0`` מנטרל).
- לא
- ``300``
- ``600``
- - WebApp
+ - Webserver
* - ``ALERTMANAGER_IP_ALLOWLIST``
- רשימת IPs (מופרדים בפסיק) שמורשים לצרוך את ה-webhook; נבדק מול ``X-Forwarded-For``/``remote_addr``.
- לא
@@ -1819,7 +1819,7 @@
- לא
- ``dummy_token``
- ``test_token``
- - Bot/WebApp
+ - Bot
* - ``DRIVE_RESCHEDULE_BOOTSTRAP_DELAY``
- כמה שניות להמתין לפני תחילת משימות ה-Drive rescheduler.
- לא
diff --git a/file_manager.py b/file_manager.py
index 7bf55d8de..d770c2a83 100644
--- a/file_manager.py
+++ b/file_manager.py
@@ -11,6 +11,7 @@
import re
import shutil
import time
+import uuid
try:
import gridfs # from pymongo
@@ -1272,4 +1273,184 @@ def delete_backup(self, backup_id: str, user_id: int) -> bool:
logger.error(f"שגיאה במחיקת גיבוי: {e}")
return False
+
+class SkillInfo:
+ """מידע על סקיל שמור (ארכיון קוד לטווח ארוך, נפרד לגמרי מגיבויים)"""
+ def __init__(self, skill_id: str, user_id: int, created_at: datetime, file_count: int,
+ total_size: int, original_name: str, file_name: str,
+ metadata: Optional[Dict[str, Any]]):
+ self.skill_id = skill_id
+ self.user_id = user_id
+ self.created_at = created_at
+ self.file_count = file_count
+ self.total_size = total_size
+ self.original_name = original_name # השם המקורי המלא (לתצוגה)
+ self.file_name = file_name # שם ה-GridFS הייחודי בפועל
+ self.metadata = metadata
+
+
+class SkillManager:
+ """מנהל אחסון סקילים — קולקציית GridFS נפרדת ("skills"), ללא retention/cleanup/restore.
+
+ בניגוד ל-BackupManager:
+ - שומר את ה-bytes as-is (fs.put ישיר) בלי לפתוח/לדחוס מחדש את ה-ZIP ובלי הזרקת metadata.json.
+ - תמיד מונגו (GridFS), ללא תלות ב-BACKUPS_STORAGE וללא משתנה סביבה מקביל.
+ - אין מחיקת retention ואין restore — סקיל לא נמחק לבד לעולם.
+ """
+
+ def _get_skills_gridfs(self):
+ """מחזיר GridFS על קולקציית "skills" (מבודדת מ-"backups"), או None אם אין חיבור מונגו."""
+ if gridfs is None:
+ return None
+ try:
+ mongo_db = None
+ if get_files_facade is not None:
+ mongo_db = get_files_facade().get_mongo_db()
+ if not mongo_db:
+ return None
+ # אוסף ייעודי "skills" — מבודד לחלוטין; cleanup_expired_backups לעולם לא נוגע בו
+ return gridfs.GridFS(mongo_db, collection="skills")
+ except Exception:
+ return None
+
+ @staticmethod
+ def _unique_filename(original_name: str) -> str:
+ """שם קובץ ייחודי לאחסון: השם המקורי אחרי סניטציה + סיומת ייחוד קצרה.
+
+ סיומת הייחוד מבטיחה ששני סקילים עם אותו שם לא ידרסו זה את זה.
+ """
+ try:
+ from utils import TextUtils
+ cleaned = TextUtils.clean_filename(original_name or "")
+ except Exception:
+ cleaned = re.sub(r'[^\w.\-]+', '_', original_name or "").strip('._')
+ stem, ext = os.path.splitext(cleaned or "")
+ if not stem:
+ stem = "skill"
+ if not ext:
+ ext = ".zip"
+ short = uuid.uuid4().hex[:6]
+ return f"{stem}_{short}{ext}"
+
+ def save_skill_bytes(self, data: bytes, metadata: Dict[str, Any]) -> Optional[str]:
+ """שומר סקיל (ZIP) as-is ב-GridFS "skills" ומחזיר skill_id, או None בכשל.
+
+ לא פותח/דוחס מחדש את ה-ZIP ולא מזריק metadata.json — ה-bytes נשמרים בדיוק כפי שהתקבלו,
+ כך שהורדה מחזירה את הקובץ byte-for-byte.
+ """
+ try:
+ fs = self._get_skills_gridfs()
+ if fs is None:
+ logger.warning("save_skill_bytes: GridFS 'skills' לא זמין")
+ return None
+ # נרמול user_id ל-int כדי ש-list_skills (שאילתת metadata.user_id כ-int) תמצא את הסקיל
+ raw_uid = metadata.get("user_id")
+ try:
+ user_id = int(raw_uid)
+ except (TypeError, ValueError):
+ logger.warning("save_skill_bytes: user_id לא תקין (%r)", raw_uid)
+ return None
+ original_name = metadata.get("original_name") or "skill.zip"
+ # מזהה לוגי ייחודי מובטח: timestamp לקריאות + uuid קצר למניעת התנגשות (כולל אותה מילישנייה)
+ skill_id = metadata.get("skill_id") or f"skill_{user_id}_{int(time.time())}_{uuid.uuid4().hex[:6]}"
+ filename = self._unique_filename(original_name)
+ # מטאדטה סופית — נשמרת בשכבת GridFS בלבד (לא בתוך הארכיון)
+ final_md = dict(metadata or {})
+ final_md["user_id"] = user_id # int מנורמל (לא ה-str המקורי אם הגיע כך)
+ final_md["skill_id"] = skill_id
+ final_md["kind"] = "skill"
+ if not final_md.get("created_at"):
+ final_md["created_at"] = datetime.now(timezone.utc).isoformat()
+ # שמירה byte-for-byte — בלי מחיקת filename קיים (הייחודיות מובטחת ע"י סיומת הייחוד)
+ fs.put(data, filename=filename, metadata=final_md)
+ return skill_id
+ except Exception as e:
+ logger.warning(f"save_skill_bytes failed: {e}")
+ return None
+
+ def list_skills(self, user_id: int) -> List[SkillInfo]:
+ """מחזיר את כל הסקילים של המשתמש (מטא-דאטה בלבד — Smart Projection, בלי משיכת bytes)."""
+ results: List[SkillInfo] = []
+ try:
+ fs = self._get_skills_gridfs()
+ if fs is None:
+ return results
+ # שאילתה ממוקדת לפי בעלים (user_id נשמר תמיד כ-int בשמירה)
+ for fdoc in fs.find({"metadata.user_id": user_id}):
+ try:
+ md = getattr(fdoc, 'metadata', None) or {}
+ skill_id = md.get("skill_id") or str(getattr(fdoc, "_id", ""))
+ if not skill_id:
+ continue
+ created_at = None
+ created_str = md.get("created_at")
+ if created_str:
+ with suppress(Exception):
+ created_at = datetime.fromisoformat(created_str)
+ if not created_at:
+ created_at = getattr(fdoc, 'uploadDate', None)
+ if not created_at:
+ created_at = datetime.now(timezone.utc)
+ if created_at.tzinfo is None:
+ created_at = created_at.replace(tzinfo=timezone.utc)
+ results.append(SkillInfo(
+ skill_id=skill_id,
+ user_id=user_id,
+ created_at=created_at,
+ file_count=int(md.get("file_count") or 0),
+ total_size=int(getattr(fdoc, 'length', 0) or 0),
+ original_name=md.get("original_name") or (getattr(fdoc, 'filename', None) or skill_id),
+ file_name=getattr(fdoc, 'filename', None) or "",
+ metadata=md,
+ ))
+ except Exception:
+ continue
+ results.sort(key=lambda s: s.created_at, reverse=True)
+ except Exception as e:
+ logger.warning(f"list_skills failed: {e}")
+ return results
+
+ def get_skill_bytes(self, user_id: int, skill_id: str) -> Optional[bytes]:
+ """מחזיר את ה-bytes המדויקים של הסקיל (אחרי אימות בעלות), או None אם לא נמצא/לא שייך."""
+ try:
+ fs = self._get_skills_gridfs()
+ if fs is None:
+ return None
+ for fdoc in fs.find({"metadata.skill_id": skill_id}):
+ md = getattr(fdoc, 'metadata', None) or {}
+ owner = md.get("user_id")
+ if isinstance(owner, str) and owner.isdigit():
+ owner = int(owner)
+ if owner != user_id:
+ continue
+ return fs.get(fdoc._id).read()
+ return None
+ except Exception as e:
+ logger.warning(f"get_skill_bytes failed: {e}")
+ return None
+
+ def delete_skills(self, user_id: int, skill_ids: List[str]) -> Dict[str, Any]:
+ """מוחק סקילים לפי skill_id (רק של המשתמש הנוכחי). מחזיר {deleted, errors}."""
+ result: Dict[str, Any] = {"deleted": 0, "errors": []}
+ try:
+ fs = self._get_skills_gridfs()
+ if fs is None:
+ result["errors"].append("GridFS 'skills' unavailable")
+ return result
+ wanted = set(skill_ids or [])
+ for fdoc in list(fs.find({"metadata.user_id": user_id})):
+ try:
+ md = getattr(fdoc, 'metadata', None) or {}
+ if md.get("skill_id") not in wanted:
+ continue
+ fs.delete(fdoc._id)
+ result["deleted"] += 1
+ except Exception as e:
+ result["errors"].append(str(e))
+ except Exception as e:
+ result["errors"].append(str(e))
+ return result
+
+
backup_manager = BackupManager()
+skill_manager = SkillManager()
diff --git a/handlers/documents.py b/handlers/documents.py
index 1a7cbe80a..68734eaf9 100644
--- a/handlers/documents.py
+++ b/handlers/documents.py
@@ -997,54 +997,71 @@ async def _maybe_store_zip_copy(
if not (is_zip_hint and is_zip_actual):
return False
- backup_id = f"upload_{update.effective_user.id}_{int(datetime.now(timezone.utc).timestamp())}"
- target_path = backup_manager.backup_dir / f"{backup_id}.zip"
+ # ZIP זוהה — במקום שמירה אוטומטית לגיבוי, בקש מהמשתמש לבחור יעד: סקיל או גיבוי.
+ # ה-bytes נשמרים כמו שהם בקובץ זמני; הבחירה בפועל מתבצעת ב-callback (בחירה מפורשת בלבד).
+ from utils import (
+ stash_pending_zip_bytes,
+ cleanup_pending_zip,
+ cleanup_stale_pending_zips,
+ PENDING_ZIP_TTL_SECONDS,
+ )
+ import uuid as _uuid
+
+ original_name = document.file_name or "upload.zip"
+
+ # ניקוי עצל: קבצים ממתינים ישנים (שלא נבחרו) — גם על הדיסק וגם ב-user_data
try:
- try:
- with zipfile.ZipFile(BytesIO(raw_bytes), "r") as ztest:
- try:
- ztest.getinfo("metadata.json")
- md_bytes = raw_bytes
- except KeyError:
- md = {
- "backup_id": backup_id,
- "backup_type": "generic_zip",
- "user_id": update.effective_user.id,
- "created_at": datetime.now(timezone.utc).isoformat(),
- "original_filename": document.file_name,
- "source": "uploaded_document",
- }
- out_buf = BytesIO()
- with zipfile.ZipFile(out_buf, "w", compression=zipfile.ZIP_DEFLATED) as zout:
- for name in ztest.namelist():
- zout.writestr(name, ztest.read(name))
- zout.writestr("metadata.json", json.dumps(md, indent=2))
- md_bytes = out_buf.getvalue()
- except Exception:
- md_bytes = raw_bytes
+ cleanup_stale_pending_zips()
+ except Exception:
+ pass
+ pending = context.user_data.setdefault("pending_zip", {})
+ try:
+ now_ts = int(datetime.now(timezone.utc).timestamp())
+ stale_tokens = [
+ t for t, m in pending.items()
+ if now_ts - int((m or {}).get("ts", 0)) > PENDING_ZIP_TTL_SECONDS
+ ]
+ for _old in stale_tokens:
+ cleanup_pending_zip((pending.get(_old) or {}).get("path", ""))
+ pending.pop(_old, None)
+ # הגבלת מספר ה-ZIP הממתינים למשתמש — הסרת הישנים ביותר מעבר לקיבולת
+ MAX_PENDING = 5
+ if len(pending) >= MAX_PENDING:
+ oldest = sorted(pending.items(), key=lambda kv: int((kv[1] or {}).get("ts", 0)))
+ for _old, _meta in oldest[: len(pending) - MAX_PENDING + 1]:
+ cleanup_pending_zip((_meta or {}).get("path", ""))
+ pending.pop(_old, None)
+ except Exception:
+ pass
- try:
- backup_manager.save_backup_bytes(
- md_bytes,
- {
- "backup_id": backup_id,
- "backup_type": "generic_zip",
- "user_id": update.effective_user.id,
- "created_at": datetime.now(timezone.utc).isoformat(),
- "original_filename": document.file_name,
- "source": "uploaded_document",
- },
- )
- except Exception:
- with open(target_path, "wb") as fzip:
- fzip.write(md_bytes)
- await update.message.reply_text(
- "✅ קובץ ZIP נשמר בהצלחה לרשימת ה‑ZIP השמורים.\n"
- "📦 ניתן למצוא אותו תחת: '📚' > '📦 קבצי ZIP' או ב‑Batch/GitHub."
- )
- return True
+ token = _uuid.uuid4().hex
+ try:
+ path = stash_pending_zip_bytes(raw_bytes, token)
except Exception as err:
- logger.warning("Failed to persist uploaded ZIP: %s", err)
+ logger.warning("Failed to stash pending ZIP: %s", err)
+ return False
+ pending[token] = {
+ "path": path,
+ "original_name": original_name,
+ "size": len(raw_bytes),
+ "ts": int(datetime.now(timezone.utc).timestamp()),
+ }
+
+ keyboard = InlineKeyboardMarkup([
+ [
+ InlineKeyboardButton("📝 סקיל", callback_data=f"zip_route_skill:{token}"),
+ InlineKeyboardButton("📦 גיבוי", callback_data=f"zip_route_backup:{token}"),
+ ]
+ ])
+ await update.message.reply_text(
+ f"📦 קיבלתי קובץ ZIP: {html_escape(original_name)}\n"
+ "איך לשמור אותו?\n\n"
+ "📝 סקיל — אחסון קבוע, בדיוק כמו שהוא (byte-for-byte), בלי מחיקה אוטומטית.\n"
+ "📦 גיבוי — נשמר לרשימת הגיבויים (כפוף למדיניות ניקוי).",
+ reply_markup=keyboard,
+ parse_mode=ParseMode.HTML,
+ )
+ return True
except Exception:
pass
return False
diff --git a/main.py b/main.py
index 7603f5bf5..6f77f1d95 100644
--- a/main.py
+++ b/main.py
@@ -148,6 +148,7 @@ def _observability_attr(name: str, default):
# ייבוא דחוי של ה-activity_reporter בתוך ה-run-time בלבד כדי למנוע יצירת חיבורים בזמן import
from github_menu_handler import GitHubMenuHandler
from backup_menu_handler import BackupMenuHandler
+from skill_menu_handler import SkillMenuHandler
from handlers.drive.menu import GoogleDriveMenuHandler
from handlers.drive.utils import extract_schedule_key as drive_extract_schedule_key
def get_drive_handler_from_application(application: Application) -> tuple[Any, bool]:
@@ -3487,6 +3488,11 @@ async def show_backup_menu(update: Update, context: ContextTypes.DEFAULT_TYPE):
await backup_handler.show_backup_menu(update, context)
self.application.add_handler(CommandHandler("backup", show_backup_menu))
self.application.add_handler(CallbackQueryHandler(backup_handler.handle_callback_query, pattern=r'^(backup_|backup_add_note:.*)'))
+
+ # יצירת SkillMenuHandler ורישום ה-callbacks שלו (סוג קובץ "סקיל" — אחסון נפרד מגיבויים)
+ skill_handler = SkillMenuHandler()
+ self.application.bot_data['skill_handler'] = skill_handler
+ self.application.add_handler(CallbackQueryHandler(skill_handler.handle_callback_query, pattern=r'^skill_'))
# הוסף את ה-callbacks של GitHub - חשוב! לפני ה-handler הגלובלי
self.application.add_handler(
@@ -3585,8 +3591,28 @@ async def handle_github_text(update: Update, context: ContextTypes.DEFAULT_TYPE)
context.user_data['suppress_code_hint_once'] = True
else:
await update.message.reply_text("❌ שמירת ההערה נכשלה")
- except Exception as e:
- await update.message.reply_text(f"❌ שגיאה בשמירת ההערה: {e}")
+ except Exception:
+ logger.exception("שמירת הערה נכשלה")
+ await update.message.reply_text("❌ שמירת ההערה נכשלה, נסה שוב מאוחר יותר")
+ return True
+ # זרימת הוספת הערה לסקיל (אותו מנגנון גנרי כמו הגיבויים, עם skill_id)
+ if context.user_data.get('waiting_for_skill_note_for'):
+ skill_id = context.user_data.pop('waiting_for_skill_note_for')
+ try:
+ from database import db
+ ok = db.save_backup_note(update.effective_user.id, skill_id, (text or '')[:1000])
+ if ok:
+ await update.message.reply_text(
+ "✅ ההערה נשמרה!",
+ reply_markup=InlineKeyboardMarkup([[InlineKeyboardButton("🔙 חזרה", callback_data=f"skill_details:{skill_id}")]])
+ )
+ # מנע הודעת "נראה שזה קטע קוד!" עבור ההודעה הזו
+ context.user_data['suppress_code_hint_once'] = True
+ else:
+ await update.message.reply_text("❌ שמירת ההערה נכשלה")
+ except Exception:
+ logger.exception("שמירת הערה נכשלה")
+ await update.message.reply_text("❌ שמירת ההערה נכשלה, נסה שוב מאוחר יותר")
return True
# קלט נתיב יעד ידני לסביבת העלאה (upload_folder_custom)
if context.user_data.get('waiting_for_upload_folder'):
diff --git a/services/config_inspector_service.py b/services/config_inspector_service.py
index 698fc59f0..07305d7ba 100644
--- a/services/config_inspector_service.py
+++ b/services/config_inspector_service.py
@@ -15,12 +15,16 @@
from enum import Enum
from typing import Any, Dict, List, Optional
+# כתובת URL עם credentials מוטמעים: scheme://user:pass@host — סוד גם אם שם המשתנה אינו רגיש
+_CREDENTIAL_URL_RE = re.compile(r"://[^/\s:@]+:[^/\s:@]+@")
+
class ConfigStatus(str, Enum):
"""סטטוס של משתנה קונפיגורציה."""
DEFAULT = "Default" # משתמש בערך ברירת המחדל
- MODIFIED = "Modified" # ערך שונה מברירת המחדל
+ MODIFIED = "Modified" # ערך שונה מברירת מחדל שקיימת בקוד
+ SET = "Set" # הוגדר בסביבה (למשל ברנדר) כשאין ברירת מחדל בקוד — לא "שונה"
MISSING = "Missing" # משתנה לא מוגדר וגם אין דיפולט
@@ -41,6 +45,12 @@ class ConfigDefinition:
category: str = "general"
sensitive: bool = False # האם להסתיר את הערך
required: bool = False # האם המשתנה הכרחי
+ # לאילו שירותי Render המשתנה שייך (משתנה יכול להיות מוגדר בכמה שירותים במקביל).
+ # "webapp" ⇒ מוצג בעמוד הראשי עם Status/Active Value (ה-inspector רץ בתהליך ה-webapp).
+ # כל שירות אחר ("bot"/"mcp"/"webserver"/"scripts") ⇒ השורה מופיעה גם בעמוד
+ # "שירותים אחרים" עם ציון השירותים — בלי Status/Active Value, כי הערכים שם חיים
+ # בתהליכים נפרדים. הערה: ה-webserver הוא שירות Render נפרד (לא חלק מתהליך הבוט).
+ services: tuple[str, ...] = ("webapp",)
@dataclass
@@ -65,6 +75,7 @@ class ConfigOverview:
generated_at: str = ""
total_count: int = 0
modified_count: int = 0
+ set_count: int = 0
missing_count: int = 0
default_count: int = 0
categories: List[str] = field(default_factory=list)
@@ -80,13 +91,15 @@ class ConfigService:
"""
# מילים רגישות בשמות משתנים - ערכים אלו יוסתרו
+ # הערה: "URL" הוסר בכוונה — כתובת ציבורית (MCP_SERVER_URL, WEBAPP_URL, PROMETHEUS_URL...)
+ # אינה סוד ומיסוכה מטעה. URL שמכיל credentials (כמו MONGODB_URL) מסומן sensitive=True
+ # מפורשות בהגדרתו. "URI" נשאר כי URI-חיבור בדרך כלל מגלם סיסמה.
SENSITIVE_PATTERNS: tuple[str, ...] = (
"TOKEN",
"KEY",
"PASSWORD",
"SECRET",
"URI",
- "URL",
"CREDENTIALS",
"API_KEY",
"AUTH",
@@ -104,6 +117,7 @@ class ConfigService:
CONFIG_DEFINITIONS: Dict[str, ConfigDefinition] = {
"MONGODB_URL": ConfigDefinition(
key="MONGODB_URL",
+ services=("webapp", "bot", "mcp", "webserver"),
default="",
description="כתובת חיבור ל-MongoDB (חובה)",
category="database",
@@ -112,78 +126,91 @@ class ConfigService:
),
"DATABASE_NAME": ConfigDefinition(
key="DATABASE_NAME",
+ services=("webapp", "bot", "mcp", "webserver"),
default="code_keeper_bot",
description="שם מסד הנתונים ב-MongoDB",
category="database",
),
"MONGODB_MAX_POOL_SIZE": ConfigDefinition(
key="MONGODB_MAX_POOL_SIZE",
+ services=("webapp", "bot", "mcp", "webserver"),
default="50",
description="מספר חיבורים מקסימלי לפול MongoDB",
category="database",
),
"MONGODB_MIN_POOL_SIZE": ConfigDefinition(
key="MONGODB_MIN_POOL_SIZE",
+ services=("webapp", "bot", "mcp", "webserver"),
default="5",
description="מספר חיבורים מינימלי לפול MongoDB",
category="database",
),
"MONGODB_MAX_IDLE_TIME_MS": ConfigDefinition(
key="MONGODB_MAX_IDLE_TIME_MS",
+ services=("webapp", "bot", "mcp", "webserver"),
default="30000",
description="זמן סרק מקסימלי לחיבור MongoDB (מילישניות)",
category="database",
),
"MONGODB_WAIT_QUEUE_TIMEOUT_MS": ConfigDefinition(
key="MONGODB_WAIT_QUEUE_TIMEOUT_MS",
+ services=("webapp", "bot", "mcp", "webserver"),
default="8000",
description="זמן המתנה בתור לחיבור MongoDB (מילישניות)",
category="database",
),
"MONGODB_SERVER_SELECTION_TIMEOUT_MS": ConfigDefinition(
key="MONGODB_SERVER_SELECTION_TIMEOUT_MS",
+ services=("webapp", "bot", "mcp", "webserver"),
default="5000",
description="זמן בחירת שרת MongoDB (מילישניות)",
category="database",
),
"MONGODB_SOCKET_TIMEOUT_MS": ConfigDefinition(
key="MONGODB_SOCKET_TIMEOUT_MS",
+ services=("webapp", "bot", "mcp", "webserver"),
default="45000",
description="טיימאאוט סוקט MongoDB (מילישניות)",
category="database",
),
"MONGODB_CONNECT_TIMEOUT_MS": ConfigDefinition(
key="MONGODB_CONNECT_TIMEOUT_MS",
+ services=("webapp", "bot", "mcp", "webserver"),
default="5000",
description="טיימאאוט התחברות ל-MongoDB (מילישניות)",
category="database",
),
"MONGODB_RETRY_WRITES": ConfigDefinition(
key="MONGODB_RETRY_WRITES",
+ services=("webapp", "bot", "mcp", "webserver"),
default="true",
description="הפעלת ניסיונות כתיבה חוזרים ב-MongoDB",
category="database",
),
"MONGODB_RETRY_READS": ConfigDefinition(
key="MONGODB_RETRY_READS",
+ services=("webapp", "bot", "mcp", "webserver"),
default="true",
description="הפעלת ניסיונות קריאה חוזרים ב-MongoDB",
category="database",
),
"MONGODB_APPNAME": ConfigDefinition(
key="MONGODB_APPNAME",
+ services=("webapp", "bot", "mcp", "webserver"),
default="",
description="שם האפליקציה למטא-דאטה MongoDB",
category="database",
),
"MONGODB_COMPRESSORS": ConfigDefinition(
key="MONGODB_COMPRESSORS",
+ services=("webapp", "bot", "mcp", "webserver"),
default="",
description="דחיסנים נתמכים (zstd,snappy,zlib)",
category="database",
),
"DB_HEALTH_TOKEN": ConfigDefinition(
key="DB_HEALTH_TOKEN",
+ services=("webapp", "bot", "webserver"),
default="",
description="טוקן אימות לבדיקות בריאות DB",
category="database",
@@ -191,30 +218,44 @@ class ConfigService:
),
"DB_RECONNECT_WAIT_BEFORE_POLL": ConfigDefinition(
key="DB_RECONNECT_WAIT_BEFORE_POLL",
+ services=("bot",),
default="120",
description="זמן המתנה ראשוני (שניות) להתחברות מחדש ל-DB בעלייה לפני מעבר ל-poll פסיבי",
category="database",
),
"DB_RECONNECT_POLL_INTERVAL": ConfigDefinition(
key="DB_RECONNECT_POLL_INTERVAL",
+ services=("bot",),
default="30",
description="מרווח (שניות) בין בדיקות חיבור ב-poll פסיבי לאחר שחלון ההמתנה הראשוני פג",
category="database",
),
"DB_HEALTH_SLOW_THRESHOLD_MS": ConfigDefinition(
key="DB_HEALTH_SLOW_THRESHOLD_MS",
+ services=("webapp", "bot", "webserver"),
default="1000",
description="סף לזיהוי שאילתות איטיות (מילישניות)",
category="database",
),
"DB_HEALTH_COLLECTIONS_COOLDOWN_SEC": ConfigDefinition(
key="DB_HEALTH_COLLECTIONS_COOLDOWN_SEC",
+ services=("webapp", "bot", "webserver"),
default="30",
description="זמן קירור בין בדיקות בריאות (שניות)",
category="database",
),
+ "BOT_TOKEN": ConfigDefinition(
+ key="BOT_TOKEN",
+ services=("webapp", "bot", "mcp", "webserver"),
+ default="",
+ description="טוקן הבוט מ-BotFather. נדרש גם בשירות ה-webapp (אימות Telegram Login ב-auth_routes).",
+ category="telegram",
+ sensitive=True,
+ required=True,
+ ),
"BOT_USERNAME": ConfigDefinition(
key="BOT_USERNAME",
+ services=("webapp", "bot", "webserver"),
default="my_code_keeper_bot",
description="שם המשתמש של הבוט בטלגרם",
category="telegram",
@@ -222,54 +263,63 @@ class ConfigService:
# --- Telegram Polling / Network timeouts (stability against getUpdates conflicts) ---
"TELEGRAM_CONNECT_TIMEOUT_SECS": ConfigDefinition(
key="TELEGRAM_CONNECT_TIMEOUT_SECS",
+ services=("bot",),
default="10.0",
description="טיימאאוט התחברות ל-Telegram Bot API (שניות).",
category="telegram",
),
"TELEGRAM_POOL_TIMEOUT_SECS": ConfigDefinition(
key="TELEGRAM_POOL_TIMEOUT_SECS",
+ services=("bot",),
default="10.0",
description="טיימאאוט המתנה ל-connection מה-pool (שניות) בעת קריאה ל-Telegram Bot API.",
category="telegram",
),
"TELEGRAM_READ_TIMEOUT_SECS": ConfigDefinition(
key="TELEGRAM_READ_TIMEOUT_SECS",
+ services=("bot",),
default="30.0",
description="טיימאאוט קריאה ל-Telegram Bot API (שניות). מומלץ להיות גבוה מ-TELEGRAM_LONG_POLL_TIMEOUT_SECS.",
category="telegram",
),
"TELEGRAM_WRITE_TIMEOUT_SECS": ConfigDefinition(
key="TELEGRAM_WRITE_TIMEOUT_SECS",
+ services=("bot",),
default="30.0",
description="טיימאאוט כתיבה ל-Telegram Bot API (שניות).",
category="telegram",
),
"TELEGRAM_LONG_POLL_TIMEOUT_SECS": ConfigDefinition(
key="TELEGRAM_LONG_POLL_TIMEOUT_SECS",
+ services=("bot",),
default="20",
description="timeout של long-polling עבור getUpdates (שניות).",
category="telegram",
),
"TELEGRAM_POLL_INTERVAL_SECS": ConfigDefinition(
key="TELEGRAM_POLL_INTERVAL_SECS",
+ services=("bot",),
default="0.0",
description="poll_interval בין סבבי polling (שניות). 0 = ברירת מחדל של PTB.",
category="telegram",
),
"TELEGRAM_CONFLICT_BACKOFF_SECS": ConfigDefinition(
key="TELEGRAM_CONFLICT_BACKOFF_SECS",
+ services=("bot",),
default="30",
description="זמן המתנה (שניות) לפני retry כאשר מתקבלת שגיאת 409 Conflict ב-getUpdates.",
category="telegram",
),
"TELEGRAM_CONFLICT_MAX_RETRIES": ConfigDefinition(
key="TELEGRAM_CONFLICT_MAX_RETRIES",
+ services=("bot",),
default="5",
description="כמה פעמים לנסות שוב (retry) אחרי 409 Conflict ב-getUpdates לפני יציאה מהתהליך כדי לשחרר lock ולאפשר recovery. 0/שלילי = ללא הגבלה (לא מומלץ).",
category="telegram",
),
"TELEGRAM_CONFLICT_MAX_SECONDS": ConfigDefinition(
key="TELEGRAM_CONFLICT_MAX_SECONDS",
+ services=("bot",),
default="300",
description="חלון זמן מקסימלי (שניות) לרצף conflicts לפני יציאה מהתהליך כדי לשחרר lock ולאפשר recovery. 0/שלילי = ללא הגבלה (לא מומלץ).",
category="telegram",
@@ -277,96 +327,112 @@ class ConfigService:
# --- Distributed Lock (Mongo Lease + Heartbeat) ---
"SERVICE_ID": ConfigDefinition(
key="SERVICE_ID",
+ services=("bot",),
default="",
description="מזהה ייחודי לשירות/סביבה עבור נעילה מבוזרת (key של מסמך הלוק). אם ריק, נופל ל-LOCK_ID המובנה.",
category="locking",
),
"RENDER_INSTANCE_ID": ConfigDefinition(
key="RENDER_INSTANCE_ID",
+ services=("bot",),
default="",
description="מזהה אינסטנס ב-Render (נשמר במסמך הלוק לצורכי תחקור). ה-owner בפועל הוא מזהה תהליך ייחודי (RENDER_INSTANCE_ID:pid). אם ריק, owner נופל ל-hostname:pid.",
category="locking",
),
"RENDER_SERVICE_NAME": ConfigDefinition(
key="RENDER_SERVICE_NAME",
+ services=("bot",),
default="",
description="שם השירות (label) לצורכי תחקור בלוק (host). אם ריק, נופל ל-HOSTNAME/hostname.",
category="locking",
),
"LOCK_LEASE_SECONDS": ConfigDefinition(
key="LOCK_LEASE_SECONDS",
+ services=("bot",),
default="10",
description="משך ה-lease של הלוק (שניות).",
category="locking",
),
"LOCK_HEARTBEAT_INTERVAL": ConfigDefinition(
key="LOCK_HEARTBEAT_INTERVAL",
+ services=("bot",),
default="3",
description="תדירות heartbeat (שניות) לרענון ה-lease. ברירת מחדל: 3 (מינימום 3).",
category="locking",
),
"LOCK_WAIT_FOR_ACQUIRE": ConfigDefinition(
key="LOCK_WAIT_FOR_ACQUIRE",
+ services=("bot",),
default="false",
description="אם true: המתנה אקטיבית ללוק עם retries קצרים. אם false: המתנה פסיבית עם jitter (ברירת מחדל).",
category="locking",
),
"LOCK_ACQUIRE_MAX_WAIT": ConfigDefinition(
key="LOCK_ACQUIRE_MAX_WAIT",
+ services=("bot",),
default="0",
description="מגבלת זמן (שניות) במצב המתנה אקטיבית. 0 = ללא מגבלה. (אליאס תאימות: LOCK_MAX_WAIT_SECONDS).",
category="locking",
),
"LOCK_WAIT_MIN_SECONDS": ConfigDefinition(
key="LOCK_WAIT_MIN_SECONDS",
+ services=("bot",),
default="15",
description="מינימום זמן המתנה פסיבית עם jitter (שניות).",
category="locking",
),
"LOCK_WAIT_MAX_SECONDS": ConfigDefinition(
key="LOCK_WAIT_MAX_SECONDS",
+ services=("bot",),
default="45",
description="מקסימום זמן המתנה פסיבית עם jitter (שניות).",
category="locking",
),
"LOCK_RETRY_INTERVAL_SECONDS": ConfigDefinition(
key="LOCK_RETRY_INTERVAL_SECONDS",
+ services=("bot",),
default="1",
description="זמן המתנה בין ניסיונות במצב המתנה אקטיבית. (Legacy/תאימות לאחור: שימש גם קודם).",
category="locking",
),
"LOCK_FAIL_OPEN": ConfigDefinition(
key="LOCK_FAIL_OPEN",
+ services=("bot",),
default="false",
description="אם true: במקרה חריגות ברכישת לוק, מאפשר עלייה 'ללא לוק' (לא מומלץ). ברירת מחדל false (fail-closed).",
category="locking",
),
"LOCK_WAIT_HEALTH_SERVER_ENABLED": ConfigDefinition(
key="LOCK_WAIT_HEALTH_SERVER_ENABLED",
+ services=("bot",),
default="true",
description="אם true: בעת המתנה ללוק ותוך קיום PORT, מפעיל שרת HTTP מינימלי ל-/health כדי לעבור health checks.",
category="locking",
),
"LOCK_PORT_GUARD_ENABLED": ConfigDefinition(
key="LOCK_PORT_GUARD_ENABLED",
+ services=("bot",),
default="false",
description="אם true: תופס פורט לוקאלי כדי למנוע שני תהליכים באותו worker. אם הפורט תפוס → יציאה.",
category="locking",
),
"LOCK_PORT_GUARD_PORT": ConfigDefinition(
key="LOCK_PORT_GUARD_PORT",
+ services=("bot",),
default="9999",
description="פורט לוקאלי לשמירה על בלעדיות תהליך (נדרש רק אם LOCK_PORT_GUARD_ENABLED=true).",
category="locking",
),
"LOCK_COLLECTION": ConfigDefinition(
key="LOCK_COLLECTION",
+ services=("bot",),
default="locks",
description="שם קולקציית הלוקים ב-MongoDB (ברירת מחדל legacy: locks).",
category="locking",
),
"ADMIN_USER_IDS": ConfigDefinition(
key="ADMIN_USER_IDS",
+ services=("webapp", "bot", "mcp", "webserver"),
default="",
description="רשימת מזהי אדמינים (מופרדים בפסיקים)",
category="telegram",
@@ -374,12 +440,14 @@ class ConfigService:
),
"PREMIUM_USER_IDS": ConfigDefinition(
key="PREMIUM_USER_IDS",
+ services=("webapp", "bot", "webserver"),
default="",
description="רשימת מזהי משתמשי פרימיום",
category="telegram",
),
"ALERT_TELEGRAM_BOT_TOKEN": ConfigDefinition(
key="ALERT_TELEGRAM_BOT_TOKEN",
+ services=("webapp", "bot", "mcp", "webserver"),
default="",
description="טוקן בוט התראות טלגרם",
category="alerts",
@@ -387,6 +455,7 @@ class ConfigService:
),
"ALERT_TELEGRAM_CHAT_ID": ConfigDefinition(
key="ALERT_TELEGRAM_CHAT_ID",
+ services=("webapp", "bot", "mcp", "webserver"),
default="",
description="מזהה צ'אט להתראות טלגרם",
category="alerts",
@@ -394,12 +463,14 @@ class ConfigService:
),
"ALERT_TELEGRAM_MIN_SEVERITY": ConfigDefinition(
key="ALERT_TELEGRAM_MIN_SEVERITY",
+ services=("webapp", "bot", "mcp", "webserver"),
default="info",
description="רמת חומרה מינימלית להתראות טלגרם",
category="alerts",
),
"ALERT_TELEGRAM_SUPPRESS_ALERTS": ConfigDefinition(
key="ALERT_TELEGRAM_SUPPRESS_ALERTS",
+ services=("webapp", "bot", "mcp", "webserver"),
default="AppLatencyEWMARegression",
description=(
"שמות alerts (מופרדים בפסיקים) שלא יישלחו לטלגרם. "
@@ -409,72 +480,84 @@ class ConfigService:
),
"ALERT_STARTUP_GRACE_PERIOD_SECONDS": ConfigDefinition(
key="ALERT_STARTUP_GRACE_PERIOD_SECONDS",
+ services=("webapp", "bot", "mcp", "webserver"),
default="1200",
description="חלון חסד (שניות) לאחר אתחול שבו מושתקים רק alerts רועשים מתוך allowlist (Mongo/Latency/EWMA)",
category="alerts",
),
"ALERTS_TEXT_INCLUDE_DASHBOARD_LINK_TELEGRAM": ConfigDefinition(
key="ALERTS_TEXT_INCLUDE_DASHBOARD_LINK_TELEGRAM",
+ services=("webapp", "bot"),
default="false",
description="אם true מוסיף שורת 📊 Dashboard לגוף ההודעה בטלגרם (ברירת מחדל כבוי כי יש כפתור Inline)",
category="alerts",
),
"ALERTS_TEXT_INCLUDE_DASHBOARD_LINK_SLACK": ConfigDefinition(
key="ALERTS_TEXT_INCLUDE_DASHBOARD_LINK_SLACK",
+ services=("webapp", "bot"),
default="true",
description="אם true מוסיף שורת 📊 Dashboard לגוף ההודעה ב-Slack (ברירת מחדל פעיל)",
category="alerts",
),
"REDIS_MAX_CONNECTIONS": ConfigDefinition(
key="REDIS_MAX_CONNECTIONS",
+ services=("webapp", "bot", "mcp", "webserver"),
default="50",
description="מספר חיבורים מקסימלי ל-Redis",
category="cache",
),
"REDIS_CONNECT_TIMEOUT": ConfigDefinition(
key="REDIS_CONNECT_TIMEOUT",
+ services=("webapp", "bot", "mcp", "webserver"),
default="3",
description="טיימאאוט התחברות ל-Redis (שניות)",
category="cache",
),
"REDIS_SOCKET_TIMEOUT": ConfigDefinition(
key="REDIS_SOCKET_TIMEOUT",
+ services=("webapp", "bot", "mcp", "webserver"),
default="5",
description="טיימאאוט סוקט Redis (שניות)",
category="cache",
),
"CACHE_ENABLED": ConfigDefinition(
key="CACHE_ENABLED",
+ services=("webapp", "bot", "mcp", "webserver"),
default="false",
description="הפעלת קאשינג גלובלי",
category="cache",
),
"CACHE_CLEAR_BUDGET_SECONDS": ConfigDefinition(
key="CACHE_CLEAR_BUDGET_SECONDS",
+ services=("webapp", "bot", "mcp", "webserver"),
default="5",
description="תקציב זמן לניקוי קאש (שניות)",
category="cache",
),
"CACHE_DELETE_PATTERN_BUDGET_SECONDS": ConfigDefinition(
key="CACHE_DELETE_PATTERN_BUDGET_SECONDS",
+ services=("webapp", "bot", "mcp", "webserver"),
default="5",
description="תקציב זמן למחיקת תבנית מפתחות בקאש (שניות) – SCAN+DEL, למניעת תקיעה ב-Redis גדול",
category="cache",
),
"DISABLE_CACHE_MAINTENANCE": ConfigDefinition(
key="DISABLE_CACHE_MAINTENANCE",
+ services=("webapp", "bot", "mcp", "webserver"),
default="false",
description="השבתת תחזוקת קאש אוטומטית",
category="cache",
),
"PORT": ConfigDefinition(
key="PORT",
+ services=("webapp", "bot", "mcp", "webserver"),
default="5000",
description="פורט השרת (Render/Heroku)",
category="webserver",
),
"SECRET_KEY": ConfigDefinition(
key="SECRET_KEY",
+ services=("webapp", "bot", "mcp", "webserver"),
default="dev-secret-key-change-in-production",
description=(
"מפתח סודי לסשנים ו-CSRF. במצב OAuth של ה-MCP: חותם את זהות המשתמש בין "
@@ -486,6 +569,7 @@ class ConfigService:
),
"WEBAPP_LOGIN_SECRET": ConfigDefinition(
key="WEBAPP_LOGIN_SECRET",
+ services=("webapp", "bot", "webserver"),
default="",
description="מפתח סודי נוסף ל-login",
category="webserver",
@@ -493,24 +577,28 @@ class ConfigService:
),
"DEBUG": ConfigDefinition(
key="DEBUG",
+ services=("webapp", "bot", "mcp", "webserver"),
default="false",
description="מצב דיבאג (true/false)",
category="webserver",
),
"PUBLIC_BASE_URL": ConfigDefinition(
key="PUBLIC_BASE_URL",
+ services=("webapp", "bot", "mcp", "webserver"),
default="",
description="כתובת URL בסיסית לשיתוף קישורים",
category="webserver",
),
"PUBLIC_URL": ConfigDefinition(
key="PUBLIC_URL",
+ services=("webapp", "bot", "mcp", "webserver"),
default="https://code-keeper-webapp.onrender.com",
description="כתובת בסיס ציבורית של ה-WebApp (משמשת ליצירת קישור ציבורי ל-Observability Dashboard בהתראות)",
category="webserver",
),
"WEBAPP_URL": ConfigDefinition(
key="WEBAPP_URL",
+ services=("webapp", "bot", "mcp", "webserver"),
default="",
description="כתובת WebApp (אם שונה מ-public)",
category="webserver",
@@ -519,6 +607,7 @@ class ConfigService:
# --- MCP Server (Claude integration) ---
"MCP_SERVER_URL": ConfigDefinition(
key="MCP_SERVER_URL",
+ services=("webapp", "bot", "mcp", "webserver"),
default="",
description=(
"ה-URL הציבורי (https) של שירות ה-MCP. בבוט: בניית פקודת /connect_claude. "
@@ -529,24 +618,28 @@ class ConfigService:
),
"MCP_SERVER_NAME": ConfigDefinition(
key="MCP_SERVER_NAME",
+ services=("mcp",),
default="CodeKeeper",
description="שם התצוגה של שרת ה-MCP (שם ה-Connector שמוצג ללקוח).",
category="mcp",
),
"MCP_ALLOWED_HOSTS": ConfigDefinition(
key="MCP_ALLOWED_HOSTS",
+ services=("mcp",),
default="",
description="Host מותרים לשרת ה-MCP (CSV, תומך wildcard). ריק = הגנת DNS-rebinding כבויה (מתאים לשרת ציבורי מוגן-טוקן).",
category="mcp",
),
"MCP_ALLOWED_ORIGINS": ConfigDefinition(
key="MCP_ALLOWED_ORIGINS",
+ services=("mcp",),
default="",
description="Origin מותרים לשרת ה-MCP (CSV). רלוונטי רק כשמפעילים הגנה דרך MCP_ALLOWED_HOSTS.",
category="mcp",
),
"MCP_REPO_DENYLIST_EXTRA": ConfigDefinition(
key="MCP_REPO_DENYLIST_EXTRA",
+ services=("mcp",),
default="",
description=(
"תבניות glob נוספות (CSV) ל-denylist הסודות של כלי דפדפן הריפו ב-MCP, "
@@ -556,6 +649,7 @@ class ConfigService:
),
"MCP_REPO_AUTOSYNC": ConfigDefinition(
key="MCP_REPO_AUTOSYNC",
+ services=("mcp",),
default="1",
description=(
"רענון אוטומטי של ה-mirrors המקומיים בשירות ה-MCP (thread רקע): "
@@ -565,6 +659,7 @@ class ConfigService:
),
"MCP_REPO_AUTOSYNC_INTERVAL": ConfigDefinition(
key="MCP_REPO_AUTOSYNC_INTERVAL",
+ services=("mcp",),
default="300",
description="מרווח בשניות בין מעברי ה-autosync של דפדפן הריפו ב-MCP (מינימום 30).",
category="mcp",
@@ -573,18 +668,21 @@ class ConfigService:
# --- Repo Sync Engine (Git Mirror) ---
"REPO_NAME": ConfigDefinition(
key="REPO_NAME",
+ services=("webapp", "bot", "webserver"),
default="CodeBot",
description="שם ריפו לוגי לשימוש ב-Repo Sync (מפתח ל-mirror בדיסק ול-metadata ב-DB).",
category="repo_sync",
),
"REPO_MIRROR_PATH": ConfigDefinition(
key="REPO_MIRROR_PATH",
+ services=("webapp", "bot", "mcp", "webserver"),
default="/var/data/repos",
description="נתיב בסיסי בדיסק לשמירת Bare Mirror של הריפו (Repo Sync Engine).",
category="repo_sync",
),
"GITHUB_WEBHOOK_SECRET": ConfigDefinition(
key="GITHUB_WEBHOOK_SECRET",
+ services=("webapp", "bot", "webserver"),
default="",
description="סוד לאימות GitHub Webhook (HMAC SHA256) עבור POST /api/webhooks/github (Repo Sync).",
category="repo_sync",
@@ -592,6 +690,7 @@ class ConfigService:
),
"GITHUB_TOKEN": ConfigDefinition(
key="GITHUB_TOKEN",
+ services=("webapp", "bot", "mcp", "webserver"),
default="",
description="טוקן GitHub לשימוש בפעולות API וגם לאימות clone/fetch של Repo Sync בריפו פרטי (אם רלוונטי).",
category="repo_sync",
@@ -599,6 +698,7 @@ class ConfigService:
),
"GITHUB_TOKENS": ConfigDefinition(
key="GITHUB_TOKENS",
+ services=("webapp", "bot", "mcp", "webserver"),
default="",
description=(
"מיפוי בעלים(owner/org)→טוקן לסנכרון ריפואים ממספר ארגונים, כשטוקן יחיד לא מכסה את כולם. "
@@ -610,12 +710,14 @@ class ConfigService:
),
"BOT_JOBS_API_BASE_URL": ConfigDefinition(
key="BOT_JOBS_API_BASE_URL",
+ services=("webapp", "bot", "webserver"),
default="",
description="בסיס URL ל-API הפנימי של הבוט עבור Trigger של Jobs ממסך המוניטור (WebApp -> Bot).",
category="jobs_monitor",
),
"BOT_API_BASE_URL": ConfigDefinition(
key="BOT_API_BASE_URL",
+ services=("webapp", "bot", "webserver"),
default="",
description="Alias/תאימות לאחור ל-BOT_JOBS_API_BASE_URL (נבדק רק אם BOT_JOBS_API_BASE_URL ריק).",
category="jobs_monitor",
@@ -658,6 +760,7 @@ class ConfigService:
),
"WEB_CONCURRENCY": ConfigDefinition(
key="WEB_CONCURRENCY",
+ services=("webapp", "bot", "webserver"),
default="1",
description="מספר ה-workers של Gunicorn ב-WebApp; אם מוגדר, גובר על ברירת המחדל ומקטין queue_delay תחת עומס",
category="gunicorn",
@@ -706,60 +809,70 @@ class ConfigService:
),
"AIOHTTP_POOL_LIMIT": ConfigDefinition(
key="AIOHTTP_POOL_LIMIT",
+ services=("webapp", "bot", "mcp", "webserver"),
default="50",
description="מגבלת חיבורים ב-TCPConnector של aiohttp",
category="http",
),
"AIOHTTP_TIMEOUT_TOTAL": ConfigDefinition(
key="AIOHTTP_TIMEOUT_TOTAL",
+ services=("webapp", "bot", "mcp", "webserver"),
default="10",
description="טיימאאוט כולל ל-aiohttp (שניות)",
category="http",
),
"AIOHTTP_LIMIT_PER_HOST": ConfigDefinition(
key="AIOHTTP_LIMIT_PER_HOST",
+ services=("webapp", "bot", "mcp", "webserver"),
default="25",
description="מגבלת חיבורים לכל host",
category="http",
),
"REQUESTS_POOL_CONNECTIONS": ConfigDefinition(
key="REQUESTS_POOL_CONNECTIONS",
+ services=("webapp", "bot", "mcp", "webserver"),
default="20",
description="חיבורי פול עבור requests",
category="http",
),
"REQUESTS_POOL_MAXSIZE": ConfigDefinition(
key="REQUESTS_POOL_MAXSIZE",
+ services=("webapp", "bot", "mcp", "webserver"),
default="100",
description="גודל מקסימלי לפול requests",
category="http",
),
"REQUESTS_TIMEOUT": ConfigDefinition(
key="REQUESTS_TIMEOUT",
+ services=("webapp", "bot", "mcp", "webserver"),
default="8.0",
description="טיימאאוט ברירת מחדל ל-requests (שניות)",
category="http",
),
"REQUESTS_RETRIES": ConfigDefinition(
key="REQUESTS_RETRIES",
+ services=("webapp", "bot", "mcp", "webserver"),
default="2",
description="מספר ניסיונות חוזרים ב-requests",
category="http",
),
"REQUESTS_RETRY_BACKOFF": ConfigDefinition(
key="REQUESTS_RETRY_BACKOFF",
+ services=("webapp", "bot", "mcp", "webserver"),
default="0.2",
description="פקטור backoff בין ניסיונות",
category="http",
),
"PUSH_NOTIFICATIONS_ENABLED": ConfigDefinition(
key="PUSH_NOTIFICATIONS_ENABLED",
+ services=("webapp", "bot", "webserver"),
default="true",
description="הפעלת התראות Push",
category="push",
),
"VAPID_PUBLIC_KEY": ConfigDefinition(
key="VAPID_PUBLIC_KEY",
+ services=("webapp", "bot", "webserver"),
default="",
description="מפתח VAPID ציבורי ל-Push",
category="push",
@@ -767,6 +880,7 @@ class ConfigService:
),
"VAPID_PRIVATE_KEY": ConfigDefinition(
key="VAPID_PRIVATE_KEY",
+ services=("webapp", "bot", "webserver"),
default="",
description="מפתח VAPID פרטי ל-Push",
category="push",
@@ -774,24 +888,28 @@ class ConfigService:
),
"VAPID_SUB_EMAIL": ConfigDefinition(
key="VAPID_SUB_EMAIL",
+ services=("webapp", "bot", "webserver"),
default="",
description="כתובת אימייל ל-VAPID",
category="push",
),
"SUPPORT_EMAIL": ConfigDefinition(
key="SUPPORT_EMAIL",
+ services=("webapp", "bot", "webserver"),
default="",
description="כתובת אימייל תמיכה",
category="push",
),
"PUSH_REMOTE_DELIVERY_ENABLED": ConfigDefinition(
key="PUSH_REMOTE_DELIVERY_ENABLED",
+ services=("webapp", "bot", "mcp", "webserver"),
default="false",
description="הפעלת משלוח Push מרוחק",
category="push",
),
"PUSH_DELIVERY_URL": ConfigDefinition(
key="PUSH_DELIVERY_URL",
+ services=("webapp", "bot", "mcp", "webserver"),
default="",
description="כתובת URL למשלוח Push",
category="push",
@@ -799,6 +917,7 @@ class ConfigService:
),
"PUSH_DELIVERY_TOKEN": ConfigDefinition(
key="PUSH_DELIVERY_TOKEN",
+ services=("webapp", "bot", "mcp", "webserver"),
default="",
description="טוקן אימות למשלוח Push",
category="push",
@@ -806,30 +925,35 @@ class ConfigService:
),
"PUSH_DELIVERY_TIMEOUT_SECONDS": ConfigDefinition(
key="PUSH_DELIVERY_TIMEOUT_SECONDS",
+ services=("webapp", "bot", "mcp", "webserver"),
default="3",
description="טיימאאוט למשלוח Push (שניות)",
category="push",
),
"PUSH_DELIVERY_URGENCY": ConfigDefinition(
key="PUSH_DELIVERY_URGENCY",
+ services=("webapp", "bot", "webserver"),
default="high",
description="רמת דחיפות ברירת מחדל ל-Push",
category="push",
),
"PUSH_SEND_INTERVAL_SECONDS": ConfigDefinition(
key="PUSH_SEND_INTERVAL_SECONDS",
+ services=("webapp", "bot", "webserver"),
default="60",
description="מרווח שליחת Push (שניות)",
category="push",
),
"PUSH_CLAIM_TTL_SECONDS": ConfigDefinition(
key="PUSH_CLAIM_TTL_SECONDS",
+ services=("webapp", "bot", "webserver"),
default="60",
description="TTL להחזקת Push (שניות)",
category="push",
),
"PASTEBIN_API_KEY": ConfigDefinition(
key="PASTEBIN_API_KEY",
+ services=("webapp", "bot", "mcp", "webserver"),
default="",
description="מפתח API ל-Pastebin",
category="external",
@@ -837,6 +961,7 @@ class ConfigService:
),
"SENTRY_DSN": ConfigDefinition(
key="SENTRY_DSN",
+ services=("webapp", "bot", "mcp", "webserver"),
default="",
description="DSN ל-Sentry לניטור שגיאות",
category="monitoring",
@@ -844,24 +969,28 @@ class ConfigService:
),
"SENTRY_DASHBOARD_URL": ConfigDefinition(
key="SENTRY_DASHBOARD_URL",
+ services=("webapp", "bot", "mcp", "webserver"),
default="",
description="כתובת לוח הבקרה של Sentry",
category="monitoring",
),
"SENTRY_TRACES_SAMPLE_RATE": ConfigDefinition(
key="SENTRY_TRACES_SAMPLE_RATE",
+ services=("webapp", "bot", "mcp", "webserver"),
default="0.1",
description="שיעור דגימת Traces ב-Sentry",
category="monitoring",
),
"SENTRY_PROFILES_SAMPLE_RATE": ConfigDefinition(
key="SENTRY_PROFILES_SAMPLE_RATE",
+ services=("webapp", "bot", "mcp", "webserver"),
default="0.1",
description="שיעור דגימת Profiles ב-Sentry",
category="monitoring",
),
"SENTRY_WEBHOOK_SECRET": ConfigDefinition(
key="SENTRY_WEBHOOK_SECRET",
+ services=("webserver",),
default="",
description="סוד ל-Sentry Webhook",
category="monitoring",
@@ -869,66 +998,77 @@ class ConfigService:
),
"SENTRY_WEBHOOK_DEDUP_WINDOW_SECONDS": ConfigDefinition(
key="SENTRY_WEBHOOK_DEDUP_WINDOW_SECONDS",
+ services=("webserver",),
default="300",
description="חלון dedup ל-Sentry Webhooks (שניות)",
category="monitoring",
),
"OTEL_EXPORTER_OTLP_ENDPOINT": ConfigDefinition(
key="OTEL_EXPORTER_OTLP_ENDPOINT",
+ services=("webapp", "bot", "webserver"),
default="",
description="Endpoint ל-OTLP Exporter",
category="monitoring",
),
"OTEL_EXPORTER_INSECURE": ConfigDefinition(
key="OTEL_EXPORTER_INSECURE",
+ services=("webapp", "bot", "webserver"),
default="false",
description="שימוש בחיבור לא מאובטח ל-OTLP",
category="monitoring",
),
"OBS_AI_EXPLAIN_TIMEOUT": ConfigDefinition(
key="OBS_AI_EXPLAIN_TIMEOUT",
+ services=("webapp", "bot", "mcp", "webserver"),
default="10",
description="טיימאאוט לבקשות AI (שניות)",
category="ai",
),
"OBS_AI_EXPLAIN_CACHE_TTL": ConfigDefinition(
key="OBS_AI_EXPLAIN_CACHE_TTL",
+ services=("webapp", "bot", "mcp", "webserver"),
default="600",
description="TTL לקאש הסברי AI (שניות)",
category="ai",
),
"LOG_LEVEL": ConfigDefinition(
key="LOG_LEVEL",
+ services=("webapp", "bot", "mcp", "webserver"),
default="INFO",
description="רמת הלוגים (DEBUG/INFO/WARNING/ERROR/CRITICAL או ערך מספרי כמו 10/20/30)",
category="logging",
),
"LOG_FORMAT": ConfigDefinition(
key="LOG_FORMAT",
+ services=("webapp", "bot", "mcp", "webserver"),
default="json",
description="פורמט הלוגים (json/console)",
category="logging",
),
"LOG_INFO_SAMPLE_RATE": ConfigDefinition(
key="LOG_INFO_SAMPLE_RATE",
+ services=("webapp", "bot", "mcp", "webserver"),
default="1.0",
description="שיעור דגימת לוגים ברמת INFO",
category="logging",
),
"LOG_INFO_SAMPLE_ALLOWLIST": ConfigDefinition(
key="LOG_INFO_SAMPLE_ALLOWLIST",
+ services=("webapp", "bot", "mcp", "webserver"),
default="",
description="רשימת אירועים שלא יידגמו (מופרדים בפסיקים)",
category="logging",
),
"ALERT_QUICK_FIX_PATH": ConfigDefinition(
key="ALERT_QUICK_FIX_PATH",
+ services=("webapp", "bot", "mcp", "webserver"),
default="config/alert_quick_fixes.json",
description="נתיב לקובץ תיקונים מהירים",
category="alerts",
),
"ALERTMANAGER_WEBHOOK_SECRET": ConfigDefinition(
key="ALERTMANAGER_WEBHOOK_SECRET",
+ services=("webapp", "bot", "webserver"),
default="",
description="סוד Webhook ל-Alertmanager",
category="alerts",
@@ -936,210 +1076,245 @@ class ConfigService:
),
"ALERTMANAGER_IP_ALLOWLIST": ConfigDefinition(
key="ALERTMANAGER_IP_ALLOWLIST",
+ services=("webapp", "bot", "webserver"),
default="",
description="רשימת IP מותרים ל-Alertmanager",
category="alerts",
),
"ALLOWED_WEBHOOK_HOSTS": ConfigDefinition(
key="ALLOWED_WEBHOOK_HOSTS",
+ services=("webapp", "bot", "mcp", "webserver"),
default="",
description="Allowlist אופציונלי ליעדי webhook (Visual Rule Engine) לפי hostnames (CSV)",
category="alerts",
),
"ALLOWED_WEBHOOK_SUFFIXES": ConfigDefinition(
key="ALLOWED_WEBHOOK_SUFFIXES",
+ services=("webapp", "bot", "mcp", "webserver"),
default="",
description="Allowlist אופציונלי ליעדי webhook (Visual Rule Engine) לפי סיומות דומיין (CSV, למשל .example.com)",
category="alerts",
),
"OBSERVABILITY_RUNBOOK_PATH": ConfigDefinition(
key="OBSERVABILITY_RUNBOOK_PATH",
+ services=("webapp", "bot", "mcp", "webserver"),
default="config/observability_runbooks.yml",
description="נתיב לקובץ Runbooks",
category="observability",
),
"ALERT_TAGS_COLLECTION": ConfigDefinition(
key="ALERT_TAGS_COLLECTION",
+ services=("webapp", "bot", "mcp", "webserver"),
default="alert_tags",
description="שם ה-Collection לתגיות התראות (Manual Alert Tagging) ב-Observability",
category="observability",
),
"ALERT_TAGS_DB_DISABLED": ConfigDefinition(
key="ALERT_TAGS_DB_DISABLED",
+ services=("webapp", "bot", "mcp", "webserver"),
default="false",
description="אם true מכבה שמירה/שליפה של תגיות להתראות (Manual Alert Tagging) מה-DB",
category="observability",
),
"OBS_RUNBOOK_STATE_TTL": ConfigDefinition(
key="OBS_RUNBOOK_STATE_TTL",
+ services=("webapp", "bot", "mcp", "webserver"),
default="14400",
description="TTL למצב Runbook (שניות)",
category="observability",
),
"OBS_RUNBOOK_EVENT_TTL": ConfigDefinition(
key="OBS_RUNBOOK_EVENT_TTL",
+ services=("webapp", "bot", "mcp", "webserver"),
default="900",
description="TTL לאירועי Runbook (שניות)",
category="observability",
),
"OBSERVABILITY_WARMUP_RANGES": ConfigDefinition(
key="OBSERVABILITY_WARMUP_RANGES",
+ services=("webapp", "bot", "webserver"),
default="24h,7d,30d",
description="רשימת טווחי זמן (CSV) לחימום /api/observability/aggregations",
category="observability",
),
"OBSERVABILITY_WARMUP_ENABLED": ConfigDefinition(
key="OBSERVABILITY_WARMUP_ENABLED",
+ services=("webapp", "bot", "webserver"),
default="true",
description="הפעלה/כיבוי של Warmup כבד לדוחות Observability ברקע אחרי עליית התהליך",
category="observability",
),
"OBSERVABILITY_WARMUP_DELAY_SECONDS": ConfigDefinition(
key="OBSERVABILITY_WARMUP_DELAY_SECONDS",
+ services=("webapp", "bot", "webserver"),
default="5",
description="השהייה (שניות) לפני תחילת Warmup הדוחות כדי לא להעמיס בזמן העלייה",
category="observability",
),
"OBSERVABILITY_WARMUP_BUDGET_SECONDS": ConfigDefinition(
key="OBSERVABILITY_WARMUP_BUDGET_SECONDS",
+ services=("webapp", "bot", "webserver"),
default="20",
description="תקציב זמן מקסימלי (שניות) ל-Warmup הדוחות ברקע; מעבר לתקציב נעצור מוקדם",
category="observability",
),
"OBSERVABILITY_WARMUP_SLOW_LIMIT": ConfigDefinition(
key="OBSERVABILITY_WARMUP_SLOW_LIMIT",
+ services=("webapp", "bot", "webserver"),
default="5",
description="ערך slow_endpoints_limit עבור החימום (ברירת מחדל כמו ב-API)",
category="observability",
),
"SAFE_MODE": ConfigDefinition(
key="SAFE_MODE",
+ services=("webapp", "bot", "mcp", "webserver"),
default="false",
description="מצב בטוח - משבית פעולות מסוכנות",
category="predictive",
),
"DISABLE_PREEMPTIVE_ACTIONS": ConfigDefinition(
key="DISABLE_PREEMPTIVE_ACTIONS",
+ services=("webapp", "bot", "mcp", "webserver"),
default="false",
description="השבתת פעולות מנע אוטומטיות",
category="predictive",
),
"RATE_LIMIT_SHADOW_MODE": ConfigDefinition(
key="RATE_LIMIT_SHADOW_MODE",
+ services=("webapp", "bot", "mcp", "webserver"),
default="false",
description="מצב צל - ספירה בלבד ללא חסימה",
category="rate_limit",
),
"RATE_LIMIT_PER_MINUTE": ConfigDefinition(
key="RATE_LIMIT_PER_MINUTE",
+ services=("webapp", "bot", "mcp", "webserver"),
default="30",
description="מגבלת בקשות לדקה",
category="rate_limit",
),
"ENABLE_METRICS": ConfigDefinition(
key="ENABLE_METRICS",
+ services=("webapp", "bot", "webserver"),
default="false",
description="הפעלת יצוא Metrics דרך OTLP (OpenTelemetry Metrics). כדי לפעול בפועל צריך גם OTEL_EXPORTER_OTLP_ENDPOINT.",
category="metrics",
),
"ENABLE_PROMETHEUS_METRICS": ConfigDefinition(
key="ENABLE_PROMETHEUS_METRICS",
+ services=("webapp", "bot", "webserver"),
default="false",
description="הפעלת OpenTelemetry Prometheus exporter (scrape דרך /metrics).",
category="metrics",
),
"ENABLE_PROMETHEUS_OTEL_METRICS": ConfigDefinition(
key="ENABLE_PROMETHEUS_OTEL_METRICS",
+ services=("webapp", "bot", "webserver"),
default="false",
description="Alias ל-ENABLE_PROMETHEUS_METRICS (תאימות לאחור).",
category="metrics",
),
"PROMETHEUS_URL": ConfigDefinition(
key="PROMETHEUS_URL",
+ services=("webapp", "bot", "mcp", "webserver"),
default="",
description="בסיס URL ל-Prometheus HTTP API. כשמוגדר, דשבורד Observability יקרא timeseries מ-Prometheus במקום מה-DB.",
category="observability",
),
"PROMETHEUS_RATE_WINDOW": ConfigDefinition(
key="PROMETHEUS_RATE_WINDOW",
+ services=("webapp", "bot", "mcp", "webserver"),
default="5m",
description="חלון ברירת מחדל ל-rate()/histogram_quantile() ב-PromQL (למשל 5m).",
category="observability",
),
"HTTP_SAMPLE_BUFFER": ConfigDefinition(
key="HTTP_SAMPLE_BUFFER",
+ services=("webapp", "bot", "mcp", "webserver"),
default="2000",
description="גודל באפר דגימות HTTP",
category="metrics",
),
"QUEUE_DELAY_WARN_MS": ConfigDefinition(
key="QUEUE_DELAY_WARN_MS",
+ services=("webapp", "bot", "webserver"),
default="500",
description="סף אזהרת עיכוב תור (מילישניות)",
category="performance",
),
"SLOW_MS": ConfigDefinition(
key="SLOW_MS",
+ services=("webapp", "bot", "webserver"),
default="0",
description="סף לוגינג בקשות איטיות (מילישניות)",
category="performance",
),
"COLLECTIONS_API_ITEMS_SLOW_MS": ConfigDefinition(
key="COLLECTIONS_API_ITEMS_SLOW_MS",
+ services=("webapp", "bot", "webserver"),
default="",
description="סף איטיות ל-Collections API",
category="performance",
),
"ANOMALY_IGNORE_ENDPOINTS": ConfigDefinition(
key="ANOMALY_IGNORE_ENDPOINTS",
+ services=("webapp", "bot", "mcp", "webserver"),
default="",
description="נקודות קצה להתעלמות בזיהוי אנומליות",
category="performance",
),
"DRIVE_MENU_V2": ConfigDefinition(
key="DRIVE_MENU_V2",
+ services=("webapp", "bot", "mcp", "webserver"),
default="true",
description="הפעלת תפריט Drive v2",
category="features",
),
"RECYCLE_TTL_DAYS": ConfigDefinition(
key="RECYCLE_TTL_DAYS",
+ services=("webapp", "bot", "mcp", "webserver"),
default="7",
description="ימים לשמירת פריטים בסל המיחזור",
category="limits",
),
"PUBLIC_SHARE_TTL_DAYS": ConfigDefinition(
key="PUBLIC_SHARE_TTL_DAYS",
+ services=("webapp", "bot", "webserver"),
default="7",
description="ימים לתוקף שיתוף ציבורי",
category="limits",
),
"PERSISTENT_LOGIN_DAYS": ConfigDefinition(
key="PERSISTENT_LOGIN_DAYS",
+ services=("webapp", "bot", "webserver"),
default="180",
description="ימים לשמירת התחברות קבועה",
category="limits",
),
"SEARCH_PAGE_SIZE": ConfigDefinition(
key="SEARCH_PAGE_SIZE",
+ services=("webapp", "bot", "mcp", "webserver"),
default="200",
description="גודל עמוד חיפוש",
category="limits",
),
"UI_PAGE_SIZE": ConfigDefinition(
key="UI_PAGE_SIZE",
+ services=("webapp", "bot", "mcp", "webserver"),
default="10",
description="גודל עמוד בממשק משתמש",
category="limits",
),
"UPTIME_PROVIDER": ConfigDefinition(
key="UPTIME_PROVIDER",
+ services=("webapp", "bot", "webserver"),
default="",
description="ספק Uptime (betteruptime וכו')",
category="uptime",
),
"UPTIME_API_KEY": ConfigDefinition(
key="UPTIME_API_KEY",
+ services=("webapp", "bot", "webserver"),
default="",
description="מפתח API ל-Uptime",
category="uptime",
@@ -1147,120 +1322,140 @@ class ConfigService:
),
"UPTIME_MONITOR_ID": ConfigDefinition(
key="UPTIME_MONITOR_ID",
+ services=("webapp", "bot", "webserver"),
default="",
description="מזהה Monitor ב-Uptime",
category="uptime",
),
"UPTIME_STATUS_URL": ConfigDefinition(
key="UPTIME_STATUS_URL",
+ services=("webapp", "bot", "webserver"),
default="",
description="כתובת דף סטטוס Uptime",
category="uptime",
),
"UPTIME_WIDGET_SCRIPT_URL": ConfigDefinition(
key="UPTIME_WIDGET_SCRIPT_URL",
+ services=("webapp", "bot", "webserver"),
default="https://uptime.betterstack.com/widgets/announcement.js",
description="כתובת סקריפט Widget",
category="uptime",
),
"UPTIME_WIDGET_ID": ConfigDefinition(
key="UPTIME_WIDGET_ID",
+ services=("webapp", "bot", "webserver"),
default="",
description="מזהה Widget ב-Uptime",
category="uptime",
),
"UPTIME_CACHE_TTL_SECONDS": ConfigDefinition(
key="UPTIME_CACHE_TTL_SECONDS",
+ services=("webapp", "bot", "webserver"),
default="120",
description="TTL לקאש Uptime (שניות)",
category="uptime",
),
"ENVIRONMENT": ConfigDefinition(
key="ENVIRONMENT",
+ services=("webapp", "bot", "mcp", "webserver"),
default="production",
description="שם הסביבה (production/staging/dev)",
category="environment",
),
"ENV": ConfigDefinition(
key="ENV",
+ services=("webapp", "bot", "mcp", "webserver"),
default="production",
description="שם סביבה מקוצר",
category="environment",
),
"DEPLOYMENT_TYPE": ConfigDefinition(
key="DEPLOYMENT_TYPE",
+ services=("webapp", "bot", "webserver"),
default="render",
description="סוג הפריסה (render/heroku/k8s)",
category="environment",
),
"HOSTNAME": ConfigDefinition(
key="HOSTNAME",
+ services=("webapp", "bot", "webserver"),
default="",
description="שם ה-Host הנוכחי",
category="environment",
),
"APP_VERSION": ConfigDefinition(
key="APP_VERSION",
+ services=("webapp", "bot", "webserver"),
default="",
description="גרסת האפליקציה",
category="versioning",
),
"ASSET_VERSION": ConfigDefinition(
key="ASSET_VERSION",
+ services=("webapp", "bot", "webserver"),
default="",
description="גרסת הנכסים הסטטיים",
category="versioning",
),
"GIT_COMMIT": ConfigDefinition(
key="GIT_COMMIT",
+ services=("webapp", "bot", "webserver"),
default="",
description="Git Commit Hash",
category="versioning",
),
"FA_SRI_HASH": ConfigDefinition(
key="FA_SRI_HASH",
+ services=("webapp", "bot", "webserver"),
default="",
description="Hash SRI של FontAwesome",
category="versioning",
),
"MAINTENANCE_MODE": ConfigDefinition(
key="MAINTENANCE_MODE",
+ services=("webapp", "bot", "mcp", "webserver"),
default="false",
description="מצב תחזוקה פעיל",
category="maintenance",
),
"MAINTENANCE_MESSAGE": ConfigDefinition(
key="MAINTENANCE_MESSAGE",
+ services=("webapp", "bot", "mcp", "webserver"),
default="🚀 אנחנו מעלים עדכון חדש!\nהבוט יחזור לפעול ממש בקרוב",
description="הודעת תחזוקה למשתמשים",
category="maintenance",
),
"MAINTENANCE_AUTO_WARMUP_SECS": ConfigDefinition(
key="MAINTENANCE_AUTO_WARMUP_SECS",
+ services=("webapp", "bot", "mcp", "webserver"),
default="30",
description="שניות חימום אחרי תחזוקה",
category="maintenance",
),
"MAINTENANCE_WARMUP_GRACE_SECS": ConfigDefinition(
key="MAINTENANCE_WARMUP_GRACE_SECS",
+ services=("webapp", "bot", "mcp", "webserver"),
default="0.75",
description="שניות גרייס נוספות לחימום",
category="maintenance",
),
"BACKUPS_STORAGE": ConfigDefinition(
key="BACKUPS_STORAGE",
+ services=("webapp", "bot", "webserver"),
default="mongo",
description="בחירת מנגנון גיבוי: mongo (GridFS) או fs (מערכת קבצים מקומית)",
category="backups",
),
"BACKUPS_DIR": ConfigDefinition(
key="BACKUPS_DIR",
+ services=("webapp", "bot", "webserver"),
default="/app/backups",
description="נתיב גיבויים בלוקאל (אם BACKUPS_STORAGE=fs)",
category="backups",
),
"ENCRYPTION_KEY": ConfigDefinition(
key="ENCRYPTION_KEY",
+ services=("webapp", "bot"),
default="",
description="מפתח הצפנה לנתונים רגישים (32 בתים)",
category="security",
@@ -1268,42 +1463,49 @@ class ConfigService:
),
"PYTEST": ConfigDefinition(
key="PYTEST",
+ services=("webapp", "bot", "webserver"),
default="",
description="דגל pytest פעיל",
category="testing",
),
"DISABLE_DB": ConfigDefinition(
key="DISABLE_DB",
+ services=("webapp", "bot", "mcp", "webserver"),
default="",
description="השבתת DB בטסטים",
category="testing",
),
"HIGHLIGHT_THEME": ConfigDefinition(
key="HIGHLIGHT_THEME",
+ services=("webapp", "bot", "mcp", "webserver"),
default="github-dark",
description="ערכת נושא להדגשת תחביר",
category="display",
),
"DEFAULT_UI_THEME": ConfigDefinition(
key="DEFAULT_UI_THEME",
+ services=("webapp", "bot", "webserver"),
default="classic",
description="ערכת ברירת מחדל ל-UI ב-WebApp. תומך בערכת builtin או בערכה ציבורית בפורמט shared:| Key | +שירות | +Default Value | +תיאור | +
|---|---|---|---|
|
+
+ {{ row.category }}
+ {{ row.key }}
+
+ |
+ + {{ row.service }} + {% if row.also_webapp %} + גם Webapp + {% endif %} + | +
+
+ {% if row.default_value %}
+ {{ row.default_value }}
+ {% if row.is_sensitive %}
+
+ {% endif %}
+ {% else %}
+ (ללא)
+ {% endif %}
+
+ |
+
+ {{ row.description or '—' }}
+ |
+
אין משתנים של שירותים אחרים
+