Repository navigation
Core Framework
StackPanel 采用 微内核(Microkernel)架构。内核提供六项基础设施职责,其余一切功能均为插件。除这六项外,内核还拥有少量平台服务(支付编排、钱包、汇率、站内通知、共享状态),供任意插件通过 ctx.* 复用——这些是横向能力,而非业务功能(见 ADR-0006 与 ADR-0020)。
| 职责 | 实现位置 | 说明 |
|---|---|---|
| 用户与 RBAC |
plugins/auth.ts、lib/rbac.ts、auth/session-store.ts
|
本地认证、服务端会话(Redis/进程内)、角色权限 |
| 插件运行时 |
plugins/runtime.ts、plugins/dispatcher.ts
|
生命周期管理、路由分发、扩展点 |
| 主题引擎 |
lib/themes.ts、routes/theme.ts
|
ZIP 主题包、CSS Token 校验、激活/预览 |
| 事件总线 | plugins/events.ts |
DB Outbox + Redis Pub/Sub,本地即时投递 |
| 设置与密钥 |
lib/platform-info.ts、lib/crypto.ts
|
JSON 设置存储、AES-256-GCM 加密密钥 |
| 审计日志 |
plugins/audit.ts、routes/admin/audit.ts
|
只追加审计,覆盖全部管理操作 |
所有功能先有 REST API,后台(Next.js Web)只是 API 的客户端(BFF 模式)。Web 不含业务逻辑,仅负责渲染和 API 转发。
内核路由和插件路由物理隔离:
- 内核路由:注册为 Fastify 原生路由,由 radix router 匹配,优先级最高
-
插件路由:存在内存路由表(
PluginDispatcher),由 catch-all/*处理器分发
因为 catch-all 最后注册,内核路由永远优先匹配。
插件路由表可运行时变更。安装、升级、启停插件无需重启服务,路由即时生效。
TypeScript strict 模式(当前 typescript@6)。@stackpanel/sdk 提供完整的类型契约,插件开发者和 API 消费者共享同一套类型定义;@stackpanel/spec 是内核 API 版本与 OpenAPI 投影的单一来源。
HTTP 请求
→ Fastify radix router 匹配内核路由
→ preHandler 链(requireAuth → requireRole)
→ 路由 handler
→ onSend 钩子(安全头注入)
→ 响应
HTTP 请求
→ Fastify radix router 无匹配(落到 catch-all)
→ PluginDispatcher.match(method, path)
→ runGuards(isActive → requireAuth → admin 时 requirePermission('platform.admin') → permission 校验)
→ 构造 HttpRequest(框架无关视图)
→ 插件 handler(request, reply)
→ reply.send(result)
→ 响应
统一错误模型为 RFC 7807 application/problem+json(ADR-0012)。全局 onSend 钩子把所有 >=400 的 JSON 响应归一化,setErrorHandler 处理异常:
-
业务错误(SDK
PluginError):按插件给定的code/status输出 problem+json,info 级日志 -
4xx:
{ type, title, status, detail, instance, requestId },响应头回传x-request-id -
5xx:
internal.unexpected+服务器内部错误,详情只入日志 -
校验失败(Fastify validation):
422 validation.invalid
initInfra() ← 校验基础设施、连接 Redis、确保对象存储桶(server.ts 中先于 buildApp)
buildApp({ redis })
├── 注册 Fastify 插件(cookie/cors/multipart/rateLimit/swagger/swagger-ui)
├── 全局 onSend 安全头 + RFC 7807 归一化 + setErrorHandler / setNotFoundHandler
├── 创建 PluginDispatcher → app.decorate
├── createPluginHost() → 内核服务 + PluginRuntime → app.decorate(payments/wallet/fx/auth/notifications/state)
├── registerKernelJobs() ← 内核 sweep 注册为 BullMQ 周期任务
├── installExtensionRoutes() ← Extension 引擎自定义资源路由
├── 注册内核路由(health/media/auth/nav/notifications/seo/open-api-v1/sp-v1/mcp/plugin/theme/admin/*)
├── registerApiVersioning() + installObservability()
├── onReady: initThemes() + seedDefaultBrand() ← 种子默认主题与品牌
├── onReady: host.registerPlugins() ← seed 内置插件 + 扫描 data/plugins 注册
└── installBodyRouting() + dispatcher.install() ← catch-all(最后注册)
start()
├── ensureBootstrapAdmin() ← 首启创建管理员
├── app.ready() ← 触发 onReady hooks
├── app.pluginHost.syncEnabledPlugins() ← 按 DB enabled 标志激活插件
├── startRuntimeCoherence() ← 订阅跨副本运行时失效
└── app.listen()
领域事件走事务性 Outbox + Redis Pub/Sub(ADR-0013):
-
publish先把事件写入 outbox(至少一次投递,进程崩溃不丢),由 relay 循环认领并本地投递 + 跨副本经 Redis Pub/Sub 分发。 - 未绑定 outbox 时(开发/测试/早期启动)退化为进程内
EventEmitter,同步投递。 -
EventBus接口是集群接缝:publish/subscribe广播事实,intercept/waterfall是同步环绕管线。
interface EventBus {
publish(topic: string, payload?: unknown): void;
subscribe(topic: string, handler: (payload: unknown) => void): () => void;
}存储引擎唯一为 PostgreSQL(Prisma provider=postgresql + @prisma/adapter-pg,ADR-0019)。连接串非 postgres:// / postgresql:// 时启动即失败。迁移一律手写 migration.sql 并用 migrate deploy,禁用 migrate dev。应用层无状态,集群一致性依赖 Redis 与对象存储(ADR-0017)。
开始
- 核心开发
构建内核扩展
- 插件
- 主题与前端包
AI 与自动化
- 内核 Agent
- MCP
对接平台
- Open Platform API
- Partner API