growi-to-slack.ts 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134
  1. import {
  2. Controller, Get, Inject, Req, Res, UseBefore,
  3. } from '@tsed/common';
  4. import axios from 'axios';
  5. import { WebAPICallResult } from '@slack/web-api';
  6. import { verifyGrowiToSlackRequest, getConnectionStatuses, relationTestToSlack } from '@growi/slack';
  7. import { GrowiReq } from '~/interfaces/growi-to-slack/growi-req';
  8. import { InstallationRepository } from '~/repositories/installation';
  9. import { RelationRepository } from '~/repositories/relation';
  10. import { OrderRepository } from '~/repositories/order';
  11. import { InstallerService } from '~/services/InstallerService';
  12. import loggerFactory from '~/utils/logger';
  13. const logger = loggerFactory('slackbot-proxy:controllers:growi-to-slack');
  14. @Controller('/g2s')
  15. export class GrowiToSlackCtrl {
  16. @Inject()
  17. installerService: InstallerService;
  18. @Inject()
  19. installationRepository: InstallationRepository;
  20. @Inject()
  21. relationRepository: RelationRepository;
  22. @Inject()
  23. orderRepository: OrderRepository;
  24. @Get('/connection-status')
  25. @UseBefore(verifyGrowiToSlackRequest)
  26. async getConnectionStatuses(@Req() req: GrowiReq, @Res() res: Res): Promise<void|string|Res|WebAPICallResult> {
  27. // asserted (tokenGtoPs.length > 0) by verifyGrowiToSlackRequest
  28. const { tokenGtoPs } = req;
  29. // retrieve Relation with Installation
  30. const relations = await this.relationRepository.createQueryBuilder('relation')
  31. .where('relation.tokenGtoP IN (:...tokens)', { tokens: tokenGtoPs })
  32. .leftJoinAndSelect('relation.installation', 'installation')
  33. .getMany();
  34. logger.debug(`${relations.length} relations found`, relations);
  35. // extract bot token
  36. const tokens: string[] = relations
  37. .map(relation => relation.installation?.data?.bot?.token)
  38. .filter((v): v is string => v != null); // filter out null values
  39. const connectionStatuses = await getConnectionStatuses(tokens);
  40. return res.send({ connectionStatuses });
  41. }
  42. @Get('/relation-test')
  43. @UseBefore(verifyGrowiToSlackRequest)
  44. async postRelation(@Req() req: GrowiReq, @Res() res: Res): Promise<void|string|Res|WebAPICallResult> {
  45. const { tokenGtoPs } = req;
  46. if (tokenGtoPs.length !== 1) {
  47. return res.status(400).send({ message: 'installation is invalid' });
  48. }
  49. const tokenGtoP = tokenGtoPs[0];
  50. // retrieve relation with Installation
  51. const relation = await this.relationRepository.createQueryBuilder('relation')
  52. .where('tokenGtoP = :token', { token: tokenGtoP })
  53. .leftJoinAndSelect('relation.installation', 'installation')
  54. .getOne();
  55. // Returns the result of the test if it already exists
  56. if (relation != null) {
  57. logger.debug('relation found', relation);
  58. const token = relation.installation.data.bot?.token;
  59. if (token == null) {
  60. return res.status(400).send({ message: 'installation is invalid' });
  61. }
  62. await relationTestToSlack(token);
  63. return res.send({ relation });
  64. }
  65. // retrieve latest Order with Installation
  66. const order = await this.orderRepository.createQueryBuilder('order')
  67. .orderBy('order.createdAt', 'DESC')
  68. .where('growiAccessToken = :token', { token: tokenGtoP })
  69. .leftJoinAndSelect('order.installation', 'installation')
  70. .getOne();
  71. if (order == null || order.isExpired()) {
  72. return res.status(400).send({ message: 'order has expired or does not exist.' });
  73. }
  74. // Access the GROWI URL saved in the Order record and check if the GtoP token is valid.
  75. try {
  76. const url = new URL('/_api/v3/slack-integration/proxied/commands', order.growiUrl);
  77. await axios.post(url.toString(), {
  78. type: 'url_verification',
  79. tokenPtoG: order.growiAccessToken,
  80. challenge: 'this_is_my_challenge_token',
  81. });
  82. }
  83. catch (err) {
  84. logger.error(err);
  85. }
  86. logger.debug('order found', order);
  87. const token = order.installation.data.bot?.token;
  88. if (token == null) {
  89. return res.status(400).send({ message: 'installation is invalid' });
  90. }
  91. await relationTestToSlack(token);
  92. logger.debug('relation test is success', order);
  93. // Transaction is not considered because it is used infrequently,
  94. const createdRelation = await this.relationRepository.save({
  95. installation: order.installation, tokenGtoP: order.growiAccessToken, tokenPtoG: order.proxyAccessToken, growiUri: order.growiUrl,
  96. });
  97. return res.send({ relation: createdRelation });
  98. }
  99. }