@dreamtree-org/integrations

中文文档
0.6.5发布于 2026/11/13MITTypeScriptESM + CJS近 30 天下载 2845

每个Dreamtree产品共享第三方集成——OAuth,被封存的每个公司连接,Google Drive和Calendar,Zoho Books,Slack,SMTP和IMAP,以及私人S3/Spaces对象存储.

中文使用文档
文档目录21 节
  1. 关于公司
  2. 作者维护者( V)
  3. ++ 赞助和支持
  4. 通过UPI进行一塔普捐赠
  5. 成为赞助者
  6. 特征
  7. 安装
  8. 设计规则
  9. 谷歌驱动器
  10. 连接和 OAuth 状态
  11. 谷歌日历
  12. 偷懒
  13. 佐霍书社
  14. 邮件: SMTP 和 IMAP
  15. 剃刀纸
  16. 存储:数字海洋空间
  17. 无 SDK 供应商的操作权限
  18. 发展
  19. 捐款
  20. 许可证
  21. 支助

@dreamtree-org/合并

替换复制到每个 Pro 产品的第三方集成代码 (单位:千美元)infrastructure/googleDriveClient.js 在任务pro, utils/googleDrive.js 内 冒险者, lib/zoho.js (......) 每包一个子路径 供养. 每个供应商的SDK是 可选同伴依赖,所以一个应用程序 只为其进口的供应商安装SDK。 例外的是 nodemailer,一个正则依赖: SMTP发送的作品没有额外的安装.

npm version License: MIT Website

关于公司

@dreamtree-org/ 整合的建设和维护 梦幻之树 ——一个团队为现代网络设计了深思熟虑,开发者第一的软件. 我们相信,伟大的产品从一个伟大的基础开始, 而这正是这个一揽子计划的目标: 一个精致的,开放的,和可靠的整合层 对于各地的团队.

🌐 网站: www.dreamtreeglobal.com (中文(简体) )

作者维护者( V)

普雷塔姆·克里希纳 —— 说 梦幻之树 📧 [email protected] 互联网档案馆的存檔,存档日期2013-12-21

有想法,一个虫子, 还是只是想打个招呼? 随时伸出援手——像你这样的开发者的回馈是这个软件包的不断增长.

++ 赞助和支持

@dreamtree-org/整合为 自由和开放源码完全由激情和深夜驱动 您所依赖的每个功能——每个 OAuth 流量, 提供者客户端, 以及 Webhook 签名检查—— 代表了数小时的仔细操作, 以便  产品可以和Google,Zoho,Slack,邮件和Razorpay交谈,而无需重写管道.

如果此软件包节省了您的时间, 引发了一个想法, 或者省去了另一个手卷的 API 客户端, 请考虑支持它的持续开发 . 您的赞助让更新不断出现, 虫子被压倒, 和文件尖锐的—— 整个社区. 甚至最小的贡献也是巨大的鼓励

通过UPI进行一塔普捐赠

QQ 用任何 UPI 应用程序扫描 QR (Google Pay, PhonePe, Paytm, BHIM,.) 即时支付:

Scan to pay Dreamtree-Org via UPI

UPI身份证: dhrugantha.llp@kotak  二. 支助  收款人 : 梦幻之树

QQ 在手机上,您也可以从下面的按钮上点击——它直接打开了您的UPI应用程序. 在桌面上, 扫描上面的 QR 或将 UPI ID 复制到您的支付应用程序中 .

Pay via UPI

成为赞助者

要长期支持项目 还是作为一个公司? 您可以直接赞助软件包 :

每个明星 分享和贡献 都比你知道的多 谢谢你参与这次旅程!

特征

