Wizzo Market, סקירה

איך אתר WIZZO CMS מתחבר ל-Wizzo Market, מפתח האתר, הפרוקסי, קובץ market_settings.json, ה-connector ואיך כלי מרקט מופיעים בפאנל

⏱ 8 דק' קריאה 1474 מילים ערוך דף זה ב-GitHub

Wizzo Market הוא החנות של Wizzo: משם מפעילים לאתר שירותים (WizzoAI, POOLSE, POOSH, KAMA, SEOK, אולג ועוד), ושם נמצאים החיוב והזהות. הליבה של האתר לא מכילה את המוצרים עצמם. היא מחזיקה מפתח אחד שמזהה את האתר מול המרקט, מושכת משם קובץ הגדרות, וחושפת דלת אחת שדרכה המרקט פונה אל האתר. בדף הזה תבינו את שלושת המרכיבים האלה ואת המעגל ביניהם.

המודל בחמישה צעדים#

  1. מפתח המרקט של האתר. הערך wizzo_market_api_key בטבלת CRM_params. הוא נשמר דרך {admin}/wizzo_market (המתודה set_api_key), והוא הסוד שמזהה את האתר מול המרקט. אותו מפתח משמש גם לאימות קריאות נכנסות מהמרקט.
  2. הפעלת שירות. הפאנל admin/wizzo_market טוען אפליקציית Vue של המרקט (assets/wizzo_market/index.html) שמדברת עם המרקט דרך פרוקסי בצד השרת: system/wizzo_market_api/<action>.
  3. סנכרון הגדרות. אחרי כל פעולה דרך הפרוקסי, ובכל קריאת sync מהמרקט, הליבה מושכת GET https://market.wizzo.media/api/get_settings וכותבת את system/market_settings.json.
  4. קריאת מפתחות. כל ספרייה בליבה שצריכה מפתח של שירות שואלת את market_service. מפתחות השירותים נקראים מהקובץ ולא מבסיס הנתונים.
  5. פאנלים. מאז ליבה 5.0.62 אין בליבה פאנלי מוצר. התפריט מציג "כלים" שהמרקט מגדיר, וכל כלי נפתח כ-{admin}/wizzo_market?tool=<tool>.
מנהל בפאנל  --(wizzo_market_api, פרוקסי)-->  market.wizzo.media
                                                   |
                       get_settings (אחרי כל פעולה / sync)
                                                   v
                              system/market_settings.json
                                                   |
              market_service::api_key("kama") וחברותיה, בתוך ספריות הליבה

מבנה system/market_settings.json#

הקובץ הוא שדה data מתשובת get_settings. הליבה קוראת ממנו את השדות הבאים בלבד:

{
  "services": [
    {
      "service": "poosh",
      "additional_data": {
        "api_key": "<public key of the site in that product>",
        "secret_key": "<secret, used by SEOK and Oleg>",
        "dashboard_url": "https://seok.wizzo.market",
        "settings": { "enabled": true, "lang": "he" }
      },
      "panels": [
        { "file": "services_poosh_stats", "name": "Poosh", "tool": "poosh", "perms": ["stats"] }
      ],
      "scripts": [ "<script src=\"https://example.invalid/sdk.js\"></script>" ]
    }
  ]
}
שדהמי קוראהערות
services[].servicemarket_service::settings, is_active, get_all_api_keysשם השירות. ראו את רשימת השמות ב-קטלוג השירותים
additional_data.api_key / secret_keyapi_key(), secret_key() ובספריות כמו wizzo_ai, SeoHosted, OlegHosted, poolse_clientמפתחות האתר בתוך המוצר
additional_data.dashboard_urlSeoHosted::base, OlegHosted::baseמיקום המנוע. חייב להיות https://, אחרת חוזרים לכתובת ברירת המחדל
additional_data.settingsmarket_service::enable_configרק לשירות enable: נכתב ל-system/accessibility_config.json
panels[]settings_tools, panel_by_tool, panel_tooltool ריק נגזר מהטבלה LEGACY_TOOLS לפי file
scripts[]generate_system_tagsנכתב ל-system/system_tags.json ומוזרק ל-body_end של כל עמוד
אזהרה
market_settings.json מכיל api_key ו-secret_key של כל שירות, והוא יושב בתוך ה-webroot. ההגנה היחידה היא system/.htaccess ש-market_service::protect_settings_dir() שותל אוטומטית (כלל FilesMatch), והוא עובד ב-Apache וב-LiteSpeed בלבד. על nginx וכל שרת אחר חובה להוסיף חסימה ידנית של /system/market_settings.json ושל /system/system_tags.json בהגדרות השרת. אחרי כל התקנה, בדקו שגישה ישירה לקובץ מחזירה 403. ראו הקשחה.

market_service: הממשק לקובץ#

הקובץ: libraries/market_service.php. כל המתודות סטטיות, והקובץ נקרא פעם אחת לבקשה (init() טוען אותו ל-market_service::$market_settings).

