SocialHub.AI

مركز المطورين · توسيع المنصة

الصفحات المضمنة — ضع أدواتك الخاصة في لوحة التحكم.

سجّل صفحتك الخاصة المستضافة على بروتوكول HTTPS، وستصبح عنصر قائمة مُصرّح به داخل لوحة تحكم SocialHub - إطار iframe معزول بملء الشاشة. Flash يمنح صفحتك رمز هوية مؤقتًا، خاليًا من معلومات التعريف الشخصية، لتتمكن من تفويض المستخدم دون الحاجة إلى تسجيل دخوله مجددًا.

وصول مبكر · خلف وحدة التوسعة المفتوحة

كيف يعمل

أربع خطوات من رابط موقعك الإلكتروني إلى عنصر في القائمة.

01

سجل صفحتك

يُضيف مسؤول مساحة العمل رابط HTTPS الخاص بك في Flash ← التكوين ← الصفحات المضمنة، مع تسمية للقائمة، وقائمة بالمصادر المسموح بها، والأدوار/المستويات التي يُمكنها فتحه.

02

انسخ سر التوقيع

يعرض Flash رمز التوقيع مرة واحدة (slotsec_…). خزّنه كسرّ خادم في تطبيقك — ستستخدمه للتحقق من هوية Flash.

03

Flash يُؤطر صفحتك

تظهر الصفحة كعنصر في قائمة الشريط الجانبي ضمن التطبيقات للمشغلين المسموح لهم؛ يؤدي فتحها إلى تحميل عنوان URL الخاص بك في إطار iframe معزول مع إضافة ?flash_context=<token>.

04

التحقق والتفويض

قراءة الرمز المميز، والتحقق منه من جانب الخادم (HS256)، وتقييد واجهة المستخدم بناءً على معرّف مساحة العمل ودور المشغل الذي يحمله.

رمز الهوية

مؤقت، لا يحتوي على معلومات شخصية، موقّع برمزك السري.

في كل عملية عرض، يُضيف Flash الرمز ?flash_context=<JWT> إلى عنوان URL الخاص بك. وهو رمز JWT من نوع HS256 مُوقّع باستخدام ورمز صفحتك السري (أي أنك أنت - وليس المنصة - مصدر الثقة)، صالح لمدة أقصاها 120 ثانية، ولا يحمل أي بيانات شخصية للأعضاء - فقط مُعرّف مساحة العمل ومعرّف المشغل غير الشفاف بالإضافة إلى دوره العام، مما يُمكّنك من المصادقة.

{
  "iss": "flash",
  "aud": "nav.page",
  "slotKey": "nav.page",
  "ctx": {
    "teamId": "team_...",   // the workspace (tenant) — the tenant already owns this
    "userId": "usr_...",    // opaque operator id (NOT an email/name/phone)
    "role":   "org_admin",  // coarse role: org_owner | org_admin | marketer | store_manager
    "pageType": "nav.page",
    "locale": "en"
  },
  "jti": "…", "iat": 1730000000, "nbf": 1730000000,
  "exp": 1730000110          // <= 120s after iat
}

تحقق من ذلك

ثق بالرمز المميز، لا بالطلب.

تحقق من التوقيع، والجهة المُصدرة (flash)، والجمهور (nav.page)، وتاريخ انتهاء الصلاحية على خادمك. ثم قم بالمصادقة على مُعرّف مساحة العمل والدور المُرتبط به - وليس على ملفات تعريف الارتباط (لا يوجد مصدر صالح للإطار المُضمّن؛ انظر أدناه).

// Node (jose). The secret is your page's signing secret, shown ONCE at registration.
import { jwtVerify } from "jose";

const SECRET = new TextEncoder().encode(process.env.FLASH_EMBED_SECRET); // slotsec_…

export async function verifyFlashContext(token) {
  const { payload } = await jwtVerify(token, SECRET, {
    algorithms: ["HS256"],
    issuer: "flash",
    audience: "nav.page",
    requiredClaims: ["exp", "jti"],
    maxTokenAge: "120s",
  });
  // payload.ctx = { teamId, userId, role, pageType, locale }
  return payload.ctx; // → authorize the operator with this
}
// In your page: read ?flash_context=… , verify it server-side, then authorize.
const token = new URLSearchParams(location.search).get("flash_context");
// POST it to YOUR backend, verify with the snippet above, and gate the UI on
// ctx.teamId + ctx.role. Never trust it unverified, and never rely on cookies —
// the iframe is a null origin (see "Framing" below).

التأطير والأمان

