本页面介绍编辑器端配置——这是将基于浏览器的 TinyMCE 编辑器与运行中的 AI 服务连接的最后一步。在部署过程中,此时 数据层、LLM providers和 JWT token endpoint已正常运行。本页面将编辑器与该基础架构连接起来。

前提条件:

  • AI 服务已运行且浏览器可访问(参见 入门指南)。

  • 存在可签发 HS256 JWT 的 token endpoint(参见 JWT 认证)。

  • 已启用 AI 功能的有效 TinyMCE license key 或 API key。

有关框架通用配置(安装包装器、组件结构),请参阅现有集成指南:

本地部署 AI 集成会将下列选项添加到标准 TinyMCE init 配置中。

Token 流程

编辑器调用 token provider 的时序图,provider 从应用后端获取 JWT 后将其传递给 AI 服务

插件在初始化时和缓存 token 过期前会调用 token provider。应用后端通过自身 session 层验证浏览器请求,使用 API Secret 签发 HS256 JWT 并返回。插件随后在每次请求中将 JWT 发送给 AI 服务。

必需的编辑器选项

选项说明

plugins

tinymceai。

toolbar

包含 tinymceai-chat、tinymceai-review、tinymceai-quickactions 中的一个或多个。

tinymceai_service_url

AI 服务的 base URL,例如 https://ai.yourcompany.com,或当反向代理通过路径提供 AI 服务时的 https://www.yourcompany.com/ai。参见 tinymceai_service_url。

tinymceai_token_provider

返回 Promise<{ token: string }> 的函数。参见下文的 tinymceai_token_provider。

最小示例

以下原生 JavaScript 示例包含所有本地部署专属选项。相同的 init 选项同样适用于 React、Vue、Angular 和 Svelte 包装器组件。

<!DOCTYPE html>
<html>
<head>
  <script src="/path/to/tinymce/tinymce.min.js" referrerpolicy="origin"></script>
</head>
<body>
  <textarea id="editor"><p>Hello world.</p></textarea>
  <script>
    tinymce.init({
      selector: '#editor',
      license_key: 'T8LK:...',
      plugins: 'tinymceai',
      toolbar: 'undo redo | bold italic | tinymceai-chat tinymceai-review tinymceai-quickactions',
      height: 500,
      tinymceai_service_url: 'https://ai.yourcompany.com',
      tinymceai_token_provider: () => {
        return fetch('/api/ai-token', {
          method: 'POST',
          credentials: 'include'
        })
          .then((r) => r.json())
          .then((data) => ({ token: data.token }));
      }
    });
  </script>
</body>
</html>

将 /path/to/tinymce/ 替换为自托管 TinyMCE 资源的位置。有关下载和设置说明,请参见 自托管安装。

tinymceai_token_provider

返回 Promise 的函数,该 Promise 解析为包含 JWT 字符串的 token 属性对象。

预期返回结构
{ token: 'eyJhbGciOiJIUzI1NiIs...' }
provider 示例
tinymceai_token_provider: () => {
  return fetch('/api/ai-token', { method: 'POST' })
    .then((r) => r.json())
    .then((data) => ({ token: data.token }));
}
行为详情

自动刷新

插件在初始化时和缓存 token 即将过期(60 秒安全余量)时会再次调用 provider。请勿在 provider 内部缓存 JWT。

错误处理

如果函数 reject 或 endpoint 返回非 OK 响应,插件会在编辑器界面显示错误。未返回 Promise 的 provider 会报错 JWT token provider did not return a promise。Promise 解析结果不是包含字符串 token 的对象时会报错 Invalid JWT token provider result。

Token 有效期

Token 应采用短时效(推荐 5–15 分钟)。关于签名密钥、payload 结构以及时效设置指南,请参阅 JWT authentication。

框架特定示例

以下示例展示了将 TinyMCE AI 插件连接到本地部署服务所需的最小配置。每个示例都使用框架包装器的 init 属性传递上文所记录的相同选项。

React

import { Editor } from '@tinymce/tinymce-react';

function MyEditor() {
  return (
    <Editor
      licenseKey="T8LK:..."
      init={{
        plugins: 'tinymceai',
        toolbar: 'undo redo | bold italic | tinymceai-chat tinymceai-review tinymceai-quickactions',
        height: 500,
        tinymceai_service_url: 'https://ai.yourcompany.com',
        tinymceai_token_provider: () =>
          fetch('/api/ai-token', { method: 'POST', credentials: 'include' })
            .then((r) => r.json())
            .then((data) => ({ token: data.token }))
      }}
    />
  );
}

Vue.js

<template>
  <Editor
    license-key="T8LK:..."
    :init="editorInit"
  />
</template>

<script setup>
import Editor from '@tinymce/tinymce-vue';

const editorInit = {
  plugins: 'tinymceai',
  toolbar: 'undo redo | bold italic | tinymceai-chat tinymceai-review tinymceai-quickactions',
  height: 500,
  tinymceai_service_url: 'https://ai.yourcompany.com',
  tinymceai_token_provider: () =>
    fetch('/api/ai-token', { method: 'POST', credentials: 'include' })
      .then((r) => r.json())
      .then((data) => ({ token: data.token }))
};
</script>

Angular

<!-- app.component.html -->
<editor
  [licenseKey]="'T8LK:...'"
  [init]="editorInit"
></editor>
// app.component.ts
import { Component } from '@angular/core';

