跳到主内容

应用扩展

ShopFaaS 应用扩展开发完整指南——目录结构、权限声明、原子 API、Webhook、前端 UI 与发布流程。强隔离架构确保店铺数据安全。

应用扩展是 ShopFaaS 的核心扩展机制,让店长与第三方开发者为店铺添加新功能。应用通过受控 API 访问店铺数据,运行在隔离沙箱中,确保店铺数据安全。

开发流程一览

  1. 初始化项目 — 用 @shopfaas/plugin-devkit init 创建应用骨架
  2. 声明权限 — 在 plugin.json 列出所需权限码
  3. 实现 Handler — 编写 Webhook handler 与自定义 Action
  4. 开发 UI — 在 ui/ 目录编写设置页与仪表盘组件(运行在 Web Worker 沙箱)
  5. 本地测试plugin-devkit dev 启动 HotReload 调试
  6. 打包发布plugin-devkit pack && publish 提交到应用市场
  7. 审核上架 — 自动化扫描 + 人工审核通过后上架

核心概念

强隔离架构

机制说明
运行时独立 Worker 子线程应用代码独立运行,CPU/内存/超时配额限制
权限声明plugin.json显式声明所需权限,店长安装时可审查
数据访问原子 API + 事件钩子不能直接操作平台数据库
前端 UIWeb Worker 沙箱UI 代码运行在沙箱,不影响主页面
数据存储元对象 API应用不允许自建表,使用平台元对象存储数据

数据访问方式

应用通过以下三种方式访问数据:

  1. 原子 APIctx.api.orders.list() / ctx.api.products.get() 等受控 CRUD 接口
  2. 元对象 API:创建应用自定义数据(基于平台元对象机制,存于 JSONB)
  3. 事件钩子:通过 Webhook 接收订单创建、支付完成等事件推送

应用目录结构

plugins/my-plugin/
├── plugin.json             # 应用元信息(名称、版本、权限、Webhook)
├── index.ts                # 应用入口(注册 Action / Webhook handler)
├── lib/                    # 应用内部库
├── webhooks/               # Webhook handler
│   ├── order-created.ts
│   └── order-paid.ts
├── actions/                # 自定义 Action handler
│   └── send-notification.ts
├── ui/                     # 前端 UI 组件(运行在 Web Worker 沙箱)
│   ├── Settings.tsx
│   └── Dashboard.tsx
├── config-schema.ts        # 设置项 schema(Zod)
└── package.json

plugin.json

{
  "name": "order-notify",
  "version": "1.0.0",
  "description": "订单通知应用,订单创建/支付时推送通知到外部服务",
  "author": "Your Name",
  "permissions": ["order:view"],
  "webhooks": [
    {
      "event": "order.created",
      "handler": "webhooks/order-created.ts"
    },
    {
      "event": "order.paid",
      "handler": "webhooks/order-paid.ts"
    }
  ],
  "actions": [
    {
      "name": "send-notification",
      "handler": "actions/send-notification.ts"
    }
  ],
  "config_schema": "config-schema.ts",
  "ui": {
    "settings": "ui/Settings.tsx",
    "dashboard": "ui/Dashboard.tsx"
  }
}

权限声明

应用在 plugin.json 中声明所需权限,权限码与 店铺角色权限码 一致:

{
  "permissions": ["order:view", "order:fulfill", "product:view", "customer:view"]
}

安装时审查

店长安装应用时,系统会展示应用所需权限列表,店长确认后才会授予对应权限。

运行时校验

应用调用原子 API 时,运行时自动校验权限:

// 若应用未声明 order:view 权限,调用将抛出 PermissionDeniedError
const orders = await ctx.api.orders.list({ limit: 10 });

原子 API

应用通过 ctx.api.* 访问店铺数据:

import type { PluginContext } from "@shopfaas/plugin-runtime";

export default async function handler(ctx: PluginContext) {
  // 订单
  const orders = await ctx.api.orders.list({ limit: 10, status: "PAID" });
  const order = await ctx.api.orders.get("uuid-1");
  await ctx.api.orders.fulfill("uuid-1", { tracking_number: "SF123" });

  // 商品
  const products = await ctx.api.products.list({ limit: 10 });
  await ctx.api.products.updateInventory("uuid-1", { quantity: 100 });

  // 客户
  const customers = await ctx.api.customers.list({ limit: 10 });

  // 元对象(应用自定义数据)
  const metaobject = await ctx.api.metaobjects.create({
    type: "wishlist",
    fields: { product_id: "uuid-1", customer_id: "uuid-2" },
  });
}