ما يجب أن تفعله صفحتك.

السماح بالإطار

يجب أن تسمح صفحتك بعرضها داخل إطار فلاش.socialhub.ai — لا تُرسل X-Frame-Options: DENY؛ اضبط Content-Security-Policy: frame-ancestors https://flash.socialhub.ai.

افترض مصدر null

Flash يُعزل الإطار المضمن (iframe) مع السماح بالبرامج النصية ومنع السماح بنفس المصدر، بحيث تعمل صفحتك على مصدر null: بدون ملفات تعريف ارتباط Flash أو تخزين أو DOM رئيسي. يتم المصادقة باستخدام الرمز المميز، وليس ملفات تعريف الارتباط. ...

تم السماح بـ postMessage

لا يقبل المُضيف إلا طلبات postMessage من المصادر التي سجلتها، ومن نافذة iframe الخاصة بك فقط. حافظ على قائمة المصادر المسموح بها مُختصرة، واجعلها مُخصصة لبروتوكول HTTPS فقط.

قم بتغيير سر التوقيع من المدير إذا تم الكشف عنه (تتوقف الرموز القديمة عن التحقق فورًا). الصفحات المضمنة هي وصول مبكر، وهي معطلة افتراضيًا لكل مساحة عمل، ويخضع تفعيلها للإنتاج لمراجعة أمان تضمين الطرف الثالث من قبل SocialHub.

مطابقة واجهة الإدارة

اجعله يبدو أصليًا، وليس مُضافًا بشكل مُتعمّد.

يتم عرض صفحتك داخل لوحة تحكم SocialHub، لذا يجب أن تبدو متناسقة معها. لوحة التحكم عبارة عن قالب بسيط وخفيف مبني على عدد قليل من الرموز — قم بعكسها. Flash يُغلّف صفحتك بالفعل بنافذة مُعنونة تحمل شارة "طرف ثالث"، لذا لا داعي لإضافة شريط علوي أو جانبي أو إطار ملء الشاشة؛ فقط اعرض المحتوى.

/* Starter tokens that match the SocialHub admin (flat, light, single-accent).
   Drop into your embedded page so it feels native, not bolted on. */
:root {
  /* type — the admin's three families */
  --font-sans:    "Inter", -apple-system, "Segoe UI", Roboto, sans-serif;
  --font-display: "Plus Jakarta Sans", "Inter", system-ui, sans-serif;
  --font-mono:    "JetBrains Mono", ui-monospace, monospace;

  /* surfaces — light theme only (the admin has no dark mode) */
  --bg:      #fafbfc;  /* page background */
  --surface: #ffffff;  /* cards / panels  */
  --subtle:  #f7fafc;  /* muted fills     */
  --border:  #e2e8f0;  /* hairlines       */

  /* text */
  --text:   #121c3d;   /* primary (brand navy) */
  --text-2: #4a5568;   /* secondary            */
  --muted:  #64748b;   /* muted                */

  /* accent — the admin's primary action colour */
  --accent:      #2ecc87;
  --accent-dark: #25b678;

  /* radius + 4px spacing scale + elevation */
  --radius: 8px; --radius-lg: 12px;
  --space-2: 8px; --space-3: 12px; --space-4: 16px; --space-6: 24px;
  --shadow-sm: 0 1px 3px rgba(0,0,0,.06), 0 1px 2px rgba(0,0,0,.04);
}

body { margin: 0; background: var(--bg); color: var(--text);
       font: 14px/1.5 var(--font-sans); }
h1, h2, h3 { font-family: var(--font-display); color: var(--text); }
.card { background: var(--surface); border: 1px solid var(--border);
        border-radius: var(--radius-lg); box-shadow: var(--shadow-sm);
        padding: var(--space-4); }
.btn  { background: var(--accent); color: #fff; border: 0; font-weight: 600;
        border-radius: var(--radius); padding: 8px 14px; cursor: pointer; }
.btn:hover { background: var(--accent-dark); }
code  { font-family: var(--font-mono); color: var(--text-2); }

لا يوجد طلاء كروم خاص بك

Flash يوفر إطار النافذة والشارة. استغنِ عن شريط التنقل/الشريط الجانبي/الرأس العام لتطبيقك، واعرض الأداة فقط.

خفيف، مسطح، بلمسة واحدة

الوضع الفاتح فقط (لا يتوفر الوضع الداكن لدى المسؤول): بطاقات بيضاء على صفحة #fafbfc، وحدود رفيعة #e2e8f0، ولمسة خضراء واحدة (#2ecc87).

