Corsair Google Drive 插件实战:22 个类型化 API、OAuth 2.0 授权与本地数据库同步
2026/9/16 11:11:03 网站建设 项目流程

Corsair Google Drive 插件实战:22 个类型化 API、OAuth 2.0 授权与本地数据库同步

【免费下载链接】corsairConnect your users to their apps项目地址: https://gitcode.com/GitHub_Trending/corsa/corsair

@corsair-dev/googledrive是 Corsair 生态中的 Google Drive 集成插件,为 AI Agent 提供"连接用户自己的 Google Drive"的能力:一个客户端即可完成文件与文件夹的增删改查、共享、搜索、共享云端硬盘(Shared Drive)管理与存储配额查询,并自动把云端数据同步到本地数据库。本文基于该插件在仓库中的源码与文档,完整讲解它的安装方式、全部 22 个端点的操作语义与风险分级、OAuth 2.0 授权流程、本地实体同步机制以及driveChangedWebhook 的订阅与处理,读完后你可以直接在你的 Corsair 应用中接入 Google Drive 能力。

插件概览:一个插件能做什么

Google Drive 插件围绕googledrive这一个插件对象展开,核心产物来自 index.ts 中的googledrive()工厂函数:

  • 22 个类型化 API 操作,按filesfolderssharedDrivessearchstorage五组组织,每个操作都有基于 zod 的输入/输出校验;
  • 3 个同步实体filesfolderssharedDrives,读写操作后自动 upsert 到本地数据库(见 schema/database.ts);
  • 1 个入站 Webhook 事件driveChanged,监听文件/文件夹的创建、更新与删除。

插件的声明定义在 index.ts:googleDriveEndpointsNested将 22 个端点组织成files.listfolders.createstorage.getQuota这样的点分命名空间,googleDriveWebhooksNested声明唯一的driveChanged事件。端点树结构同时被用作permissions配置的类型约束——配置里写错路径会直接产生 TypeScript 类型错误。

安装与初始化

安装

在你的 Corsair 项目中安装插件(以 pnpm 为例,仓库 package.json 中声明了 npm/yarn/pnpm/bun 均可用):

pnpm add @corsair-dev/googledrive

插件以corsair(要求>=0.1.120)和zod^4.1.13)为 peer 依赖,运行时还会用到corsair/corecorsair/http提供的上下文、OAuth token 获取、HTTP 请求与 Webhook 基础设施。

注册插件

官方文档(docs/plugins/googledrive/overview.mdx)给出了完整的初始化示例:

// corsair.ts import Database from 'better-sqlite3'; import { createCorsair } from 'corsair'; import { googledrive } from '@corsair-dev/googledrive'; export const corsair = createCorsair({ plugins: [ googledrive(), ], database: new Database('corsair.db'), kek: process.env.CORSAIR_KEK!, hub: { projectApiKey: process.env.CORSAIR_API_KEY!, signingSecret: process.env.CORSAIR_SIGNING_SECRET!, }, });

多租户是默认形态:通过corsair.withTenant(id)圈定租户后再调用插件 API。连接租户时,调用管理接口生成连接链接并引导用户浏览器跳转:

const { connectUrl } = await corsair.manage.connect.createLink({ plugin: 'googledrive', tenantId: 'acme', }); // 将用户浏览器重定向到 connectUrl

首次使用时,Corsair 会提示租户完成凭据授权(README 中 Auth 一节的说明),之后所有端点调用都会自动携带该租户的访问令牌。

端点总览:22 个操作与风险分级

README 的 Endpoints 一节完整列出了全部 22 个端点。下表完整继承该清单,并按源码 index.ts 中googleDriveEndpointMeta声明的风险分级补充了说明:

