Plugins › การเชื่อมต่อ
เอกสาร/Plugins/การเชื่อมต่อ

การเชื่อมต่อ (Integrations)

ตั้งค่า → การเชื่อมต่อ คือที่ที่ผู้ดูแลเว็บเชื่อมบัญชีกับผู้ให้บริการภายนอก เช่น bot ของ Telegram, ผู้ให้บริการ AI หรือเพจ Facebook plugin เป็นตัวประกาศผู้ให้บริการ ส่วนผู้ดูแลเชื่อมบัญชีได้โดยไม่ต้องแก้ environment variable

หน้านี้มีอะไร

  • หมวด ใช้กรองหน้า: โซเชียลและแชท, AI, แจ้งเตือน, อัตโนมัติ และ ตั้งในโค้ด ซึ่งแสดง adapter ของอีเมล ที่เก็บไฟล์ การค้นหา การเข้าสู่ระบบ และการชำระเงินที่ config ตั้งไว้ (อ่านอย่างเดียว)
  • การ์ดต่อผู้ให้บริการ ที่ plugin เพิ่มเข้ามา: เปิดหรือปิด เชื่อมบัญชี ทดสอบ ตั้งเป็นค่าเริ่มต้น แก้ไข หรือ ตัดการเชื่อม เมื่อปิด plugin จะใช้ไม่ได้ แต่บัญชียังเก็บไว้
  • หลายบัญชีต่อผู้ให้บริการ ตั้งชื่อแต่ละบัญชีได้ (เพจ Facebook สองเพจ, OpenAI key สองชุด) บัญชีแรกเป็นค่าเริ่มต้น
  • plugin ทางการที่ยังไม่ได้ติดตั้งแสดงคำสั่ง npm install เพราะ admin ติดตั้ง package เองไม่ได้
  • การเชื่อมต่อที่ทดสอบไม่ผ่านหรือใช้งานล่าสุดล้มเหลวจะขึ้นใน "ต้องจัดการ" บนแดชบอร์ด

admin เห็นหน้านี้ รวมถึง บทบาท ที่ได้สิทธิ์ การเชื่อมต่อ ส่วนผู้ใช้ที่ถูกจำกัดอยู่ในบางส่วนของเว็บ (tenant) จะไม่เห็น การเชื่อม แก้ไข ตัดการเชื่อม และเปิด/ปิดผู้ให้บริการ บันทึกลง audit log

Secret

ค่าที่ตั้ง secret จะถูกเข้ารหัสในฐานข้อมูล (AES-256-GCM ด้วย key ที่ได้จาก secret) และไม่ถูกส่งกลับไปที่ admin ซึ่งแสดงเป็น ••••1234 เวลาแก้การเชื่อมต่อ ถ้าไม่พิมพ์ secret ใหม่จะใช้ค่าเดิม

หลังหมุน secret การเชื่อมต่อจะอ่านด้วย previousSecrets ได้ และเข้ารหัสใหม่ด้วย secret ใหม่ตอนถูกใช้ รัน easy-cms secrets:rotate เพื่อทำทั้งหมดในครั้งเดียว แล้วค่อยลบ secret เก่าออก หน้านี้บอกว่ายังเหลือกี่รายการที่ใช้ secret เก่า

environment variable ที่ระบุใน env ใช้ก่อนค่าใน admin ถ้าตั้งครบทุกช่อง ผู้ให้บริการจะมีการเชื่อมต่อชื่อ "Environment" ที่แก้ใน admin ไม่ได้

ประกาศ integration

plugin เพิ่มของตัวเองใน integrations (เว็บประกาศเองก็ได้):

import { definePlugin } from '@easy-cms/core'
 