ملء العرض، وتدفق الارتفاع

استخدم العرض الكامل للصفحة ودع المحتوى يتدفق بحرية؛ يقوم النظام المُضيف بتحديد حجم الإطار (تقريبًا ارتفاع نافذة العرض الكامل) والتمرير - لا تُجبر نفسك على استخدام 100vh أو تذييلات ثابتة.

التدهور التدريجي

يتم استخدام الخطوط الافتراضية (Inter / Plus Jakarta Sans / JetBrains Mono) كخطوط النظام إذا لم تقم بتثبيتها بنفسك؛ مع مراعاة تفضيلات تقليل الحركة ونظام الألوان الفاتح.

تتوافق هذه القيم مع تصميم لوحة التحكم الحالية؛ اعتبرها نقطة انطلاق، وحافظ على علامتك التجارية في المواضع المهمة. الهدف هو صفحة متكاملة مع SocialHub، وليست نافذة على منتج آخر.

مثال كامل

صفحة مضمنة تعمل بكفاءة في حوالي 30 سطرًا.

خادم Express بسيط يمكنك تشغيله وتسجيله كما هو: يسمح لـ Flash بتأطيره، والتحقق من رمز الهوية، والمصادقة بناءً على دور المشغل، وعرض أداة مخصصة لمساحة العمل. انسخ سر التوقيع من المدير إلى FLASH_EMBED_SECRET وشغّله.

// server.js — a complete, minimal SocialHub Embedded Page (Express + jose).
// Register https://<host>/embed in Flash → Settings → Embedded Pages, allow the
// origin https://<host>, pick roles/tier, then copy the signing secret into env.
import express from "express";
import { jwtVerify } from "jose";

const app = express();
const SECRET = new TextEncoder().encode(process.env.FLASH_EMBED_SECRET); // slotsec_…
const FLASH = "https://flash.socialhub.ai";

app.get("/embed", async (req, res) => {
  // 1. Let ONLY Flash frame this page.
  res.setHeader("Content-Security-Policy", "frame-ancestors " + FLASH);

  // 2. Read + verify the identity token Flash appended (?flash_context=…).
  let ctx;
  try {
    const { payload } = await jwtVerify(String(req.query.flash_context || ""), SECRET, {
      algorithms: ["HS256"], issuer: "flash", audience: "nav.page",
      requiredClaims: ["exp", "jti"], maxTokenAge: "120s",
    });
    ctx = payload.ctx; // { teamId, userId, role, pageType, locale }
  } catch {
    return res.status(401).send("<p>Unauthorized — open this from inside SocialHub.</p>");
  }

  // 3. Authorize on the VERIFIED role (never on the raw request).
  const canEdit = ctx.role === "org_owner" || ctx.role === "org_admin";

  // 4. Render your tool, scoped to ctx.teamId. (Values are verified + opaque;
  //    still escape anything user-supplied you add.)
  res.send(
    '<!doctype html><meta charset="utf-8"><body style="font:14px system-ui;padding:24px">' +
    '<h1>Returns portal</h1>' +
    '<p>Workspace <code>' + ctx.teamId + '</code> · operator <code>' + ctx.userId +
    '</code> · role <b>' + ctx.role + '</b></p>' +
    (canEdit ? '<button>Process a return</button>' : '<p>Read-only for your role.</p>') +
    '</body>'
  );
});

app.listen(3000, () => console.log("Embedded page on :3000/embed"));

1 · السماح بالإطار

frame-ancestors https://flash.socialhub.ai — لذلك لا يمكن إلا لمسؤول SocialHub تضمينها في إطار iframe.

2 · التحقق من الرمز المميز

اقرأ ?flash_context، وتحقق من HS256 + فلاش المُصدر + صفحة تنقل الجمهور + مدة انتهاء صلاحية ≤120 ثانية؛ وإلا فارفض.

3 · المصادقة

ميزات البوابة في دور "تم التحقق منه" (org_owner / org_admin / …), ليس بناءً على الطلب الخام.

4 · عرض، نطاق

اعرض أداتك لـ ctx.teamId — مساحة العمل التي تم فتحها فيها.

ثم في Flash → الإعدادات → الصفحات المضمنة: أضف عنوان URL (مثلاً https://tools.yourapp.com/embed)، وحدد نفس المصدر، واختر الأدوار والمستوى المسموح لهما بفتحه، ثم الصق رمز التوقيع لمرة واحدة في بيئة خادمك. سيظهر الآن ضمن التطبيقات للمشغلين الذين سمحت لهم بالوصول.