事件钩子(Webhook)

Webhook handler

// plugins/order-notify/webhooks/order-created.ts
import type { WebhookHandler } from "@shopfaas/plugin-runtime";

const handler: WebhookHandler<"order.created"> = async (event, ctx) => {
  const { order_id } = event.payload;

  // 调用原子 API 获取订单详情
  const order = await ctx.api.orders.get(order_id);

  // 读取应用配置
  const config = await ctx.getConfig();
  const webhookUrl = config.notification_webhook_url;

  // 发送通知到外部服务
  await fetch(webhookUrl, {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({
      event: "order.created",
      order,
      timestamp: event.timestamp,
    }),
  });
};

export default handler;

支持的事件

完整事件清单见 Webhook 文档

自定义 Action

应用可注册自定义 Action,供前端 UI 调用:

// plugins/order-notify/actions/send-notification.ts
import type { ActionHandler } from "@shopfaas/plugin-runtime";

const handler: ActionHandler<"send-notification"> = async (input, ctx) => {
  const { message, channel } = input;
  const config = await ctx.getConfig();

  const response = await fetch(config.notification_webhook_url, {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ message, channel }),
  });

  if (!response.ok) {
    throw new Error(`通知发送失败: ${response.status}`);
  }

  return { success: true };
};

export default handler;

应用配置

config-schema.ts

使用 Zod 定义应用配置 schema(店长在后台填写):

import { z } from "astro/zod";

export const configSchema = z.object({
  notification_webhook_url: z.url({ message: "请输入有效的 URL" }),
  notification_channels: z.array(z.enum(["email", "sms", "webhook"])).default(["webhook"]),
  retry_times: z.number().int().min(0).max(5).default(3),
});

export type Config = z.infer<typeof configSchema>;

读取配置

const config = await ctx.getConfig();
const webhookUrl = config.notification_webhook_url;

前端 UI

应用前端 UI 运行在 Web Worker 沙箱,通过 @shopfaas/plugin-bridge 与主页面通信:

// plugins/order-notify/ui/Settings.tsx
import { usePluginConfig, usePluginAction } from "@shopfaas/plugin-bridge";

export default function Settings() {
  const [config, setConfig] = usePluginConfig();
  const sendNotification = usePluginAction("send-notification");

  return (
    <div>
      <h2>订单通知设置</h2>
      <label>
        通知 Webhook URL:
        <input type="url" value={config?.notification_webhook_url ?? ""} onChange={e => setConfig({ notification_webhook_url: e.target.value })} />
      </label>
      <button onClick={() => sendNotification({ message: "测试通知", channel: "webhook" })}>发送测试通知</button>
    </div>
  );
}

开发工具链

@shopfaas/plugin-devkit

CLI 工具,提供应用扩展开发的完整工作流:

# 初始化新应用
bunx @shopfaas/plugin-devkit init my-plugin

# 本地开发(HotReload + 调试)
bunx @shopfaas/plugin-devkit dev

# 运行测试
bunx @shopfaas/plugin-devkit test

# 打包
bunx @shopfaas/plugin-devkit pack

# 发布到应用市场
bunx @shopfaas/plugin-devkit publish

@shopfaas/plugin-runtime

应用扩展运行时,提供:

  • PluginContext — 应用上下文(API 调用、配置读取、日志)
  • WebhookHandler — Webhook handler 类型
  • ActionHandler — Action handler 类型

@shopfaas/plugin-bridge

前端 UI 桥接,提供:

  • usePluginConfig() — 读取/写入应用配置
  • usePluginAction(name) — 调用应用自定义 Action
  • usePluginData(key) — 读取应用缓存数据

发布与审核

应用提交到应用市场需通过自动化扫描 + 人工审核

自动化扫描

  • 权限最小化检查(是否声明了不必要的权限)
  • API 调用合规性检查
  • 静态代码分析(潜在安全风险)
  • 性能基准测试(响应时间、资源占用)

人工审核

  • 业务合规性(是否符合平台政策)
  • 代码质量(注释、类型、测试覆盖)
  • UI/UX 体验
  • 文档完整性

审核通过后应用即可在应用市场上架,详见 应用市场

相关文档

此页面有帮助吗?

在 GitHub 上编辑此页