操作Operation ID风险说明
files.copygoogledrive.api.files.copywrite复制 Google Drive 中的文件
files.createFromTextgoogledrive.api.files.createFromTextwrite从文本内容创建新的 Drive 文件
files.deletegoogledrive.api.files.deletedestructive永久删除文件 [破坏性 · 不可逆]
files.downloadgoogledrive.api.files.downloadread下载文件内容
files.getgoogledrive.api.files.getread获取指定文件的元数据
files.listgoogledrive.api.files.listread列出 Google Drive 中的文件
files.movegoogledrive.api.files.movewrite将文件移动到不同文件夹
files.sharegoogledrive.api.files.sharewrite通过授予权限共享文件
files.updategoogledrive.api.files.updatewrite更新文件内容或元数据
files.uploadgoogledrive.api.files.uploadwrite上传文件到 Google Drive
folders.creategoogledrive.api.folders.createwrite创建新文件夹
folders.deletegoogledrive.api.folders.deletedestructive永久删除文件夹及其内容 [破坏性 · 不可逆]
folders.getgoogledrive.api.folders.getread获取指定文件夹的元数据
folders.listgoogledrive.api.folders.listread列出 Google Drive 中的文件夹
folders.sharegoogledrive.api.folders.sharewrite通过授予权限共享文件夹
search.filesAndFoldersgoogledrive.api.search.filesAndFoldersread搜索 Google Drive 中的文件和文件夹
sharedDrives.creategoogledrive.api.sharedDrives.createwrite创建新的共享云端硬盘
sharedDrives.deletegoogledrive.api.sharedDrives.deletedestructive永久删除共享云端硬盘 [破坏性 · 不可逆]
sharedDrives.getgoogledrive.api.sharedDrives.getread获取共享云端硬盘信息
sharedDrives.listgoogledrive.api.sharedDrives.listread列出共享云端硬盘
sharedDrives.updategoogledrive.api.sharedDrives.updatewrite更新共享云端硬盘
storage.getQuotagoogledrive.api.storage.getQuotaread获取用户的 Google Drive 存储配额与用量

风险分级(riskLevel)是插件安全模型的核心:read类操作仅读取数据;write类操作会修改云端数据,但可恢复;destructive类操作(三个delete)被标记为irreversible: true,用于 MCP 服务器的权限系统决定 allow / deny / require_approval。README 中所有标有[DESTRUCTIVE · IRREVERSIBLE]的操作,在调用前都应经过审批或强确认。

API 调用实践:典型操作与输入参数

插件所有端点都通过tenant.googledrive.api.*路径调用。这里结合 endpoints/types.ts 中的 zod 输入 schema 与各端点实现,给出最常用的几类调用。

列出文件

files.list支持 Google Drive 标准查询参数(q查询串、pageSize分页大小、pageToken翻页游标、spacescorporadriveIdorderBysupportsAllDrives等):

const tenant = corsair.withTenant('acme'); const result = await tenant.googledrive.api.files.list({ pageSize: 50, q: "mimeType='text/plain'", });

实现上,files.list会请求GET /files并把返回的每条记录按 mimeType 判断后 upsert 进本地filesfolders表(endpoints/files.ts),因此列表结果可以直接在本地复用。

从文本创建文件