导入什麽需求
@dreamtree-org/integrationsIntegrationError, OAuth 2.0 客户端结束 fetch, OAuth 状态商店, 已封接的连接商店, 内存/ knex 适配器—— 说
@dreamtree-org/integrations/google谷歌 OAuth (英语:createGoogleAuth),驱动(createDriveClient)和日历+相会(createCalendarClient(中文(简体) )googleapis
@dreamtree-org/integrations/zoho区域认识的Zoho OAuth(createZohoAuth),而"佐霍书社"v3(createZohoBooksClient(中文(简体) )—— 说
@dreamtree-org/integrations/mailSMTP 发送( S)createSmtpMailer)和IMAP读取(createImapReader),包含主机,信头和密码守护器nodemailer· ; imapflow + 键 mailparser 用于 IMAP
@dreamtree-org/integrations/slackSlack OAuth v2 bot 安装 (createSlackAuth)和网络API(createSlackClient(中文(简体) )—— 说
@dreamtree-org/integrations/razorpayRazorpay 付款、发票、订阅、合作伙伴OAuth、Webhooks和签名—— 说
@dreamtree-org/integrations/storage数字海洋空间或任何S3兼容商店的私人物体存储(createSpacesStorage),有字节范围和关键守卫@aws-sdk/client-s3

安装

npm install @dreamtree-org/integrations
# plus only the provider SDKs you import, e.g. googleapis or @aws-sdk/client-s3 (nodemailer is included)

设计规则

  • 供货商的客户不储存任何东西. 他们把全权证书当作投入 (单位:千美元)drive.forToken(refreshToken)从来没有读过数据库,所以应用程序 注册 / 测试双模式继续工作 .
  • 一个错误类型 . 每个客户都扔 IntegrationError 带有 code数字 : AUTH_REQUIRED, (中文(简体) ), FORBIDDEN, (中文(简体) ), NOT_FOUND, (中文(简体) ), RATE_LIMITED, (中文(简体) ), RETRYABLE, (中文(简体) ), NOT_CONFIGURED 或者说 UNKNOWN。检查它与 IntegrationError.is(err)没有 instanceof (每个子路径都是自己捆绑的).
  • 证书以租赁方式密封. 连通店收你的 @dreamtree-org/tenancy 特勤服务tenancy.secrets 或者说 createSecretService()) (中文(简体) ). AAD 将环境、公司、行、供应商捆绑在一起 和字段,所以一个密封值复制到另一个公司或字段不会打开.
  • 儒取为懒. 通过配置作为函数; 应用靴子不包含 供应商的证书,以及 status() 名称 缺少.
  • 无硬码主机 . 重定向URI通过每个呼叫,从 请求的来源 。 Google 只接受在 OAuth 客户端注册的 URI, 因此,该列表是主机可以完成登录的限度.

谷歌驱动器

const { createGoogleAuth, createDriveClient, BASE_SCOPES, DRIVE_FILE_SCOPE } = require('@dreamtree-org/integrations/google');

const auth = createGoogleAuth({
  provider: 'google-drive',
  scopes: [...BASE_SCOPES, DRIVE_FILE_SCOPE],
  config: () => ({
    clientId: process.env.GOOGLE_CLIENT_ID,
    clientSecret: process.env.GOOGLE_CLIENT_SECRET,
  }),
});
const drive = createDriveClient({ auth, appProperties: { app: 'taskpro' } });

// The redirect URI comes from the request, so one build serves every host
// (localhost, stage, prod). Behind a proxy, set Express `trust proxy`.
const redirectUri = `${req.protocol}://${req.get('host')}/api/integrations/google-drive/callback`;

auth.status();                                               // { ok, hint }
const url = auth.buildAuthUrl({ state, redirectUri });       // offline access, prompt=consent
const tokens = await auth.exchangeCode(code, { redirectUri }); // same URI; { refreshToken, idToken, scopes, ... }
const who = await auth.verifyIdToken(tokens.idToken); // { sub, email, name }

const d = drive.forToken(refreshToken);         // a fresh OAuth client per operation
const leaf = await d.ensureFolder({ rootId, segments: ['PRO-4', 'BRIT-1'] });
const file = await d.upload({ name, parentId: leaf.id, mimeType, buffer });
const bytes = await d.download(file.id);        // Buffer; downloadWithType() adds mimeType
await d.rename(file.id, 'new name');
await d.remove(file.id);

连接和 OAuth 状态

const { createConnectionStore, createStateStore, knexAdapters, createIntegrationTables } = require('@dreamtree-org/integrations');

await createIntegrationTables(knex);            // integration_connections + integration_oauth_states
const adapters = knexAdapters(knex);            // or knexAdapters(() => currentCompanyKnex())
const connections = createConnectionStore({ adapter: adapters.connections, secrets: tenancy.secrets });
const states = createStateStore({ adapter: adapters.states });

// connect
const state = await states.issue({ companyId, provider: 'google-drive', userId });
res.redirect(auth.buildAuthUrl({ state, redirectUri }));

// callback
const { companyId } = await states.consume(req.query.state, { provider: 'google-drive' }); // one-time, 10 min
const tokens = await auth.exchangeCode(req.query.code, { redirectUri });
await connections.save({
  companyId, provider: 'google-drive',
  account: await auth.verifyIdToken(tokens.idToken),
  scopes: tokens.scopes,
  credentials: { refreshToken: tokens.refreshToken },   // undefined keeps, null clears
});

// use
const conn = await connections.open({ companyId, provider: 'google-drive' });
try {
  await drive.forToken(conn.credentials.refreshToken).upload({ ... });
} catch (err) {
  await connections.markError({ companyId, provider: 'google-drive' }, err); // AUTH_REQUIRED → status "revoked"
  throw err;
}

connections.get() 永远不返回证书,只返回 secrets: { refreshToken: { configured: true, keyId } }所以这是安全的 发送到客户端.

已经有自己的连接表的 Apps 可以保存它们并通过一个 其自己的适配器({ find, insert, update, remove }),或仅使用 供应商客户.

谷歌日历

const { createGoogleAuth, createCalendarClient, BASE_SCOPES, CALENDAR_APP_CREATED_SCOPE } = require('@dreamtree-org/integrations/google');

const auth = createGoogleAuth({ provider: 'google-calendar', scopes: [...BASE_SCOPES, CALENDAR_APP_CREATED_SCOPE], config });
const calendar = createCalendarClient({ auth });

const c = calendar.forToken(refreshToken);
const cal = (await c.getCalendar(storedCalendarId)) || (await c.createCalendar({ name: 'TaskPro' }));
const ev = await c.createEvent({ calendarId: cal.id, event: { summary, start, end, attendees }, withMeet: true });
ev.meetUrl;                                        // the Meet link
await c.patchEvent({ calendarId: cal.id, eventId: ev.id, event: { summary: 'Moved' } }); // keeps the conference
await c.deleteEvent({ calendarId: cal.id, eventId: ev.id });                              // 404/410 = already gone, fine
const busy = await c.listEvents({ timeMin, timeMax });                                    // recurring expanded, all pages
  • 范围. calendar.app.created 是最小的特权: 应用程序创建 一个日历,管理它上的事件。 它不能阅读日历列表,所以 以存储的 ID 查看日历 。 使用 calendar.events 只有工作时 使用用户自己的日历.
  • 参见链接. 每一次带有相遇链接的事件都会得到新的随机请求ID, 因为再利用的id会让同一次会议讨论两个事件.

偷懒

const { createSlackAuth, createSlackClient } = require('@dreamtree-org/integrations/slack');

const auth = createSlackAuth({ config: () => ({ clientId, clientSecret }) }); // bot scopes: chat:write, channels:read, …
res.redirect(auth.buildAuthUrl({ state, redirectUri }));
const install = await auth.exchangeCode(code, { redirectUri }); // { botToken, teamId, teamName, botUserId, scopes }

const slack = createSlackClient().forToken(install.botToken);
const channels = await slack.listChannels();                    // public + private the bot is in, all pages
const who = await slack.lookupUserByEmail('[email protected]');       // null when not a member
await slack.postMessage({ channel, text: who ? `<@${who.id}> done` : 'Ann: done', blocks });
  • 只有瓶装的. 只归还了机器人标志 安装用户的 代号被丢掉,所以没有像一个人.
  • 错误. 黑答 HTTP 200 用 { ok: false } 对于大多数失败, 所以客户端检查 ok失败投出 IntegrationError 与斯拉克的 自有字符串 err.slackError数字 :
    • token_revoked 编号 : invalid_auth → AUTH_REQUIRED
    • missing_scope → FORBIDDEN
    • channel_not_found → NOT_FOUND
    • ratelimited → RATE_LIMITED,与 err.retryAfterMs
  • 复问. 电话不是默认重审的,因为呼叫者通常 从工作重试。 过 retries: 2 等待一个短 Retry-After 改为内线.

佐霍书社

const { createZohoAuth, createZohoBooksClient } = require('@dreamtree-org/integrations/zoho');

const auth = createZohoAuth({ config: () => ({ clientId, clientSecret }) }); // scope ZohoBooks.fullaccess.all
const books = createZohoBooksClient({ auth });

// connect: redirect to the company's data center
res.redirect(auth.buildAuthUrl({ state, redirectUri, dataCenter: 'in' }));

// callback: Zoho appends ?code&accounts-server — the server is validated against
// the known Zoho hosts before the client secret is sent anywhere.
const t = await auth.exchangeCode(req.query.code, { redirectUri, accountsServer: req.query['accounts-server'] });
// store t.refreshToken (sealed), t.accountsServer, t.apiDomain

// use
const b = books.forConnection({
  refreshToken, accountsServer, apiDomain, organizationId,
  accessToken, expiresAt,                     // optional: a stored, still-valid token
  onTokens: (fresh) => saveAccessToken(fresh), // Zoho caps token minting — persist each one
});
const orgs = await b.organizations();
const { contact } = await b.post('contacts', { contact_name: 'Acme' });
const items = await b.listAll('items');       // follows page_context.has_more_page
  • 访问令牌在到期前被缓存并刷新了60 s. 401部队1 刷新一次再试一次 并联通话共享一次刷新.
  • 429(与 Retry-After), Books代码为43, 5xx和连接重审. 每个请求都有30分超时.
  • 错误是 IntegrationError,带有 Books 自己的代码 err.zohoCode。 。 。 . invalid_code, (中文(简体) ), invalid_grant 和第二个401 × AUTH_REQUIRED· ; 代码 57 – FORBIDDEN; 43 / 429 + RATE_LIMITED。 。 。 .

邮件: SMTP 和 IMAP

const { createSmtpMailer, createImapReader, smtpAccountFromSettings } = require('@dreamtree-org/integrations/mail');

const mailer = createSmtpMailer();
const account = smtpAccountFromSettings(resolvedSettings); // tenancy's smtp.* family → { host, port, username, password, from }
await mailer.verify(account);                              // logs in, sends nothing
const r = await mailer.send(account, { from: account.from, to: '[email protected]', subject: 'Hi', text: '…', html: '…' });
// r.ok, r.errorCode ('AUTH_FAILED' | 'RECIPIENT_REFUSED' | …), r.code (IntegrationError code), r.response (scrubbed)

const reader = createImapReader();
const res = await reader.fetch(
  { host: 'imap.gmail.com', port: 993, username, password, allowedHosts: ['imap.gmail.com'] },
  { sinceUid: mailbox.lastUid, uidValidity: mailbox.uidValidity, maxMessages: 50 },
);
// res.messages (parsed), res.highestUid (store it), res.more, res.errorCode === 'UIDVALIDITY_CHANGED' → reset the mark
  • 永不掷出. 邮件呼叫返回结果{ ok, errorCode, code, message }· ; sendBatch 因此,一个死继电器,邮箱或拒绝 收件人不能中止工人勾选 .
  • 宿卫. IP 字节、单一标签名称和非邮件端口总是 拒绝。 过 allowedHosts 缩小范围.
  • TLS政策,没有逃生舱门. Port 465 从一开始就使用 TLS; 所有其他 端口必须使用STARTLS升级,所以密码从不以清文本行走. 验证证书, TLS 1.2 是最小值 .
  • 秘诀. 服务器响应被擦除密码: 按键, 无 空格,和 AUTH LOGIN 和 AUTH PLAIN 所使用的基数64。 平流日志为 关闭, 因为 IMAP 对话包含 LOGIN 密码 .
  • 入入道场. 检查每个地址、主题和标题的CR/LF和 发送前折叠.
  • IMAP 边界 . 阅读受计数,大小和期限的限制. 一个信息 太大了,或不会解析回 作为其信封oversized: true, (中文(简体) ), 所以水印还在进步. AUTH_FAILED 财务报告和审定财务报表 APP_PASSWORD_REQUIRED 为: 单独的代码,因为谷歌报告 既是AUTHENTICATIONFAILED 和他们 需要不同的修正.

剃刀纸

const {
  createRazorpayAuth,
  createRazorpayClient,
  verifyPaymentSignature,
  verifyWebhookSignature,
} = require('@dreamtree-org/integrations/razorpay');

// 1. Direct Merchant Key Auth (standard)
const rzp = createRazorpayClient().forCredentials({
  keyId: process.env.RAZORPAY_KEY_ID,
  keySecret: process.env.RAZORPAY_KEY_SECRET,
});

// Create Order for checkout
const order = await rzp.orders.create({
  amount: 49900, // in paise (₹499.00)
  currency: 'INR',
  receipt: 'rcpt_order_123',
  notes: { companyId: 'cmp_1' },
});

// Capture or Refund Payment
await rzp.payments.capture(paymentId, 49900, 'INR');
await rzp.payments.refund(paymentId, { amount: 10000 });

// Verify Checkout Payment Signature
const isValid = verifyPaymentSignature(
  { orderId: req.body.razorpay_order_id, paymentId: req.body.razorpay_payment_id, signature: req.body.razorpay_signature },
  process.env.RAZORPAY_KEY_SECRET
);

// Verify Webhook Signature (raw body string or Buffer)
const isWebhookValid = verifyWebhookSignature(req.rawBody, req.headers['x-razorpay-signature'], process.env.RAZORPAY_WEBHOOK_SECRET);

// 2. Partner OAuth Flow (connect merchant accounts)
const auth = createRazorpayAuth({
  config: () => ({ clientId: process.env.RAZORPAY_CLIENT_ID, clientSecret: process.env.RAZORPAY_CLIENT_SECRET }),
});
const url = auth.buildAuthUrl({ state, redirectUri });
const tokens = await auth.exchangeCode(code, { redirectUri }); // { accessToken, refreshToken, razorpayAccountId, ... }

// Use Partner Access Token
const partnerClient = createRazorpayClient().forToken(tokens.accessToken);
const merchantOrder = await partnerClient.orders.create({ amount: 10000, currency: 'INR' });
  • 双重认证. 支持标准密钥 ID & 密钥 (HTTP Basic Auth) 和 Partner OAuth Bearer 令牌.
  • 资源覆盖. 提供专用手柄用于 orders, (中文(简体) ), payments, (中文(简体) ), paymentLinks, (中文(简体) ), invoices, (中文(简体) ), plans, (中文(简体) ), subscriptions, (中文(简体) ), customers,以及 refunds。 。 。 .
  • 时常签名. Webhooks和取出签名通过时间安全比较检查,以防止时间攻击.
  • 自动重试 . 429个费率限额(荣誉) Retry-After),网络出错,而上游5xx进行回转.

