index.js 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121
  1. /*
  2. * Copyright (C) 2020 Tencent Cloud.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. 'use strict';
  17. const crypto = require('crypto');
  18. /**
  19. * 插件模块使用情况统计
  20. * @param {object} event - 通过uniCloud.callFunction调用云函数时传入的data对象
  21. * @param {string} event.secretId - 配置的secretId
  22. * @param {string} event.secretKey - 配置的secretKey
  23. * @param {string} event.module - 模块名称
  24. * @param {object} event.extraInfo - 附加信息
  25. * @return {Promise<void>}
  26. */
  27. exports.main = async ({ secretId, secretKey, module, extraInfo }) => {
  28. if (!secretId || !secretKey || !module) {
  29. throw new Error('secretId,secretKey和module不能为空');
  30. }
  31. const db = uniCloud.database();
  32. // 暂时未在DCloud找到“判断表是否存在”的API,故每次都调用createCollection
  33. try {
  34. await db.createCollection('tencentcloud_plugin_report');
  35. } catch (error) {
  36. if (/DATABASE_COLLECTION_ALREADY_EXIST/.test(error.message)) {
  37. // 如果发现表已存在,则忽略错误,继续向下执行
  38. } else {
  39. // 其它错误,包括但不限于(阿里云不支持代码自动创建表,暂不统计),直接抛出异常
  40. throw error;
  41. }
  42. }
  43. const collection = db.collection('tencentcloud_plugin_report');
  44. // 查找出第一条记录
  45. let {
  46. data: [record]
  47. } = await collection.limit(1).get();
  48. // 如果为空则创建一条记录,该记录的主键_id作为数据上报的siteId使用,不同模块的上报共用一个siteId
  49. if (!record) {
  50. const { id } = await collection.add({});
  51. record = { _id: id };
  52. }
  53. // 如果module已存在,说明已经上报过,直接返回
  54. if (record[module]) {
  55. return;
  56. }
  57. // 获取UserUin
  58. const userUin = await getUserUin(secretId, secretKey);
  59. // 调用上报接口,此接口不返回上报状态
  60. await uniCloud.httpclient.request('https://appdata.qq.com/upload', {
  61. method: 'POST',
  62. headers: {
  63. 'Content-Type': 'application/json'
  64. },
  65. data: {
  66. action: 'activate',
  67. plugin_type: module.toLowerCase(),
  68. data: {
  69. site_id: `uniapp_${record._id}`,
  70. site_app: 'uni-app',
  71. site_url: 'uni-app',
  72. uin: userUin,
  73. cust_sec_on: 1,
  74. others: JSON.stringify(extraInfo)
  75. }
  76. }
  77. });
  78. // 保存上报标识,之后不再上报
  79. await collection.doc(record._id).update({
  80. [module]: JSON.stringify(extraInfo)
  81. });
  82. };
  83. // 获取UserUin
  84. async function getUserUin(secretId, secretKey) {
  85. const payloadHash = crypto.createHash('sha256').update('{}').digest('hex');
  86. const requestString = `POST\n/\n\ncontent-type:application/json\nhost:ms.tencentcloudapi.com\n\ncontent-type;host\n${payloadHash}`;
  87. const currentDate = new Date();
  88. const timestamp = `${Math.floor(currentDate.getTime() / 1000)}`;
  89. const dateString = currentDate.toISOString().substr(0, 10);
  90. const requestStringHash = crypto.createHash('sha256').update(requestString).digest('hex');
  91. const stringToSign = `TC3-HMAC-SHA256\n${timestamp}\n${dateString}/ms/tc3_request\n${requestStringHash}`;
  92. const secretDate = crypto.createHmac('sha256', `TC3${secretKey}`).update(dateString).digest();
  93. const secretService = crypto.createHmac('sha256', secretDate).update('ms').digest();
  94. const secretSigning = crypto.createHmac('sha256', secretService).update('tc3_request').digest();
  95. const signature = crypto.createHmac('sha256', secretSigning).update(stringToSign).digest('hex');
  96. const authorization = `TC3-HMAC-SHA256 Credential=${secretId}/${dateString}/ms/tc3_request, SignedHeaders=content-type;host, Signature=${signature}`;
  97. const { res } = await uniCloud.httpclient.request('https://ms.tencentcloudapi.com', {
  98. method: 'POST',
  99. headers: {
  100. 'Content-Type': 'application/json',
  101. 'X-TC-Action': 'DescribeUserBaseInfoInstance',
  102. 'X-TC-Version': '2018-04-08',
  103. 'X-TC-Timestamp': timestamp,
  104. Authorization: authorization
  105. },
  106. data: {},
  107. dataType: 'json'
  108. });
  109. const { status, statusMessage, data } = res;
  110. if (status !== 200) {
  111. throw new Error('获取UserUin失败');
  112. }
  113. if (data.Response.Error) {
  114. throw new Error(data.Response.Error.Message);
  115. }
  116. return data.Response.UserUin;
  117. }