const tenant = corsair.withTenant('acme'); const file = await tenant.googledrive.api.files.createFromText({ name: 'hello.txt', content: 'Hello from Corsair', mimeType: 'text/plain', // 可选 parents: ['folderId'], // 可选,指定父文件夹 description: 'demo', // 可选 });

从源码看,createFromTextuploadType: 'multipart'发起POST /files,创建成功后自动回查files.get刷新最新元数据(endpoints/files.ts)。files.upload的入参与其一致(namemimeTypeparentsdescription),同样走 multipart 上传。

更新、复制与移动文件

// 更新元数据:重命名、加星、移入回收站、改父目录 await tenant.googledrive.api.files.update({ fileId: 'FILE_ID', name: 'renamed.txt', starred: true, trashed: false, addParents: 'newFolderId', removeParents: 'oldFolderId', }); // 复制 await tenant.googledrive.api.files.copy({ fileId: 'FILE_ID', name: 'copy-of-file.txt', parents: ['targetFolderId'], }); // 移动 await tenant.googledrive.api.files.move({ fileId: 'FILE_ID', addParents: 'targetFolderId', removeParents: 'sourceFolderId', });

files.update在实现中通过PATCH /files/{fileId}合并提交namedescriptionstarredtrashedparentspropertiesappProperties,并把addParents/removeParents/supportsAllDrives等作为查询参数透传(endpoints/files.ts)。

下载与共享

// 下载文件原始内容(返回二进制流,形状取决于文件类型) const binary = await tenant.googledrive.api.files.download({ fileId: 'FILE_ID', acknowledgeAbuse: false, }); // 共享文件:对"任何人"授予只读 await tenant.googledrive.api.files.share({ fileId: 'FILE_ID', type: 'anyone', role: 'reader', });

files.sharePOST /files/{fileId}/permissionstype可选user/group/domain/anyonerole可选owner/organizer/fileOrganizer/writer/commenter/reader,还支持expirationTime(过期时间)、sendNotificationEmailtransferOwnership等高级参数。folders.share的参数与行为一致(endpoints/folders.ts)。

搜索与配额

// 跨文件与文件夹搜索,q 为必填 await tenant.googledrive.api.search.filesAndFolders({ q: "name contains 'report'", pageSize: 20, }); // 存储配额:limit 总量、usage 已用、usageInDrive / usageInDriveTrash const quota = await tenant.googledrive.api.storage.getQuota();

storage.getQuota请求GET /about?fields=storageQuota,若响应缺少storageQuota会抛出GoogleDriveAPIError('Google Drive about.get returned no storageQuota', 502)(endpoints/storage.ts)。

认证机制:OAuth 2.0 与令牌生命周期

README 明确:插件使用OAuth 2.0,Corsair 会在首次使用时提示租户提供凭据。源码层面的完整配置见 index.ts:

export const googledriveAuthConfig = { oauth_2: { account: ['channel_id', 'changes_page_token'] as const, }, };
  • 授权端点https://accounts.google.com/o/oauth2/v2/auth
  • 令牌端点https://oauth2.googleapis.com/token
  • Scopehttps://www.googleapis.com/auth/drive(完整读写权限)
  • 授权参数access_type: 'offline'(获取可用于刷新的 refresh token)、prompt: 'consent'

keyBuilder在调用端点时通过getOAuthAccessToken(ctx, { plugin: 'googledrive', tokenUrl: ... })解析当前租户的访问令牌;若 authType 不是oauth_2或取不到令牌,则抛出AuthMissingError('googledrive', 'oauth_2')。同时支持传入options.key作为静态 key 兜底。

底层 HTTP 客户端见 client.ts:所有请求以https://www.googleapis.com/drive/v3为 base,走corsair/httprequestmakeAuthenticatedGoogleDriveRequest内置 401 自动重试——遇到 401 且存在_refreshAuth回调时,先刷新令牌再重放请求,对上层调用透明。

本地数据库同步:files / folders / sharedDrives

插件把 Google Drive 数据建模为三类本地实体(schema/database.ts):

  • files:文件实体,除 Drive 元数据(namemimeTypeparentstrashedsizewebViewLinksha256Checksum等)外,额外包含filePathcreatedAt
  • folders:文件夹实体,结构类似 files,mimeType 固定为application/vnd.google-apps.folder
  • sharedDrives:共享云端硬盘实体(namethemeIdcolorRgbhidden)。

几乎每个读端点都会在返回结果后把记录upsertByEntityId到对应表;删除端点则deleteByEntityId同步清理本地数据。这样 Agent 就可以不命中 Drive API直接查询已同步数据:

const tenant = corsair.withTenant('acme'); const rows = await tenant.googledrive.db.files.search({ data: { trashed: false }, limit: 50, });

plugin-docs.yaml(packages/googledrive/plugin-docs.yaml)中给出的 db 示例正是"搜索未进回收站的文件、无需再请求 Drive",适合作为常用查询范式。

Webhook:driveChanged 事件

README 说明插件处理1 个 Webhook 事件driveChanged)。它基于 Google Drive 的changes feed + Watch channel机制实现:文件/文件夹被创建、更新或删除时触发,属于典型的"入站 Webhook + 拉取增量"混合模型。

订阅原理

googledriveSubscribe(subscribe.ts)在租户连接时执行:

  1. 用访问令牌请求GET /changes/startPageToken获取起始页令牌;
  2. 将令牌持久化到租户 keys(set_changes_page_token);
  3. 调用googleChannelSubscribeGET /changes/watch?pageToken=...上打开共享的 Watch channel——覆盖整个 Drive(单个文件需要单独的文件级 channel,插件选择整盘监听)。

处理器挂载

将 Corsair 的 HTTP handler 挂载到任意框架路由(docs/plugins/googledrive/webhooks.mdx 示例为 Next.js App Router):

// app/api/webhook/route.ts import { processWebhook } from "corsair"; import { corsair } from "@/server/corsair"; export async function POST(request: Request) { const headers = Object.fromEntries(request.headers); const body = await request.json(); const result = await processWebhook(corsair, headers, body); return result.response; }

插件通过pluginWebhookMatcher做前置过滤:校验请求头from: noreply@google.comuser-agent包含APIs-Google,并解码 Pub/Sub 消息确认resourceUri与 drive 相关(index.ts)。

事件载荷与响应数据

driveChanged的响应数据(DriveChangedEventSchema,见 webhooks/types.ts)核心字段:

{ type: 'fileChanged' | 'folderChanged', fileId?: string, folderId?: string, changeType: 'created' | 'updated' | 'deleted' | 'trashed' | 'untrashed', file?: File, folder?: File, filePath?: string, // 例如 /folder/sub/file.txt change?: Change, binaryData?: string | null, // 文件内容 base64(文件事件时) allFiles: [...], // 本次变化涉及的全部文件 allFolders: [...], // 本次变化涉及的全部文件夹 }

webhookHooks处理事件:

googledrive({ webhookHooks: { driveChanged: { after: async (ctx, result) => { console.log('Drive change:', result.data?.changeType, result.data?.fileId ?? result.data?.folderId); }, }, }, });

增量消费的健壮性设计

driveChanged的处理逻辑(webhooks/changes.ts)值得关注:

  • 分页拉满fetchAllChanges逐页消费 changes feed(每页 100 条),循环中记录已请求的 page token 防止环状引用导致重复拉取,并以MAX_CHANGE_PAGES = 10封顶避免大积压拖垮 Webhook 响应;
  • 断点续传:最终页返回的newStartPageToken作为下一轮游标写入租户 keys;若被截断则记录resumeToken供下一条通知继续,并用 CAS(compare-and-set)防止并发覆盖;
  • 失效令牌恢复:当存储的游标过期(400invalidStartPageToken)且与通知 URI 中的 token 不一致时,自动回退到 URI 中携带的 token 重新拉取;
  • 变更归类removed标记映射为deletedfile.trashed映射为trashed,并计算文件路径(buildFilePath向上回溯父目录、深度上限 20);
  • 事件归并:一次通知可能包含多个变化,返回数据中的allFiles/allFolders汇总全部受影响对象,首个变化作为主事件。

测试与验证

仓库为插件提供了完整的测试保障,是理解端点的最佳佐证:

  • api.test.ts:面向真实 Google API 的集成测试(需要GOOGLE_ACCESS_TOKEN环境变量),逐一对 files/folders/sharedDrives/storage/search 五组端点做"创建 → 操作 → 校验输出 schema → 清理"闭环,并用GoogleDriveEndpointOutputSchemas.*校验返回类型;
  • webhooks/changes.test.ts、subscribe.test.ts 与 client.test.ts 覆盖 Webhook 处理与 HTTP 客户端行为;
  • jest作为测试运行器,package.json中提供pnpm testpnpm typecheck脚本。

结语与进阶阅读

至此,你已经掌握了@corsair-dev/googledrive的核心用法:22 个类型化端点的调用方式与风险分级、OAuth 2.0 授权与 401 自动刷新、三类本地实体的自动同步,以及driveChangedWebhook 的订阅与增量消费。继续深入可参考仓库中的以下位置:

  • 插件声明与配置:packages/googledrive/index.ts
  • 全部输入/输出 zod schema:packages/googledrive/endpoints/types.ts
  • 本地实体模型:packages/googledrive/schema/database.ts
  • Webhook 处理细节:packages/googledrive/webhooks/changes.ts
  • 集成测试用例:packages/googledrive/api.test.ts
  • 官方使用文档:docs/plugins/googledrive/overview.mdx 与 docs/plugins/googledrive/webhooks.mdx

如需了解如何将插件的操作暴露为 MCP 工具给 Agent 使用,可进一步查看 docs/mcp-adapters/mcp-adapters.mdx。

【免费下载链接】corsairConnect your users to their apps项目地址: https://gitcode.com/GitHub_Trending/corsa/corsair

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询