存储:数字海洋空间

文件的私人对象存储程序只有在它授权查看器后才服务 (上载,广告创意,文档). 与任何S3兼容的商店(Spaces, (原始内容存档于2018-09-29). Minio, R2.

const { createSpacesStorage, spacesConfigFromEnv } = require('@dreamtree-org/integrations/storage');

const store = createSpacesStorage(spacesConfigFromEnv(process.env));
await store.put('creatives/42/ad.mp4', bytes, { contentType: 'video/mp4' });
const size = await store.size('creatives/42/ad.mp4');
const body = await store.stream('creatives/42/ad.mp4', { start: 0, end: 99 }); // a Readable; pipe it to res
await store.remove('creatives/42/ad.mp4');
变量含义
SPACES_BUCKET, (中文(简体) ), SPACES_KEY, (中文(简体) ), SPACES_SECRET要求。 最好只用这桶钥匙.
SPACES_REGION默认 blr1。 。 。 .
SPACES_ENDPOINT默认 https://<region>.digitaloceanspaces.com。 。 。 .
SPACES_FOLDER每个密钥的前缀,所以应用程序和环境可以共享一桶(例如. myapp-stage) (中文(简体) ).
SPACES_FORCE_PATH_STYLE1 给米尼欧的.
  • 每个对象都用 ACL: 'private';没有公开的选择. 提供文件 在授权检查后通过您的应用程序.
  • 键是相对路径. 空密钥, 线索 /,或一个 ., (中文(简体) ), .. 或空块 扔出数(status: 400在任何请求之前.
  • 丢失对象拒绝 IntegrationError NOT_FOUND (单位:千美元)status: 404页:1 失败被归类为每个提供者(AUTH_REQUIRED, (中文(简体) ), FORBIDDEN, (中文(简体) ), RETRYABLE(......) (中文(简体) ).
  • @aws-sdk/client-s3 是一个可选的对等点, 被装入到第一次使用中。 过 client (任何东西) 与 send(command)在测试中注入假药.

无 SDK 供应商的操作权限

const { createOAuthClient } = require('@dreamtree-org/integrations');
const zoho = createOAuthClient({
  provider: 'zoho-books',
  config: () => ({
    authorizeUrl: 'https://accounts.zoho.in/oauth/v2/auth',
    tokenUrl: 'https://accounts.zoho.in/oauth/v2/token',
    clientId, clientSecret, redirectUri,
    scopes: ['ZohoBooks.fullaccess.all'], scopeSeparator: ',',
    authorizeParams: { access_type: 'offline', prompt: 'consent' },
  }),
});
const tokens = await zoho.exchangeCode(code);   // tokens.raw.api_domain kept
const fresh = await zoho.refresh(refreshToken); // invalid_grant → AUTH_REQUIRED

发展

npm install
npm test          # node --test
npm run typecheck
npm run build     # dist/{index,google,zoho,mail,slack,razorpay}.{js,cjs}

捐款

  1. 叉子存储器
  2. 创建您的特性分支( E)git checkout -b feature/amazing-feature(中文(简体) )
  3. 提交更改( E)git commit -m 'Add some amazing feature'(中文(简体) )
  4. 推向分行(git push origin feature/amazing-feature(中文(简体) )
  5. 打开拉请求

许可证

这个项目根据麻省理工学院许可证颁发——见 自由主义 详细文件.

支助


由 Q 制作 普雷塔姆·克里希纳 下级 梦幻之树 • 妇女 www.dreamtreeglobal.com (中文(简体) )

版本与发布记录

@dreamtree-org/integrations 共发布 17 个版本,首次发布于 2026/10/21;过去 12 个月发布了 17 个版本。

版本发布日期距今
0.6.5latest2026/11/13今天
0.6.62026/11/13今天
0.6.72026/11/13今天
0.6.82026/11/13今天
0.6.02026/11/085 天前
0.5.52026/11/0310 天前

其他发布通道:stage → 0.5.0。

关于 @dreamtree-org/integrations 的常见问题

@dreamtree-org/integrations 是什么?

每个Dreamtree产品共享第三方集成——OAuth,被封存的每个公司连接,Google Drive和Calendar,Zoho Books,Slack,SMTP和IMAP,以及私人S3/Spaces对象存储.

如何安装 @dreamtree-org/integrations?

在项目目录运行 npm install @dreamtree-org/integrations,或使用 pnpm add / yarn add / bun add。

@dreamtree-org/integrations 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@dreamtree-org/integrations 支持 ESM 还是 CommonJS?

同时提供 ES Module 与 CommonJS 入口,import 和 require() 都可以使用,打包工具会自动选择合适的版本。

@dreamtree-org/integrations 需要什么版本的 Node.js?

package.json 的 engines 字段要求 Node.js >=18.17。版本不符时 npm 默认只给出警告,开启 engine-strict 后会拒绝安装。

@dreamtree-org/integrations 可以商用吗?

@dreamtree-org/integrations 使用 MIT 许可。宽松许可:允许商业使用、修改和再分发,通常只需在分发时保留原版权与许可声明。以上为概括,不构成法律意见,请以许可原文为准。

@dreamtree-org/integrations 的下载量有多少?

根据 npm 官方下载统计,@dreamtree-org/integrations 在 2026/10/13 – 2026/11/11 这 30 天内被下载 2,845 次,日均约 95 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@dreamtree-org/integrations 还在维护吗?

最新版本 0.6.5 发布于 2026/11/13(今天);至今共发布 17 个版本,其中过去 12 个月发布了 17 个;首次发布于 2026/10/21。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。