מתודהערך החזרההערה
market_service::init()voidמוודא את ה-.htaccess וטוען את הקובץ. נקראת אוטומטית מכל שאר המתודות
market_service::settings($service)object או falseרשומת השירות כאובייקט (->additional_data->api_key). מקבלת גם את השם הישן של שירות בשינוי שם (poosh / wizzopoosh, kama / wizzoradar)
market_service::api_key($service)string או falseadditional_data.api_key, מחרוזת ריקה אם אין, false אם השירות לא פעיל
market_service::secret_key($service)string או falseadditional_data.secret_key
market_service::get_all_api_keys()array[service => api_key] לכל השירותים עם מפתח
market_service::is_active($service)boolהאם השירות מופיע בקובץ
market_service::settings_tools()arrayמפת הכלים שהמרקט מגדיר בתפריט ([tool => true])
market_service::panel_by_tool($tool)array או nullהפאנל שהכלי שייך לו, עם file (מפתח ההרשאה) ו-perms
market_service::panel_tool($panel)stringשם הכלי של פאנל, כולל נפילה ל-LEGACY_TOOLS
market_service::enable_config()array או nullהגדרות ווידג'ט הנגישות (שירות enable)
market_service::sync_enable_config()voidכותב את system/accessibility_config.json, נקרא בכל סנכרון
market_service::migrate_legacy_enable_settings()voidמעביר פעם אחת הגדרות נגישות ישנות מ-PARAMS למרקט
market_service::write_enable_config($cfg)voidכותב רק את המפתחות המותרים
market_service::generate_system_tags()arrayבונה את system/system_tags.json מכל scripts[]
market_service::get_system_tags()arrayקורא את הקובץ הזה
market_service::protect_settings_dir($force = false)voidשותל את בלוק ה-.htaccess של /system/, ובלוקים שהאתר הוסיף לידו נשמרים
market_service::guard_block()stringהטקסט של הבלוק
$this->load->library("market_service");

if (market_service::is_active("spamguard"))
{
    $key = market_service::api_key("spamguard"); // never echo it into a page
}
שימו לב
market_service::secret_key() קורא additional_data->secret_key ישירות, ולכן קריאה לשירות שרשום בלי שדה כזה נותנת אזהרת PHP. בדקו קודם is_active(). ו-api_key() של שירות לא פעיל מחזירה false, לא מחרוזת ריקה, אז השוו עם ! ולא עם === "".

הפרוקסי system/wizzo_market_api/<action>#

הפרוקסי הוא בקר ליבה (controllers/wizzo_market_api.php) שמעביר בקשות מהפאנל אל https://market.wizzo.media/api/<action>, ובכך שומר על המפתח בצד השרת. הוא דורש ADMIN::is_admin(), והבקשה נשלחת בכותרות הבאות:

כותרתתוכן
Authorizationמפתח המרקט של האתר (wizzo_market_api_key)
X-Core-Versionהתוכן של system/core/version.txt. לפי הגרסה המרקט בוחר שמות שירות חדשים או ישנים
X-Cms-Admin<id>|<base64url של השם> של המנהל המחובר, כדי שהמרקט יפתח את המוצר בשמו
X-Cms-Permsמה הקבוצה של המנהל מורשית לעשות בכלי הנדרש: *, או רשימת מפתחות הרשאה
X-Cms-Tokenרק בפעולות mcp...: אסימון שמאשר שמנהל מחובר באמת מבקש (ראו MCP חיצוני)

אחרי כל קריאה הפרוקסי מריץ get_settings(), כלומר סנכרון מלא של market_settings.json, ואז system_tags.json, accessibility_config.json וניקוי מטמון העמוד הראשי. סנכרון שנכשל (שגיאת רשת, קוד HTTP שאינו 2xx, או תשובה בלי services) לא דורס את הקובץ הקיים: נרשמת שורה ב-error_log והקובץ הישן נשאר. הכתיבה אטומית: קובץ זמני ואז rename.

הדלת ההפוכה: sync#

כשהמרקט רוצה שהאתר ימשוך הגדרות מיד (חשבון חובר, מפתח הוחלף) הוא קורא ל-system/wizzo_market_api/sync. אין כאן סשן מנהל: האימות הוא WIZZO_KEY::matches(PARAMS::get("wizzo_market_api_key")), ובכישלון חוזר 403 עם {"ok":false}.

שימו לב

נתיב הפעולה בפרוקסי נגזר מ-REQUEST_URI ומצורף לכתובת המרקט כמו שהוא, וכל מנהל מחובר מגיע אליו, לא רק מפתח (developer). בנוסף, admin/wizzo_market::set_api_key כותב את מפתח המרקט בלי לוודא שהמנהל הוא מפתח ובלי אסימון CSRF. בקוד שלכם אל תבנו על הפרוקסי כדלת כללית: קראו לשירותים דרך הספריות. זה ממצאים ידועים בביקורת (SVC-19) ופתרונם בתהליך.