@Component({ selector: 'app-root', templateUrl: './app.component.html' })
export class AppComponent {
  editorInit = {
    plugins: 'tinymceai',
    toolbar: 'undo redo | bold italic | tinymceai-chat tinymceai-review tinymceai-quickactions',
    height: 500,
    tinymceai_service_url: 'https://ai.yourcompany.com',
    tinymceai_token_provider: () =>
      fetch('/api/ai-token', { method: 'POST', credentials: 'include' })
        .then((r: Response) => r.json())
        .then((data: { token: string }) => ({ token: data.token }))
  };
}

关于通用的框架配置(安装包装器、SSR 配置、bundler 设置),请参考本页面顶部链接的框架集成指南。请将 T8LK:…​ 替换为 Tiny 账户代表提供的 license key。

对 token 请求进行身份验证

tinymceai_token_provider 会从应用后端获取一个 JWT。该后端如何验证来自浏览器的请求取决于应用架构。

会话 Cookie

如果页面和 token 端点位于同一 origin(或同一父域),浏览器将自动发送会话 cookie:

fetch('/api/ai-token', { method: 'POST', credentials: 'include' })

对于跨 origin 的 token 端点,后端必须返回 Access-Control-Allow-Origin: <editor-origin>(不能为 *)和 Access-Control-Allow-Credentials: true,并且会话 cookie 必须设置为 SameSite=None; Secure。

Bearer Header

如果应用已持有会话 JWT(在渲染时注入,或来自某个 auth 库),请将其作为 header 转发:

fetch('/api/ai-token', {
  method: 'POST',
  headers: { 'Authorization': `Bearer ${sessionJwt}` }
})

此模式完全避免使用 cookie,并且适用于跨 origin 设置。

对 AI 服务的跨 origin 请求

在生产环境中,编辑器页面与 AI 服务通常运行在不同的 origin(https://app.yourcompany.com 与 https://ai.yourcompany.com)。AI 服务必须返回允许编辑器 origin 的 CORS headers;否则浏览器将阻止所有请求。

配置 ALLOWED_ORIGINS

在 AI 服务容器上将 ALLOWED_ORIGINS 环境变量设置为以逗号分隔的、被允许的编辑器 origins 列表(scheme + host + port):

-e ALLOWED_ORIGINS='https://app.yourcompany.com,https://staging.yourcompany.com'
行为说明

格式

以逗号分隔的 origins。每个条目必须包含 scheme(https://)。不要包含路径或末尾斜杠。

未设置时的默认行为

服务将拒绝跨 origin 请求(无 Access-Control-Allow-Origin header)。当编辑器位于不同 origin 的任何部署中,都应设置此变量。

通配符

允许使用 *,但不建议在生产环境使用。它允许任意 origin 调用 AI 服务端点。

Preflight(OPTIONS)

服务会在内部处理 OPTIONS preflight 请求,并返回相应的 Access-Control-Allow-Methods 与 Access-Control-Allow-Headers。对于 OPTIONS,无需进行反向代理配置。插件会发送 Authorization、Content-Type 以及 x-editor-version 请求头,因此 preflight 响应必须允许这三个头。

凭证

当请求源与 ALLOWED_ORIGINS 中的条目匹配时,服务会响应 Access-Control-Allow-Credentials: true。

验证 CORS

curl -i -X OPTIONS https://ai.yourcompany.com/v1/conversations \
  -H 'Origin: https://app.yourcompany.com' \
  -H 'Access-Control-Request-Method: POST' \
  -H 'Access-Control-Request-Headers: authorization,content-type,x-editor-version'

响应应包含 Access-Control-Allow-Origin: https://app.yourcompany.com。如果显示 * 或没有 CORS 头,请更新 AI 服务容器上的 ALLOWED_ORIGINS 并重启。

常见的 CORS 错误

错误修复方法

源末尾有斜杠(https://app.example.com/)

去掉末尾的斜杠。

非标准端口缺少端口号(https://app.example.com:3000)

在 ALLOWED_ORIGINS 中包含端口号。

ALLOWED_ORIGINS 完全未设置

所有跨域请求都会静默失败。添加编辑器的源。

反向代理剥离了 Origin 头

配置代理将 Origin 头传递给 AI 服务。

内容安全策略(CSP)

如果应用程序设置了 Content-Security-Policy 头,请在 connect-src 中允许 AI 服务的源:

Content-Security-Policy:
  connect-src 'self' https://ai.yourcompany.com;
  script-src 'self';

如果使用 Tiny CDN 而不是自托管资源,还需将 https://cdn.tiny.cloud 添加到 script-src 中。

常见的集成错误

症状可能的原因修复方法

编辑器加载但未出现 AI 按钮

plugins 中未包含 tinymceai,或 TinyMCE 版本为 7.x 或更低

将 tinymceai 添加到 plugins 列表,并确认脚本 URL 使用的是 /tinymce/8/。验证 API 密钥是否启用了 AI 功能。

POST /api/ai-token 返回 401

令牌端点拒绝 fetch 请求

确认 fetch 发送了会话 cookie(credentials: 'include')或后端期望的 Authorization 头。

AI 响应挂起然后超时

反向代理正在缓冲服务器发送事件(SSE)

禁用代理缓冲。参见生产部署。

浏览器控制台显示 /v1/conversations 上的 CORS 错误

ALLOWED_ORIGINS 中未包含编辑器的源

更新 ALLOWED_ORIGINS 并重启 AI 服务。

tinymceai_token_provider 在紧密循环中被调用

令牌端点返回无效的 JSON 或非 200 状态码

验证:curl -X POST http://localhost:3000/api/ai-token 应返回带有 HTTP 200 的 {"token":"eyJ..."}。

对于其他问题,请参见故障排除。

另请参阅