export const telegramPlugin = () =>
  definePlugin((config) => ({
    ...config,
    integrations: [
      ...(config.integrations ?? []),
      {
        id: 'telegram',
        label: 'Telegram',
        category: 'notifications',
        plugin: '@acme/easy-cms-telegram',
        docs: 'https://example.com/guide',
        kind: 'fields',
        fields: [
          { name: 'botToken', label: 'Bot token', secret: true, required: true },
          { name: 'chatId', label: 'Chat ID', required: true },
        ],
        env: { botToken: 'TELEGRAM_BOT_TOKEN' },
        // ตรวจค่าโดยไม่โพสต์อะไร ตอนเชื่อมจะรันก่อนเสมอ
        test: async ({ botToken }) => {
          const res = await fetch(`https://api.telegram.org/bot${botToken}/getMe`)
          const body = await res.json()
          return body.ok ? { ok: true, account: body.result.username } : { ok: false, message: body.description }
        },
      },
    ],
  }))

plugin อ่านการเชื่อมต่อฝั่ง server และแจ้งผลการใช้งาน:

const telegram = await cms.integrations.get('telegram') // ค่าเริ่มต้น หรือใส่ { name } เพื่อเลือกตัวอื่น
if (telegram) {
  try {
    await send(telegram.values.botToken, telegram.values.chatId, text)
    await cms.integrations.markUsed('telegram')
  } catch (error) {
    await cms.integrations.markError('telegram', String(error)) // ขึ้นบนการ์ดและแดชบอร์ด
  }
}

get คืน null เมื่อยังไม่ได้เชื่อมหรือผู้ให้บริการถูกปิด cms.integrations.all('telegram') คืนการเชื่อมต่อทั้งหมด (ของ environment ก่อน) เช่นเพื่อส่งไปทุกแชต

ค้นหาค่า และติ๊กตัวเลือก

ช่องที่มี lookup จะมีปุ่ม ค้นหา อยู่ข้าง ๆ Easy CMS รัน lookup ด้วยค่าที่กรอกไว้แล้ว (ตอนแก้การเชื่อมต่อ จะใช้ secret ที่เก็บไว้แทนช่องที่เว้นว่าง) แล้วผู้ดูแลเลือกจากรายการ เช่นแชตที่มีคนส่งข้อความหาบอท ช่อง multiselect เป็นกลุ่ม checkbox เก็บเป็น a,b,c

fields: [
  { name: 'botToken', label: 'Bot token', secret: true, required: true },
  {
    name: 'chatId',
    label: 'Chat ID',
    lookup: async ({ botToken }) => (await chatsOf(botToken)).map((c) => ({ value: String(c.id), label: c.title })),
  },
  { name: 'events', type: 'multiselect', options: [{ value: 'order.created', label: 'คำสั่งซื้อใหม่' }, 'form.submitted'] },
]

ข้อผิดพลาดที่ lookup throw จะขึ้นใต้ช่อง แสดงได้ไม่เกิน 100 รายการ

OAuth

kind: 'oauth' มีปุ่ม เชื่อม แทนช่องกรอก Easy CMS เซ็น state (ใช้ได้ 10 นาที) แล้วพาผู้ดูแลไปที่ authorizeUrl ผู้ให้บริการพากลับมาที่ <api>/admin/integrations/<id>/callback และ exchange แลก code เป็นค่าต่างๆ ถ้ามีหลายบัญชี (เพจทั้งหมดของผู้ใช้) ผู้ดูแลจะเลือกหนึ่งบัญชี:

{
  id: 'facebook-page',
  label: 'Facebook Page',
  category: 'social',
  kind: 'oauth',
  oauth: {
    authorizeUrl: ({ redirectUri, state }) =>
      `https://www.facebook.com/v21.0/dialog/oauth?client_id=${process.env.META_APP_ID}&redirect_uri=${encodeURIComponent(redirectUri)}&state=${state}&scope=pages_manage_posts`,
    exchange: async ({ code, redirectUri }) => {
      const pages = await pagesFor(code, redirectUri) // API ของผู้ให้บริการ
      return { accounts: pages.map((p) => ({ id: p.id, name: p.name, values: { token: p.access_token } })) }
    },
  },
}

ทุกอย่างที่ exchange คืนมาถูกเข้ารหัส ส่วน id และ secret ของแอปเก็บไว้ใน environment variable

ขั้นต่อไป