WIZZO_KEY: קריאת המפתח מבקשה נכנסת#

כל דלת בליבה שנקראת מצד שרת של Wizzo (ה-connector, sync, ריצות SEOK ואולג, ריצות הסוכנים) קוראת את המפתח דרך collections/WIZZO_KEY.php, ולא מ-$_SERVER["HTTP_AUTHORIZATION"]. הסיבה: מארחי cgi-fcgi לא מעבירים את הכותרת Authorization ל-PHP.

מתודההערה
WIZZO_KEY::incoming($body = null): stringמחפשת בסדר: HTTP_AUTHORIZATION, REDIRECT_HTTP_AUTHORIZATION, apache_request_headers(), הכותרת X-Wizzo-Key, ובגוף ה-POST אחד מהשדות _key, api_key, _token. מסירה קידומת Bearer
WIZZO_KEY::matches($expected, $body = null): boolהשוואה ב-hash_equals. ציפייה ריקה לעולם לא תואמת, כך שאתר בלי מפתח לא נפתח בבקשה בלי מפתח
if (!WIZZO_KEY::matches(PARAMS::get("wizzo_market_api_key"), $body))
{
    http_response_code(401);
    die(json_encode(["ok" => false, "error" => "unauthorized"]));
}

מפתח לא מתקבל ב-query string, כדי שלא יישמר בלוגי הגישה וב-Referer.

system/connector: הדלת של המרקט לתוך האתר#

controllers/connector.php הוא הדלת היחידה שדרכה המרקט (ובאמצעותו כל מוצר) פונה לאתר. האימות הוא WIZZO_KEY::matches מול מפתח המרקט, והתשובה תמיד JSON עם ok.

פעולהתוצאה
GET /system/connector (או ?do=discovery)גרסאות, זהות האתר, היכולות והנתיבים שלהן
POST /system/connector/mcp_connect (גוף: rotate אופציונלי)רישום דלת ה-MCP של האתר ב-gateway. נכשל עם 502
POST /system/connector/agents_readyרישום האתר בבקרת הסוכנים. 409 אם WizzoAI לא פעיל
POST /system/connector/seo_readyהעברת ה-SEO של האתר למנוע המאוחסן (רישום, ייצוא היסטוריה, החלפת מצב)
POST /system/connector/oleg_readyהצגת האתר למנוע של אולג. 409 אם אולג לא פעיל
GET /system/connector/statusמצב חי של כל יכולת

פירוט ה-MCP ב-MCP חיצוני, והסוכנים ב-סוכני AI.

כלי מרקט בפאנל: איך זה מופיע#

מוצר חדש לא דורש קוד בליבה:

  1. בצד המרקט מוסיפים לשירות רשומת panels[] עם file, name, tool ו-perms. השדה file הוא מפתח ההרשאה שקבוצות מנהלים קיבלו (ADMIN::has_perms($key, $file)).
  2. באתר, לחיצה על סנכרון (או כל פעולה בפאנל) מושכת את ה-JSON. settings_tools() מונע כפילות עם שורות ישנות של אותו פאנל בטבלת adminPanel_panels.
  3. הכלי נפתח כ-{admin}/wizzo_market?tool=<tool>. הפאנל מעביר ל-Vue של המרקט את route: '/tool/<tool>' ואת perms: all אם לקבוצה יש show_all על הפאנל, אחרת limited.
  4. אם הכלי צריך נקודת קצה בצד האתר (העלאת קבצים, webhook), כותבים בקר או קובץ admin/<svc>_media.php עם static $skip_permission_check = true, ובתוכו בודקים ADMIN::is_admin() ו-market_service::is_active("<svc>"). בלי שתי הבדיקות האלה הדלת פתוחה.
הערה

רשומות adminPanel_panels של פאנלים שנמחקו מהליבה (למשל ai_dashboard, seo_advisor, services_*) עדיין עשויות להיות בבסיס הנתונים של אתרים ותיקים. הטבלה market_service::LEGACY_TOOLS ממפה אותן לכלי המרקט המתאים.

סקריפטים שהמרקט מזריק#

שירותים עם scripts[] (ווידג'ט נגישות, ווידג'ט צ'אט, מדידה) מוזרקים לסוף ה-body של כל עמוד באתר דרך system/system_tags.json.

שימו לב

אין באתר רשימת דומיינים מותרים, אין SRI, ואין מסך שמראה למנהל מה מוזרק. כלומר המרקט הוא ספק עם גישת סקריפט מלאה לדפי האתר. בדקו מדי פעם את התוכן של system/system_tags.json (ראו SVC-20 בביקורת).

ראו גם#

מצאתם טעות או חוסר? תקנו את הדף או פתחו Issue בריפו. התיעוד נכתב מתוך הקוד של ליבה 5.0.115.