מפרט בנייה — SSoT Hub

מסמך זה הוא מפרט הביצוע המלא לאפליקציית ה-Hub. צור אפליקציית Base44 חדשה בשם "SSoT Hub", העתק את ההנחיות, ובצע שם את Phases A-E. בסיום חזור לכאן ל-Phase E כדי לחבר את שתי המערכות.

מבוא — מה בונים
אפליקציית Base44 נפרדת בשם "SSoT Hub" שמשמשת מקור אמת מרכזי (BaaS) לכל מערכת-לקוח.
ה-Hub מספק: זהות גלובלית למשתמשים, אימות גישה לפי ארגונים ופרויקטים, ניהול לקוחות (ClientApps) כולל יצירת Secret/API Key לכל לקוח, וקליטת אירועי סנכרון דו-כיווני.
המערכת הנוכחית (Client) כבר כוללת את פונקציית ssotClient שצורכת את ה-API הזה.
Phase A — מודל הנתונים (7 ישויות)
HubOrganization — ארגון-על: name, slug (ייחודי), owner_email, status (active/suspended/archived).
HubProject — פרויקט: name, organization_id, description, status (active/on_hold/completed/archived).
HubUser — זהות גלובלית: email (ייחודי), full_name, status (active/invited/suspended), metadata (object).
HubMembership — מיפוי: user_email, organization_id, project_id (אופציונלי), role (owner/admin/member/viewer), status.
ClientApp — מערכת-לקוח: app_id, name, organization_id, status (active/suspended), description.
ClientApiKey — מפתח API: client_app_id, key_hash, key_prefix, scopes (array), revoked (bool), last_used_at.
HubEventLog — יומן אירועים: direction (inbound/outbound), event_type, source_app_id, payload (object), status, processed_at.
Phase B — מנגנון API Keys לכל לקוח
hashing — חישוב SHA-256 של המפתח באמצעות Web Crypto (crypto.subtle.digest). שומרים רק את ה-hash + prefix (8 תווים ראשונים לזיהוי).
issueClientApiKey — backend function (admin בלבד): מקבל client_app_id, מייצר מפתח אקראי (crypto.getRandomValues), שומר ClientApiKey עם key_hash, ומחזיר את המפתח הגולמי פעם אחת בלבד.
verifyApiKey — מקבל api_key נכנס, מחשב hash, משווה מול ClientApiKey הפעיל (revoked=false), מעדכן last_used_at, מחזיר את ה-ClientApp המשויך.
revokeClientApiKey — admin בלבד: מסמן revoked=true.
Phase C — API ליבה (3 פונקציות ציבוריות)
כל פונקציה ציבורית קוראת תחילה ל-verifyApiKey עם api_key מה-header לפני כל פעולה.
authenticate — payload: { api_key, user_email }. מאמת את ה-ClientApp ומחזיר את ה-HubUser (יוצר אם לא קיים, במצב invited).
verifyUserAccess — payload: { user_email }. מחזיר { memberships: [{ organization_id, project_id, role }] } מתוך HubMembership הפעילים. זה ה-endpoint ש-ssotClient קורא לו ב-verify_access.
receiveEvent — payload: { event_type, entity_name, entity_id, payload, source_email }. רושם HubEventLog (direction=inbound, status=completed) ומחזיר { received: true }. זה ה-endpoint ש-ssotClient קורא לו ב-push_event.
Phase D — דפי ניהול
HubClientsAdmin — רשימת לקוחות (ClientApps): רישום לקוח חדש (registerClientApp), יצירת/סבב מפתח API (issueClientApiKey, מציג את המפתח פעם אחת ב-dialog), ביטול מפתח, סטטוס.
HubOrganizations — ניהול ארגונים ופרויקטים בהיררכיה, בעלים, סטטוס.
HubMembers — ניהול חברויות: הוספה/הזמנה, שיוך User→Org→Project→Role, השעיה.
HubEventMonitor — טבלת HubEventLog: סינון לפי direction/status/event_type, צפייה ב-payload.
Phase E — חיבור End-to-End חזרה למערכת הנוכחית
1. ב-Hub: רשום את המערכת הנוכחית כ-ClientApp (registerClientApp) ושייך אותה לארגון.
2. ב-Hub: צור API Key (issueClientApiKey) — העתק את המפתח הגולמי.
3. במערכת הנוכחית: צור רשומת Config עם key="ssot_hub" ו-value={ hub_app_id, hub_api_url, hub_api_key, enabled:true }. (hub_api_url = כתובת פונקציות ה-Hub).
4. הרץ במערכת הנוכחית את ssotClient action=status → אמור להחזיר mode=connected.
5. הרץ verify_access ו-push_event → אמורים לפנות ל-Hub אמיתי.
6. בדוק Fallback: הגדר enabled=false → המערכת חוזרת ל-local_only.
אבטחה

מפתחות API נשמרים כ-hash בלבד; המפתח הגולמי מוצג פעם אחת בעת היצירה. registerClientApp ו-issueClientApiKey מוגנים ל-admin בלבד. כל endpoint ציבורי מאמת api_key לפני כל פעולה.