@larksuiteoapi/node-sdk 1.2.3 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -403,6 +403,32 @@ const resule = await dispatcher.invoke(data);
403
403
  server.sendResult(result);
404
404
  ````
405
405
 
406
+ #### challenge校验
407
+ When configuring the event request address, The Open Platform will push a POST request in `application/json` format to the request address. The POST request is used to verify the validity of the configured request address, and the request body will carry a `challenge` field , **The application needs to return the received challenge value to the Open Platform within 1 second**. See: [ Document](https://open.feishu.cn/document/ukTMukTMukTM/uYDNxYjL2QTM24iN0EjN/event-subscription-configure-/request-url-configuration-case)
408
+
409
+ The adapter provided by the sdk above encapsulates the verification logic. Set the `autoChallenge` field in the options parameter to true to enable:
410
+ ```typescript
411
+ // adaptDefault
412
+ lark.adaptDefault('/webhook/event', eventDispatcher, {
413
+ autoChallenge: true,
414
+ });
415
+ // express
416
+ lark.adaptExpress(eventDispatcher, {
417
+ autoChallenge: true,
418
+ });
419
+ // koa
420
+ lark.adaptKoa('/webhook/event', eventDispatcher, {
421
+ autoChallenge: true,
422
+ });
423
+ // koa-router
424
+ router.post(
425
+ '/webhook/event',
426
+ lark.adaptKoaRouter(eventDispatcher, {
427
+ autoChallenge: true,
428
+ })
429
+ );
430
+ ```
431
+
406
432
  ### [Message Card](https://open.feishu.cn/document/ukTMukTMukTM/uczM3QjL3MzN04yNzcDN)
407
433
  The processing of the Message Card is also a kind of Event processing. The only difference between the two is that the processor of the Message Card is used to respond to the events generated by the interaction between the user and the Message Card. If the processor has a return value (*the value structure should be in line with the structure defined by [Message Card Structure](https://open.feishu.cn/document/ukTMukTMukTM/uEjNwUjLxYDM14SM2ATN)*), then the return value is used to update the responded message card:
408
434
 
@@ -443,6 +469,10 @@ import * as lark from '@larksuiteoapi/node-sdk';
443
469
 
444
470
  new lark.AESCipher('encrypt key').decrypt('content');
445
471
  ````
472
+
473
+ ## Blog
474
+ [ISV Application Development Guide](https://bytedance.feishu.cn/docx/RUZKdGwdyoH4KexMJgDcITQnn0b)
475
+
446
476
  ## LICENSE
447
477
  MIT
448
478
 
package/README.zh.md CHANGED
@@ -402,6 +402,32 @@ const resule = await dispatcher.invoke(data);
402
402
  server.sendResult(result);
403
403
  ```
404
404
 
405
+ #### challenge校验
406
+ 在配置事件请求地址时,开放平台会向请求地址推送一个`application/json`格式的 POST请求,该POST请求用于验证所配置的请求地址的合法性,请求体中会携带一个`challenge`字段,**应用需要在 1 秒内,将接收到的challenge值原样返回给飞书开放平台**。详见:[文档](https://open.feishu.cn/document/ukTMukTMukTM/uYDNxYjL2QTM24iN0EjN/event-subscription-configure-/request-url-configuration-case)
407
+
408
+ 上面sdk提供出的适配器内部封装了这部分验证的逻辑,将options参数中的`autoChallenge`字段设为true即可启用:
409
+ ```typescript
410
+ // adaptDefault
411
+ lark.adaptDefault('/webhook/event', eventDispatcher, {
412
+ autoChallenge: true,
413
+ });
414
+ // express
415
+ lark.adaptExpress(eventDispatcher, {
416
+ autoChallenge: true,
417
+ });
418
+ // koa
419
+ lark.adaptKoa('/webhook/event', eventDispatcher, {
420
+ autoChallenge: true,
421
+ });
422
+ // koa-router
423
+ router.post(
424
+ '/webhook/event',
425
+ lark.adaptKoaRouter(eventDispatcher, {
426
+ autoChallenge: true,
427
+ })
428
+ );
429
+ ```
430
+
405
431
  ### [消息卡片](https://open.feishu.cn/document/ukTMukTMukTM/uczM3QjL3MzN04yNzcDN)
406
432
  对消息卡片的处理亦是对事件处理的一种,两者的不同点仅在于消息卡片的处理器用于响应用户与消息卡片交互所产生的事件,若处理器有返回值(*返回值的数据结构理应为符合[消息卡片结构](https://open.feishu.cn/document/ukTMukTMukTM/uEjNwUjLxYDM14SM2ATN)所定义的结构*),则返回值被用来更新被响应的消息卡片:
407
433
 
@@ -442,6 +468,10 @@ import * as lark from '@larksuiteoapi/node-sdk';
442
468
 
443
469
  new lark.AESCipher('encrypt key').decrypt('content');
444
470
  ```
471
+
472
+ ## Blog
473
+ [ISV(商店应用)开发指南](https://bytedance.feishu.cn/docx/RUZKdGwdyoH4KexMJgDcITQnn0b)
474
+
445
475
  ## 许可协议
446
476
  MIT
447
477
 
package/es/index.js CHANGED
@@ -19214,13 +19214,40 @@ const pickRequestData = (req) => new Promise((resolve) => {
19214
19214
  });
19215
19215
  });
19216
19216
 
19217
- const adaptDefault = (path, dispatcher) => (req, res) => __awaiter(void 0, void 0, void 0, function* () {
19217
+ const generateChallenge = (data, options) => {
19218
+ if ('encrypt' in data && !options.encryptKey) {
19219
+ throw new Error('auto-challenge need encryptKey, please check for missing in dispatcher');
19220
+ }
19221
+ const targetData = 'encrypt' in data
19222
+ ? JSON.parse(new AESCipher(options.encryptKey).decrypt(data.encrypt))
19223
+ : data;
19224
+ return {
19225
+ isChallenge: targetData.type === 'url_verification',
19226
+ challenge: {
19227
+ challenge: targetData.challenge,
19228
+ },
19229
+ };
19230
+ };
19231
+
19232
+ const adaptDefault = (path, dispatcher, options) => (req, res) => __awaiter(void 0, void 0, void 0, function* () {
19218
19233
  if (req.url !== path) {
19219
19234
  return;
19220
19235
  }
19221
19236
  const data = Object.assign(Object.create({
19222
19237
  headers: req.headers,
19223
19238
  }), yield pickRequestData(req));
19239
+ // 是否自动响应challange事件:
19240
+ // https://open.feishu.cn/document/ukTMukTMukTM/uYDNxYjL2QTM24iN0EjN/event-subscription-configure-/request-url-configuration-case
19241
+ const autoChallenge = get(options, 'autoChallenge', false);
19242
+ if (autoChallenge) {
19243
+ const { isChallenge, challenge } = generateChallenge(data, {
19244
+ encryptKey: dispatcher.encryptKey,
19245
+ });
19246
+ if (isChallenge) {
19247
+ res.end(JSON.stringify(challenge));
19248
+ return;
19249
+ }
19250
+ }
19224
19251
  const value = yield dispatcher.invoke(data);
19225
19252
  // event don't need response
19226
19253
  if (dispatcher instanceof CardActionHandler) {
@@ -19244,6 +19271,18 @@ const adaptExpress = (dispatcher, options) => (req, res) => __awaiter(void 0, vo
19244
19271
  const data = Object.assign(Object.create({
19245
19272
  headers: req.headers,
19246
19273
  }), reqData);
19274
+ // 是否自动响应challange事件:
19275
+ // https://open.feishu.cn/document/ukTMukTMukTM/uYDNxYjL2QTM24iN0EjN/event-subscription-configure-/request-url-configuration-case
19276
+ const autoChallenge = get(options, 'autoChallenge', false);
19277
+ if (autoChallenge) {
19278
+ const { isChallenge, challenge } = generateChallenge(data, {
19279
+ encryptKey: dispatcher.encryptKey,
19280
+ });
19281
+ if (isChallenge) {
19282
+ res.json(challenge);
19283
+ return;
19284
+ }
19285
+ }
19247
19286
  const value = yield dispatcher.invoke(data);
19248
19287
  // event don't need response
19249
19288
  if (dispatcher instanceof CardActionHandler) {
@@ -19269,6 +19308,19 @@ const adaptKoa = (path, dispatcher, options) => (ctx, next) => __awaiter(void 0,
19269
19308
  const data = Object.assign(Object.create({
19270
19309
  headers: req.headers,
19271
19310
  }), reqData);
19311
+ // 是否自动响应challange事件:
19312
+ // https://open.feishu.cn/document/ukTMukTMukTM/uYDNxYjL2QTM24iN0EjN/event-subscription-configure-/request-url-configuration-case
19313
+ const autoChallenge = get(options, 'autoChallenge', false);
19314
+ if (autoChallenge) {
19315
+ const { isChallenge, challenge } = generateChallenge(data, {
19316
+ encryptKey: dispatcher.encryptKey,
19317
+ });
19318
+ if (isChallenge) {
19319
+ ctx.body = challenge;
19320
+ yield next();
19321
+ return;
19322
+ }
19323
+ }
19272
19324
  const value = yield dispatcher.invoke(data);
19273
19325
  // event don't need response
19274
19326
  if (dispatcher instanceof CardActionHandler) {
@@ -19297,6 +19349,19 @@ const adaptKoaRouter = (dispatcher, options) => (ctx, next) => __awaiter(void 0,
19297
19349
  const data = Object.assign(Object.create({
19298
19350
  headers: req.headers,
19299
19351
  }), reqData);
19352
+ // 是否自动响应challange事件:
19353
+ // https://open.feishu.cn/document/ukTMukTMukTM/uYDNxYjL2QTM24iN0EjN/event-subscription-configure-/request-url-configuration-case
19354
+ const autoChallenge = get(options, 'autoChallenge', false);
19355
+ if (autoChallenge) {
19356
+ const { isChallenge, challenge } = generateChallenge(data, {
19357
+ encryptKey: dispatcher.encryptKey,
19358
+ });
19359
+ if (isChallenge) {
19360
+ ctx.body = challenge;
19361
+ yield next();
19362
+ return;
19363
+ }
19364
+ }
19300
19365
  const value = yield dispatcher.invoke(data);
19301
19366
  // event don't need response
19302
19367
  if (dispatcher instanceof CardActionHandler) {
package/lib/index.js CHANGED
@@ -19229,13 +19229,40 @@ const pickRequestData = (req) => new Promise((resolve) => {
19229
19229
  });
19230
19230
  });
19231
19231
 
19232
- const adaptDefault = (path, dispatcher) => (req, res) => __awaiter(void 0, void 0, void 0, function* () {
19232
+ const generateChallenge = (data, options) => {
19233
+ if ('encrypt' in data && !options.encryptKey) {
19234
+ throw new Error('auto-challenge need encryptKey, please check for missing in dispatcher');
19235
+ }
19236
+ const targetData = 'encrypt' in data
19237
+ ? JSON.parse(new AESCipher(options.encryptKey).decrypt(data.encrypt))
19238
+ : data;
19239
+ return {
19240
+ isChallenge: targetData.type === 'url_verification',
19241
+ challenge: {
19242
+ challenge: targetData.challenge,
19243
+ },
19244
+ };
19245
+ };
19246
+
19247
+ const adaptDefault = (path, dispatcher, options) => (req, res) => __awaiter(void 0, void 0, void 0, function* () {
19233
19248
  if (req.url !== path) {
19234
19249
  return;
19235
19250
  }
19236
19251
  const data = Object.assign(Object.create({
19237
19252
  headers: req.headers,
19238
19253
  }), yield pickRequestData(req));
19254
+ // 是否自动响应challange事件:
19255
+ // https://open.feishu.cn/document/ukTMukTMukTM/uYDNxYjL2QTM24iN0EjN/event-subscription-configure-/request-url-configuration-case
19256
+ const autoChallenge = get__default["default"](options, 'autoChallenge', false);
19257
+ if (autoChallenge) {
19258
+ const { isChallenge, challenge } = generateChallenge(data, {
19259
+ encryptKey: dispatcher.encryptKey,
19260
+ });
19261
+ if (isChallenge) {
19262
+ res.end(JSON.stringify(challenge));
19263
+ return;
19264
+ }
19265
+ }
19239
19266
  const value = yield dispatcher.invoke(data);
19240
19267
  // event don't need response
19241
19268
  if (dispatcher instanceof CardActionHandler) {
@@ -19259,6 +19286,18 @@ const adaptExpress = (dispatcher, options) => (req, res) => __awaiter(void 0, vo
19259
19286
  const data = Object.assign(Object.create({
19260
19287
  headers: req.headers,
19261
19288
  }), reqData);
19289
+ // 是否自动响应challange事件:
19290
+ // https://open.feishu.cn/document/ukTMukTMukTM/uYDNxYjL2QTM24iN0EjN/event-subscription-configure-/request-url-configuration-case
19291
+ const autoChallenge = get__default["default"](options, 'autoChallenge', false);
19292
+ if (autoChallenge) {
19293
+ const { isChallenge, challenge } = generateChallenge(data, {
19294
+ encryptKey: dispatcher.encryptKey,
19295
+ });
19296
+ if (isChallenge) {
19297
+ res.json(challenge);
19298
+ return;
19299
+ }
19300
+ }
19262
19301
  const value = yield dispatcher.invoke(data);
19263
19302
  // event don't need response
19264
19303
  if (dispatcher instanceof CardActionHandler) {
@@ -19284,6 +19323,19 @@ const adaptKoa = (path, dispatcher, options) => (ctx, next) => __awaiter(void 0,
19284
19323
  const data = Object.assign(Object.create({
19285
19324
  headers: req.headers,
19286
19325
  }), reqData);
19326
+ // 是否自动响应challange事件:
19327
+ // https://open.feishu.cn/document/ukTMukTMukTM/uYDNxYjL2QTM24iN0EjN/event-subscription-configure-/request-url-configuration-case
19328
+ const autoChallenge = get__default["default"](options, 'autoChallenge', false);
19329
+ if (autoChallenge) {
19330
+ const { isChallenge, challenge } = generateChallenge(data, {
19331
+ encryptKey: dispatcher.encryptKey,
19332
+ });
19333
+ if (isChallenge) {
19334
+ ctx.body = challenge;
19335
+ yield next();
19336
+ return;
19337
+ }
19338
+ }
19287
19339
  const value = yield dispatcher.invoke(data);
19288
19340
  // event don't need response
19289
19341
  if (dispatcher instanceof CardActionHandler) {
@@ -19312,6 +19364,19 @@ const adaptKoaRouter = (dispatcher, options) => (ctx, next) => __awaiter(void 0,
19312
19364
  const data = Object.assign(Object.create({
19313
19365
  headers: req.headers,
19314
19366
  }), reqData);
19367
+ // 是否自动响应challange事件:
19368
+ // https://open.feishu.cn/document/ukTMukTMukTM/uYDNxYjL2QTM24iN0EjN/event-subscription-configure-/request-url-configuration-case
19369
+ const autoChallenge = get__default["default"](options, 'autoChallenge', false);
19370
+ if (autoChallenge) {
19371
+ const { isChallenge, challenge } = generateChallenge(data, {
19372
+ encryptKey: dispatcher.encryptKey,
19373
+ });
19374
+ if (isChallenge) {
19375
+ ctx.body = challenge;
19376
+ yield next();
19377
+ return;
19378
+ }
19379
+ }
19315
19380
  const value = yield dispatcher.invoke(data);
19316
19381
  // event don't need response
19317
19382
  if (dispatcher instanceof CardActionHandler) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@larksuiteoapi/node-sdk",
3
- "version": "1.2.3",
3
+ "version": "1.3.0",
4
4
  "description": "larksuite open sdk for nodejs",
5
5
  "keywords": [
6
6
  "feishu",
@@ -37,26 +37,31 @@
37
37
  "lodash.pickby": "^4.6.0"
38
38
  },
39
39
  "devDependencies": {
40
- "@typescript-eslint/parser": "^5.27.1",
40
+ "@koa/router": "^12.0.0",
41
41
  "@rollup/plugin-alias": "^3.1.9",
42
42
  "@rollup/plugin-typescript": "^8.3.2",
43
43
  "@types/jest": "^28.1.3",
44
44
  "@types/lodash.get": "^4.4.7",
45
45
  "@types/lodash.merge": "^4.6.7",
46
46
  "@types/lodash.pick": "^4.4.7",
47
+ "@typescript-eslint/parser": "^5.27.1",
48
+ "body-parser": "^1.20.1",
47
49
  "eslint": "^8.17.0",
48
50
  "eslint-config-airbnb-base": "^15.0.0",
49
51
  "eslint-config-prettier": "^8.5.0",
50
52
  "eslint-plugin-import": "^2.26.0",
51
- "prettier": "2.6.2",
53
+ "express": "^4.18.2",
52
54
  "jest": "^28.1.1",
55
+ "koa": "^2.13.4",
56
+ "koa-body": "^5.0.0",
57
+ "prettier": "2.6.2",
53
58
  "rollup": "^2.75.5",
54
59
  "rollup-plugin-dts": "^4.2.2",
55
60
  "rollup-plugin-license": "^2.8.1",
56
61
  "ts-jest": "^28.0.5",
62
+ "ts-node": "^10.8.1",
57
63
  "tsconfig-paths": "^4.0.0",
58
64
  "tslib": "^2.4.0",
59
- "ts-node": "^10.8.1",
60
65
  "typescript": "^4.7.3"
61
66
  },
62
67
  "publishConfig": {
package/types/index.d.ts CHANGED
@@ -39443,18 +39443,23 @@ declare class CardActionHandler {
39443
39443
  invoke(data: any): Promise<any>;
39444
39444
  }
39445
39445
 
39446
- declare const adaptDefault: (path: string, dispatcher: EventDispatcher | CardActionHandler) => (req: any, res: any) => Promise<void>;
39446
+ declare const adaptDefault: (path: string, dispatcher: EventDispatcher | CardActionHandler, options?: {
39447
+ autoChallenge?: boolean;
39448
+ }) => (req: any, res: any) => Promise<void>;
39447
39449
 
39448
39450
  declare const adaptExpress: (dispatcher: EventDispatcher | CardActionHandler, options?: {
39449
39451
  logger?: Logger;
39452
+ autoChallenge?: boolean;
39450
39453
  }) => (req: any, res: any) => Promise<void>;
39451
39454
 
39452
39455
  declare const adaptKoa: (path: string, dispatcher: EventDispatcher | CardActionHandler, options?: {
39453
39456
  logger?: Logger;
39457
+ autoChallenge?: boolean;
39454
39458
  }) => (ctx: any, next: any) => Promise<void>;
39455
39459
 
39456
39460
  declare const adaptKoaRouter: (dispatcher: EventDispatcher | CardActionHandler, options?: {
39457
39461
  logger?: Logger;
39462
+ autoChallenge?: boolean;
39458
39463
  }) => (ctx: any, next: any) => Promise<void>;
39459
39464
 
39460
39465
  export { AESCipher, AppType, CAppTicket, CardActionHandler, Client, Domain, EventDispatcher, IHandles as EventHandles, LoggerLevel, adaptDefault, adaptExpress, adaptKoa, adaptKoaRouter, withAll, withHelpDeskCredential, withTenantKey, withTenantToken, withUserAccessToken };