SocialHub.AI

ศูนย์พัฒนา · ขยายแพลตฟอร์ม

เพจแบบฝัง — ใส่เครื่องมือของคุณเองในผู้ดูแลระบบ

ลงทะเบียนเพจที่โฮสต์ด้วย https ของคุณเอง และเพจนั้นจะกลายเป็นรายการเมนูที่ได้รับอนุญาตภายในผู้ดูแลระบบ SocialHub ซึ่งเป็น iframe แบบแซนด์บ็อกซ์แบบเต็มหน้า Flash มอบ โทเค็นข้อมูลระบุตัวตนที่ไม่มี PII ที่มีอายุสั้นให้กับเพจของคุณ เพื่อให้คุณสามารถให้สิทธิ์ผู้ดำเนินการได้โดยไม่ต้องเข้าสู่ระบบอีกครั้ง

การเข้าถึงก่อนใคร · ด้านหลังโมดูลการขยายแบบเปิด

วิธีการทำงาน

สี่ขั้นตอนจาก URL ของคุณไปยังรายการเมนู

01

ลงทะเบียนเพจของคุณ

ผู้ดูแลระบบพื้นที่ทำงานเพิ่ม https URL ของคุณใน Flash → กำหนดค่า → เพจที่ฝัง พร้อมด้วยป้ายกำกับเมนู รายการต้นทางที่อนุญาต และบทบาท/ระดับที่สามารถเปิดได้

02

คัดลอกความลับในการลงนาม

Flash แสดงความลับในการเซ็นชื่อหนึ่งครั้ง (slotsec_…) เก็บไว้เป็นความลับของเซิร์ฟเวอร์ในแอปของคุณ — คุณยืนยันโทเค็นประจำตัวของ Flash ด้วยโทเค็นนั้น

03

Flash จัดเฟรมเพจของคุณ

หน้านี้จะปรากฏเป็นรายการเมนูแถบด้านข้างใต้แอปสำหรับผู้ให้บริการที่ได้รับอนุญาต การเปิดจะโหลด URL ของคุณใน iframe แบบแซนด์บ็อกซ์โดยมี ?flash_context=<token> ต่อท้าย

04

ตรวจสอบ + อนุมัติ

อ่านโทเค็น ตรวจสอบฝั่งเซิร์ฟเวอร์ (HS256) และเกต UI ของคุณบนรหัสพื้นที่ทำงาน + บทบาทตัวดำเนินการที่โทเค็นมีอยู่

โทเค็นประจำตัว

อายุสั้น ปลอด PII ลงนามด้วยความลับของคุณ

ในทุก ๆ การเรนเดอร์ Flash จะเพิ่ม ?flash_context=<JWT> ต่อท้าย URL ของคุณ เป็น HS256 JWT ที่ลงนามด้วย ข้อมูลลับของเพจของคุณ (ดังนั้นคุณ (ไม่ใช่แพลตฟอร์ม) เป็นผู้ยึดถือ) ใช้งานได้สูงสุด 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) และการหมดอายุบนเซิร์ฟเวอร์ของคุณ จากนั้นให้อนุญาตรหัสพื้นที่ทำงาน + บทบาทที่มี โดยห้ามใช้คุกกี้เลย (iframe ไม่มีแหล่งที่มาที่ใช้งานได้ ดูด้านล่าง)

// 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).

กรอบและความปลอดภัย

สิ่งที่เพจของคุณต้องทำ

ขออนุญาตลงเฟรม.

หน้าเว็บของคุณต้องอนุญาตให้เฟรมโดย Flash socialhub.ai — อย่าส่ง X-Frame-Options: DENY; ตั้งค่า Content-Security-Policy: frame-ancestors https://flash.socialhub.ai.

สมมติว่าต้นกำเนิดเป็นโมฆะ

Flash กั้น iframe ด้วย allow-scripts และไม่มี allow-same-origin ดังนั้นหน้าเว็บของคุณจึงทำงานที่ null ต้นทาง: ไม่มี Flash คุกกี้ พื้นที่จัดเก็บ หรือ DOM หลัก ยืนยันตัวตนด้วยโทเค็น ไม่ใช่คุกกี้

postMessage อยู่ในรายการที่อนุญาต

โฮสต์ยอมรับเฉพาะ postMessage จากต้นทางที่คุณลงทะเบียน และจากหน้าต่าง iframe ของคุณเท่านั้น รักษารายการต้นทางที่อนุญาตให้แน่นหนาและเป็น https เท่านั้น

สับเปลี่ยนความลับในการลงนามจากผู้จัดการหากมีการเปิดเผย (โทเค็นเก่าจะหยุดการตรวจสอบทันที) เพจที่ฝังไว้นั้น การเข้าถึงล่วงหน้า โดยปิดไว้ตามค่าเริ่มต้นสำหรับแต่ละพื้นที่ทำงาน และการเปิดใช้งานสำหรับการผลิตนั้นอยู่ภายใต้การตรวจสอบความปลอดภัยแบบฝังโดยบุคคลที่สามของ SocialHub

เข้ากับลุคแอดมินเลย

ทำให้รู้สึกเหมือนเป็นส่วนหนึ่งของระบบ ไม่ใช่การเพิ่มเติมเข้ามาแบบฝืนๆ

เพจของคุณแสดงผล ภายใน ผู้ดูแลระบบ SocialHub ดังนั้นจึงควรมีลักษณะเหมือนเป็นส่วนหนึ่ง ผู้ดูแลระบบเป็นธีมเรียบๆ สว่างๆ เดียวที่สร้างขึ้นจากโทเค็นเพียงไม่กี่ตัว — สะท้อนธีมเหล่านั้น Flash ล้อมรอบหน้าเว็บของคุณในหน้าต่างชื่อด้วยป้าย "บุคคลที่สาม" ดังนั้นอย่าเพิ่มแถบด้านบน แถบด้านข้าง หรือ Chrome แบบเต็มหน้าของคุณเอง เพียงแค่แสดงเนื้อหา

/* 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) จะเปลี่ยนไปใช้ system-ui หากคุณไม่ได้โฮสต์แบบอักษรเหล่านั้นด้วยตนเอง honour ชอบการเคลื่อนไหวที่ลดลงและโทนสีที่ชอบ: สว่าง

ค่าเหล่านี้ติดตามธีมผู้ดูแลระบบปัจจุบัน ปฏิบัติต่อพวกเขาเป็นจุดเริ่มต้นและรักษาแบรนด์ของคุณเองในจุดที่มีความสำคัญ เป้าหมายคือหน้าที่อ่านเป็นพื้นผิวเดียวกับ 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 + ผู้ออก flash + ผู้ชม nav.page + ≤120s หมดอายุ; ปฏิเสธเป็นอย่างอื่น

3 · อนุญาต

คุณลักษณะ Gate ในบทบาท VERIFIED (org_owner / org_admin / …) ไม่เคยอยู่ในคำขอดิบ

4 · เรนเดอร์ กำหนดขอบเขต

แสดงผลเครื่องมือของคุณสำหรับ ctx.teamId — พื้นที่ทำงานที่เปิดใช้งาน

จากนั้นใน Flash → การตั้งค่า → เพจที่ฝัง: เพิ่ม URL (เช่น https://tools.yourapp.com/embed) แสดงรายการที่มาเดียวกัน เลือกบทบาท + ระดับที่อาจเปิด และวางข้อมูลลับการลงนามแบบครั้งเดียวลงในสภาพแวดล้อมของเซิร์ฟเวอร์ของคุณ ตอนนี้ปรากฏภายใต้ แอป สำหรับผู้ให้บริการที่คุณอนุญาต