qiniu查看kodo对象的status功能实现
This commit is contained in:
parent
5a07ee4b87
commit
216a215095
|
|
@ -36,6 +36,10 @@ export declare class QiniuCloudInstance {
|
|||
streamKey: string;
|
||||
expireAt: number;
|
||||
}>;
|
||||
/**
|
||||
* https://developer.qiniu.com/kodo/1308/stat
|
||||
*/
|
||||
getKodoStat(bucket: string, key: string): Promise<any>;
|
||||
/**
|
||||
* 计算直播流地址相关信息
|
||||
* @param publishDomain
|
||||
|
|
@ -57,7 +61,24 @@ export declare class QiniuCloudInstance {
|
|||
expireAt: number;
|
||||
};
|
||||
getPlayBackUrl(hub: string, playBackDomain: string, streamTitle: string, start: number, end: number, method: 'GET' | 'POST' | 'PUT' | 'DELETE', host: string, rawQuery?: string): Promise<string>;
|
||||
private getToken;
|
||||
/**
|
||||
* 管理端访问七牛云服务器
|
||||
* @param path
|
||||
* @param method
|
||||
* @param headers
|
||||
* @param body
|
||||
*/
|
||||
private access;
|
||||
/**
|
||||
* https://developer.qiniu.com/kodo/1208/upload-token
|
||||
* @param scope
|
||||
* @returns
|
||||
*/
|
||||
private generateKodoUploadToken;
|
||||
/**
|
||||
* https://developer.qiniu.com/kodo/1201/access-token
|
||||
*/
|
||||
private genernateKodoAccessToken;
|
||||
private base64ToUrlSafe;
|
||||
private hmacSha1;
|
||||
private urlSafeBase64Encode;
|
||||
|
|
|
|||
|
|
@ -2,6 +2,9 @@ require('../../fetch');
|
|||
import crypto from 'crypto';
|
||||
import { Md5 } from 'ts-md5';
|
||||
import { Buffer } from 'buffer';
|
||||
import { stringify } from 'querystring';
|
||||
import { OakExternalException, OakNetworkException } from 'oak-domain/lib/types/Exception';
|
||||
const QINIU_CLOUD_HOST = 'rs.qiniuapi.com';
|
||||
export class QiniuCloudInstance {
|
||||
accessKey;
|
||||
secretKey;
|
||||
|
|
@ -20,7 +23,7 @@ export class QiniuCloudInstance {
|
|||
getUploadInfo(uploadHost, bucket, key) {
|
||||
try {
|
||||
const scope = key ? `${bucket}:${key}` : bucket;
|
||||
const uploadToken = this.getToken(scope);
|
||||
const uploadToken = this.generateKodoUploadToken(scope);
|
||||
return {
|
||||
key,
|
||||
uploadToken,
|
||||
|
|
@ -89,6 +92,18 @@ export class QiniuCloudInstance {
|
|||
const obj = this.getStreamObj(publishDomain, playDomain, hub, publishKey, playKey, streamTitle, expireAt);
|
||||
return obj;
|
||||
}
|
||||
/**
|
||||
* https://developer.qiniu.com/kodo/1308/stat
|
||||
*/
|
||||
async getKodoStat(bucket, key) {
|
||||
const entry = `${bucket}:${key}`;
|
||||
const encodedEntryURI = this.urlSafeBase64Encode(entry);
|
||||
const path = `/stat/${encodedEntryURI}`;
|
||||
const result = await this.access(path, undefined, 'Get', {
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
});
|
||||
return result;
|
||||
}
|
||||
/**
|
||||
* 计算直播流地址相关信息
|
||||
* @param publishDomain
|
||||
|
|
@ -146,7 +161,60 @@ export class QiniuCloudInstance {
|
|||
});
|
||||
return `https://${playBackDomain}/${streamTitle}.m3u8`;
|
||||
}
|
||||
getToken(scope) {
|
||||
/**
|
||||
* 管理端访问七牛云服务器
|
||||
* @param path
|
||||
* @param method
|
||||
* @param headers
|
||||
* @param body
|
||||
*/
|
||||
async access(path, query, method, headers, body) {
|
||||
const contentType = headers && headers['Content-Type'];
|
||||
const url = new URL(`https://${QINIU_CLOUD_HOST}${path}`);
|
||||
if (query) {
|
||||
url.search = typeof query === 'object' ? stringify(query) : query;
|
||||
}
|
||||
const accessToken = this.genernateKodoAccessToken(method || 'Get', QINIU_CLOUD_HOST, path, undefined, contentType);
|
||||
let response;
|
||||
try {
|
||||
response = await fetch(`https://${QINIU_CLOUD_HOST}${path}`, {
|
||||
method,
|
||||
headers: {
|
||||
'Authorization': `Qiniu ${accessToken}`,
|
||||
...headers,
|
||||
},
|
||||
body,
|
||||
});
|
||||
}
|
||||
catch (err) {
|
||||
// fetch返回异常一定是网络异常
|
||||
throw new OakNetworkException();
|
||||
}
|
||||
const responseType = response.headers.get('Content-Type') || response.headers.get('content-type');
|
||||
if (responseType?.toLocaleLowerCase().match(/application\/json/i)) {
|
||||
const json = await response.json();
|
||||
if (response.status > 299) {
|
||||
// 七牛服务器返回异常,根据文档一定是json
|
||||
// https://developer.qiniu.com/kodo/3928/error-responses
|
||||
const { code, error } = json;
|
||||
return new OakExternalException('qiniu', code, error);
|
||||
}
|
||||
return json;
|
||||
}
|
||||
else if (responseType?.toLocaleLowerCase().match(/application\/octet-stream/i)) {
|
||||
const result = await response.arrayBuffer();
|
||||
return result;
|
||||
}
|
||||
else {
|
||||
throw new Error(`尚不支持的content-type类型${responseType}`);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* https://developer.qiniu.com/kodo/1208/upload-token
|
||||
* @param scope
|
||||
* @returns
|
||||
*/
|
||||
generateKodoUploadToken(scope) {
|
||||
// 构造策略
|
||||
const putPolicy = {
|
||||
scope: scope,
|
||||
|
|
@ -159,6 +227,31 @@ export class QiniuCloudInstance {
|
|||
const uploadToken = this.accessKey + ':' + encodedSign + ':' + encodedFlags;
|
||||
return uploadToken;
|
||||
}
|
||||
/**
|
||||
* https://developer.qiniu.com/kodo/1201/access-token
|
||||
*/
|
||||
genernateKodoAccessToken(method, host, path, query, contentType, body, xHeaders) {
|
||||
let signingStr = method + ' ' + path;
|
||||
if (query) {
|
||||
signingStr += '?' + query;
|
||||
}
|
||||
signingStr += '\nHost: ' + host;
|
||||
if (contentType) {
|
||||
signingStr += '\nContent-Type: ' + contentType;
|
||||
}
|
||||
if (xHeaders) {
|
||||
const ks = Object.keys(xHeaders);
|
||||
ks.sort((e1, e2) => e1 < e2 ? -1 : 1);
|
||||
ks.forEach((k) => signingStr += `\n${k}: ${xHeaders[k]}`);
|
||||
}
|
||||
signingStr += '\n\n';
|
||||
if (body) {
|
||||
signingStr += body;
|
||||
}
|
||||
const sign = this.hmacSha1(signingStr, this.secretKey);
|
||||
const encodedSign = this.urlSafeBase64Encode(sign);
|
||||
return `${this.accessKey}:${encodedSign}`;
|
||||
}
|
||||
base64ToUrlSafe(v) {
|
||||
return v.replace(/\//g, '_').replace(/\+/g, '-');
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,8 @@ import crypto from 'crypto';
|
|||
import { Buffer } from 'buffer';
|
||||
import URL from 'url';
|
||||
import FormData from 'form-data';
|
||||
import { OakNetworkException, OakServerProxyException } from 'oak-domain/lib/types/Exception';
|
||||
import { OakExternalException, OakNetworkException, OakServerProxyException } from 'oak-domain/lib/types/Exception';
|
||||
import assert from 'assert';
|
||||
export class WechatPublicInstance {
|
||||
appId;
|
||||
appSecret;
|
||||
|
|
@ -54,7 +55,7 @@ export class WechatPublicInstance {
|
|||
if ([40001, 42001].includes(json.errcode)) {
|
||||
return this.refreshAccessToken(url, init);
|
||||
}
|
||||
throw new Error(`调用微信接口返回出错,code是${json.errcode},信息是${json.errmsg}`);
|
||||
throw new OakExternalException('wechatPublic', json.errcode, json.errmsg);
|
||||
}
|
||||
return json;
|
||||
}
|
||||
|
|
@ -70,7 +71,7 @@ export class WechatPublicInstance {
|
|||
if ([40001, 42001].includes(json.errcode)) {
|
||||
return this.refreshAccessToken(url, init);
|
||||
}
|
||||
throw new Error(`调用微信接口返回出错,code是${json.errcode},信息是${json.errmsg}`);
|
||||
throw new OakExternalException('wechatPublic', json.errcode, json.errmsg);
|
||||
}
|
||||
return json;
|
||||
}
|
||||
|
|
@ -259,9 +260,7 @@ export class WechatPublicInstance {
|
|||
}
|
||||
async getQrCode(options) {
|
||||
const { sceneId, sceneStr, expireSeconds, isPermanent } = options;
|
||||
if (!sceneId && !sceneStr) {
|
||||
throw new Error('Missing sceneId or sceneStr');
|
||||
}
|
||||
assert(sceneId || sceneStr);
|
||||
const scene = sceneId
|
||||
? {
|
||||
scene_id: sceneId,
|
||||
|
|
@ -396,7 +395,7 @@ export class WechatPublicInstance {
|
|||
break;
|
||||
}
|
||||
default: {
|
||||
throw new Error('当前消息类型暂不支持');
|
||||
assert(false, '当前消息类型暂不支持');
|
||||
}
|
||||
}
|
||||
const token = await this.getAccessToken();
|
||||
|
|
|
|||
|
|
@ -36,6 +36,10 @@ export declare class QiniuCloudInstance {
|
|||
streamKey: string;
|
||||
expireAt: number;
|
||||
}>;
|
||||
/**
|
||||
* https://developer.qiniu.com/kodo/1308/stat
|
||||
*/
|
||||
getKodoStat(bucket: string, key: string): Promise<any>;
|
||||
/**
|
||||
* 计算直播流地址相关信息
|
||||
* @param publishDomain
|
||||
|
|
@ -57,7 +61,24 @@ export declare class QiniuCloudInstance {
|
|||
expireAt: number;
|
||||
};
|
||||
getPlayBackUrl(hub: string, playBackDomain: string, streamTitle: string, start: number, end: number, method: 'GET' | 'POST' | 'PUT' | 'DELETE', host: string, rawQuery?: string): Promise<string>;
|
||||
private getToken;
|
||||
/**
|
||||
* 管理端访问七牛云服务器
|
||||
* @param path
|
||||
* @param method
|
||||
* @param headers
|
||||
* @param body
|
||||
*/
|
||||
private access;
|
||||
/**
|
||||
* https://developer.qiniu.com/kodo/1208/upload-token
|
||||
* @param scope
|
||||
* @returns
|
||||
*/
|
||||
private generateKodoUploadToken;
|
||||
/**
|
||||
* https://developer.qiniu.com/kodo/1201/access-token
|
||||
*/
|
||||
private genernateKodoAccessToken;
|
||||
private base64ToUrlSafe;
|
||||
private hmacSha1;
|
||||
private urlSafeBase64Encode;
|
||||
|
|
|
|||
|
|
@ -6,6 +6,9 @@ require('../../fetch');
|
|||
const crypto_1 = tslib_1.__importDefault(require("crypto"));
|
||||
const ts_md5_1 = require("ts-md5");
|
||||
const buffer_1 = require("buffer");
|
||||
const querystring_1 = require("querystring");
|
||||
const Exception_1 = require("oak-domain/lib/types/Exception");
|
||||
const QINIU_CLOUD_HOST = 'rs.qiniuapi.com';
|
||||
class QiniuCloudInstance {
|
||||
accessKey;
|
||||
secretKey;
|
||||
|
|
@ -24,7 +27,7 @@ class QiniuCloudInstance {
|
|||
getUploadInfo(uploadHost, bucket, key) {
|
||||
try {
|
||||
const scope = key ? `${bucket}:${key}` : bucket;
|
||||
const uploadToken = this.getToken(scope);
|
||||
const uploadToken = this.generateKodoUploadToken(scope);
|
||||
return {
|
||||
key,
|
||||
uploadToken,
|
||||
|
|
@ -93,6 +96,18 @@ class QiniuCloudInstance {
|
|||
const obj = this.getStreamObj(publishDomain, playDomain, hub, publishKey, playKey, streamTitle, expireAt);
|
||||
return obj;
|
||||
}
|
||||
/**
|
||||
* https://developer.qiniu.com/kodo/1308/stat
|
||||
*/
|
||||
async getKodoStat(bucket, key) {
|
||||
const entry = `${bucket}:${key}`;
|
||||
const encodedEntryURI = this.urlSafeBase64Encode(entry);
|
||||
const path = `/stat/${encodedEntryURI}`;
|
||||
const result = await this.access(path, undefined, 'Get', {
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
});
|
||||
return result;
|
||||
}
|
||||
/**
|
||||
* 计算直播流地址相关信息
|
||||
* @param publishDomain
|
||||
|
|
@ -150,7 +165,60 @@ class QiniuCloudInstance {
|
|||
});
|
||||
return `https://${playBackDomain}/${streamTitle}.m3u8`;
|
||||
}
|
||||
getToken(scope) {
|
||||
/**
|
||||
* 管理端访问七牛云服务器
|
||||
* @param path
|
||||
* @param method
|
||||
* @param headers
|
||||
* @param body
|
||||
*/
|
||||
async access(path, query, method, headers, body) {
|
||||
const contentType = headers && headers['Content-Type'];
|
||||
const url = new URL(`https://${QINIU_CLOUD_HOST}${path}`);
|
||||
if (query) {
|
||||
url.search = typeof query === 'object' ? (0, querystring_1.stringify)(query) : query;
|
||||
}
|
||||
const accessToken = this.genernateKodoAccessToken(method || 'Get', QINIU_CLOUD_HOST, path, undefined, contentType);
|
||||
let response;
|
||||
try {
|
||||
response = await fetch(`https://${QINIU_CLOUD_HOST}${path}`, {
|
||||
method,
|
||||
headers: {
|
||||
'Authorization': `Qiniu ${accessToken}`,
|
||||
...headers,
|
||||
},
|
||||
body,
|
||||
});
|
||||
}
|
||||
catch (err) {
|
||||
// fetch返回异常一定是网络异常
|
||||
throw new Exception_1.OakNetworkException();
|
||||
}
|
||||
const responseType = response.headers.get('Content-Type') || response.headers.get('content-type');
|
||||
if (responseType?.toLocaleLowerCase().match(/application\/json/i)) {
|
||||
const json = await response.json();
|
||||
if (response.status > 299) {
|
||||
// 七牛服务器返回异常,根据文档一定是json
|
||||
// https://developer.qiniu.com/kodo/3928/error-responses
|
||||
const { code, error } = json;
|
||||
return new Exception_1.OakExternalException('qiniu', code, error);
|
||||
}
|
||||
return json;
|
||||
}
|
||||
else if (responseType?.toLocaleLowerCase().match(/application\/octet-stream/i)) {
|
||||
const result = await response.arrayBuffer();
|
||||
return result;
|
||||
}
|
||||
else {
|
||||
throw new Error(`尚不支持的content-type类型${responseType}`);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* https://developer.qiniu.com/kodo/1208/upload-token
|
||||
* @param scope
|
||||
* @returns
|
||||
*/
|
||||
generateKodoUploadToken(scope) {
|
||||
// 构造策略
|
||||
const putPolicy = {
|
||||
scope: scope,
|
||||
|
|
@ -163,6 +231,31 @@ class QiniuCloudInstance {
|
|||
const uploadToken = this.accessKey + ':' + encodedSign + ':' + encodedFlags;
|
||||
return uploadToken;
|
||||
}
|
||||
/**
|
||||
* https://developer.qiniu.com/kodo/1201/access-token
|
||||
*/
|
||||
genernateKodoAccessToken(method, host, path, query, contentType, body, xHeaders) {
|
||||
let signingStr = method + ' ' + path;
|
||||
if (query) {
|
||||
signingStr += '?' + query;
|
||||
}
|
||||
signingStr += '\nHost: ' + host;
|
||||
if (contentType) {
|
||||
signingStr += '\nContent-Type: ' + contentType;
|
||||
}
|
||||
if (xHeaders) {
|
||||
const ks = Object.keys(xHeaders);
|
||||
ks.sort((e1, e2) => e1 < e2 ? -1 : 1);
|
||||
ks.forEach((k) => signingStr += `\n${k}: ${xHeaders[k]}`);
|
||||
}
|
||||
signingStr += '\n\n';
|
||||
if (body) {
|
||||
signingStr += body;
|
||||
}
|
||||
const sign = this.hmacSha1(signingStr, this.secretKey);
|
||||
const encodedSign = this.urlSafeBase64Encode(sign);
|
||||
return `${this.accessKey}:${encodedSign}`;
|
||||
}
|
||||
base64ToUrlSafe(v) {
|
||||
return v.replace(/\//g, '_').replace(/\+/g, '-');
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ const buffer_1 = require("buffer");
|
|||
const url_1 = tslib_1.__importDefault(require("url"));
|
||||
const form_data_1 = tslib_1.__importDefault(require("form-data"));
|
||||
const Exception_1 = require("oak-domain/lib/types/Exception");
|
||||
const assert_1 = tslib_1.__importDefault(require("assert"));
|
||||
class WechatPublicInstance {
|
||||
appId;
|
||||
appSecret;
|
||||
|
|
@ -58,7 +59,7 @@ class WechatPublicInstance {
|
|||
if ([40001, 42001].includes(json.errcode)) {
|
||||
return this.refreshAccessToken(url, init);
|
||||
}
|
||||
throw new Error(`调用微信接口返回出错,code是${json.errcode},信息是${json.errmsg}`);
|
||||
throw new Exception_1.OakExternalException('wechatPublic', json.errcode, json.errmsg);
|
||||
}
|
||||
return json;
|
||||
}
|
||||
|
|
@ -74,7 +75,7 @@ class WechatPublicInstance {
|
|||
if ([40001, 42001].includes(json.errcode)) {
|
||||
return this.refreshAccessToken(url, init);
|
||||
}
|
||||
throw new Error(`调用微信接口返回出错,code是${json.errcode},信息是${json.errmsg}`);
|
||||
throw new Exception_1.OakExternalException('wechatPublic', json.errcode, json.errmsg);
|
||||
}
|
||||
return json;
|
||||
}
|
||||
|
|
@ -263,9 +264,7 @@ class WechatPublicInstance {
|
|||
}
|
||||
async getQrCode(options) {
|
||||
const { sceneId, sceneStr, expireSeconds, isPermanent } = options;
|
||||
if (!sceneId && !sceneStr) {
|
||||
throw new Error('Missing sceneId or sceneStr');
|
||||
}
|
||||
(0, assert_1.default)(sceneId || sceneStr);
|
||||
const scene = sceneId
|
||||
? {
|
||||
scene_id: sceneId,
|
||||
|
|
@ -400,7 +399,7 @@ class WechatPublicInstance {
|
|||
break;
|
||||
}
|
||||
default: {
|
||||
throw new Error('当前消息类型暂不支持');
|
||||
(0, assert_1.default)(false, '当前消息类型暂不支持');
|
||||
}
|
||||
}
|
||||
const token = await this.getAccessToken();
|
||||
|
|
|
|||
|
|
@ -1,7 +1,13 @@
|
|||
require('../../fetch');
|
||||
import crypto from 'crypto';
|
||||
import { UrlObject } from 'url';
|
||||
import { Md5 } from 'ts-md5';
|
||||
import { Buffer } from 'buffer';
|
||||
import { stringify } from 'querystring';
|
||||
import { OakExternalException, OakNetworkException } from 'oak-domain/lib/types/Exception';
|
||||
|
||||
const QINIU_CLOUD_HOST = 'rs.qiniuapi.com';
|
||||
type X_Header = `X-Qiniu-${string}`;
|
||||
|
||||
export class QiniuCloudInstance {
|
||||
private accessKey: string;
|
||||
|
|
@ -27,7 +33,7 @@ export class QiniuCloudInstance {
|
|||
) {
|
||||
try {
|
||||
const scope = key ? `${bucket}:${key}` : bucket;
|
||||
const uploadToken = this.getToken(scope);
|
||||
const uploadToken = this.generateKodoUploadToken(scope);
|
||||
return {
|
||||
key,
|
||||
uploadToken,
|
||||
|
|
@ -126,6 +132,29 @@ export class QiniuCloudInstance {
|
|||
return obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* https://developer.qiniu.com/kodo/1308/stat
|
||||
*/
|
||||
async getKodoStat(bucket: string, key: string) {
|
||||
const entry = `${bucket}:${key}`;
|
||||
const encodedEntryURI = this.urlSafeBase64Encode(entry);
|
||||
|
||||
const path = `/stat/${encodedEntryURI}`;
|
||||
|
||||
const result = await this.access(path, undefined, 'Get', {
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
});
|
||||
|
||||
return result as {
|
||||
fsize: number;
|
||||
hash: string;
|
||||
mimeType: string;
|
||||
type: 0 | 1 | 2 | 3;
|
||||
putTime: number;
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算直播流地址相关信息
|
||||
* @param publishDomain
|
||||
|
|
@ -210,7 +239,71 @@ export class QiniuCloudInstance {
|
|||
return `https://${playBackDomain}/${streamTitle}.m3u8`;
|
||||
}
|
||||
|
||||
private getToken(scope: string) {
|
||||
/**
|
||||
* 管理端访问七牛云服务器
|
||||
* @param path
|
||||
* @param method
|
||||
* @param headers
|
||||
* @param body
|
||||
*/
|
||||
private async access(
|
||||
path: string,
|
||||
query?: UrlObject['query'],
|
||||
method?: RequestInit['method'],
|
||||
headers?: Record<string, string>,
|
||||
body?: RequestInit['body']
|
||||
) {
|
||||
const contentType = headers && headers['Content-Type'];
|
||||
const url = new URL(`https://${QINIU_CLOUD_HOST}${path}`);
|
||||
if (query) {
|
||||
url.search = typeof query === 'object' ? stringify(query) : query;
|
||||
}
|
||||
const accessToken = this.genernateKodoAccessToken(method || 'Get', QINIU_CLOUD_HOST, path, undefined, contentType);
|
||||
|
||||
let response: Response;
|
||||
try {
|
||||
response = await fetch(`https://${QINIU_CLOUD_HOST}${path}`, {
|
||||
method,
|
||||
headers: {
|
||||
'Authorization': `Qiniu ${accessToken}`,
|
||||
...headers,
|
||||
},
|
||||
body,
|
||||
});
|
||||
}
|
||||
catch (err) {
|
||||
// fetch返回异常一定是网络异常
|
||||
throw new OakNetworkException();
|
||||
}
|
||||
|
||||
|
||||
const responseType = response.headers.get('Content-Type') || response.headers.get('content-type');
|
||||
if (responseType?.toLocaleLowerCase().match(/application\/json/i)) {
|
||||
const json = await response.json();
|
||||
|
||||
if (response.status > 299) {
|
||||
// 七牛服务器返回异常,根据文档一定是json
|
||||
// https://developer.qiniu.com/kodo/3928/error-responses
|
||||
const { code, error } = json;
|
||||
return new OakExternalException('qiniu', code, error);
|
||||
}
|
||||
return json;
|
||||
}
|
||||
else if (responseType?.toLocaleLowerCase().match(/application\/octet-stream/i)) {
|
||||
const result = await response.arrayBuffer();
|
||||
return result;
|
||||
}
|
||||
else {
|
||||
throw new Error(`尚不支持的content-type类型${responseType}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* https://developer.qiniu.com/kodo/1208/upload-token
|
||||
* @param scope
|
||||
* @returns
|
||||
*/
|
||||
private generateKodoUploadToken(scope: string) {
|
||||
// 构造策略
|
||||
const putPolicy = {
|
||||
scope: scope,
|
||||
|
|
@ -227,6 +320,43 @@ export class QiniuCloudInstance {
|
|||
return uploadToken;
|
||||
}
|
||||
|
||||
/**
|
||||
* https://developer.qiniu.com/kodo/1201/access-token
|
||||
*/
|
||||
private genernateKodoAccessToken(
|
||||
method: string,
|
||||
host: string,
|
||||
path: string,
|
||||
query?: string,
|
||||
contentType?: string,
|
||||
body?: string,
|
||||
xHeaders?: Record<X_Header, string>
|
||||
) {
|
||||
let signingStr = method + ' ' + path;
|
||||
if (query) {
|
||||
signingStr += '?' + query;
|
||||
}
|
||||
signingStr += '\nHost: ' + host;
|
||||
if (contentType) {
|
||||
signingStr += '\nContent-Type: ' + contentType;
|
||||
}
|
||||
if (xHeaders) {
|
||||
const ks = Object.keys(xHeaders);
|
||||
ks.sort((e1, e2) => e1 < e2 ? -1 : 1);
|
||||
ks.forEach(
|
||||
(k) => signingStr += `\n${k}: ${xHeaders[k as X_Header]}`,
|
||||
);
|
||||
}
|
||||
signingStr += '\n\n';
|
||||
if (body) {
|
||||
signingStr += body;
|
||||
}
|
||||
|
||||
const sign = this.hmacSha1(signingStr, this.secretKey);
|
||||
const encodedSign = this.urlSafeBase64Encode(sign);
|
||||
return `${this.accessKey}:${encodedSign}`;
|
||||
}
|
||||
|
||||
private base64ToUrlSafe(v: string) {
|
||||
return v.replace(/\//g, '_').replace(/\+/g, '-');
|
||||
}
|
||||
|
|
@ -236,6 +366,7 @@ export class QiniuCloudInstance {
|
|||
hmac.update(encodedFlags);
|
||||
return hmac.digest('base64');
|
||||
}
|
||||
|
||||
private urlSafeBase64Encode(jsonFlags: string) {
|
||||
const encoded = Buffer.from(jsonFlags).toString('base64');
|
||||
return this.base64ToUrlSafe(encoded);
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import { Buffer } from 'buffer';
|
|||
import URL from 'url';
|
||||
import FormData from 'form-data';
|
||||
import { OakExternalException, OakNetworkException, OakServerProxyException } from 'oak-domain/lib/types/Exception';
|
||||
import assert from 'assert';
|
||||
|
||||
// 目前先支持text和news, 其他type文档:https://developers.weixin.qq.com/doc/offiaccount/Message_Management/Service_Center_messages.html
|
||||
// type ServeMessageType = 'text' | 'news' | 'mpnews' | 'mpnewsarticle' | 'image' | 'voice' | 'video' | 'music' | 'msgmenu';/
|
||||
|
|
@ -106,9 +107,7 @@ export class WechatPublicInstance {
|
|||
if ([40001, 42001].includes(json.errcode)) {
|
||||
return this.refreshAccessToken(url, init);
|
||||
}
|
||||
throw new Error(
|
||||
`调用微信接口返回出错,code是${json.errcode},信息是${json.errmsg}`
|
||||
);
|
||||
throw new OakExternalException('wechatPublic', json.errcode, json.errmsg);
|
||||
}
|
||||
return json;
|
||||
}
|
||||
|
|
@ -126,9 +125,7 @@ export class WechatPublicInstance {
|
|||
if ([40001, 42001].includes(json.errcode)) {
|
||||
return this.refreshAccessToken(url, init);
|
||||
}
|
||||
throw new Error(
|
||||
`调用微信接口返回出错,code是${json.errcode},信息是${json.errmsg}`
|
||||
);
|
||||
throw new OakExternalException('wechatPublic', json.errcode, json.errmsg);
|
||||
}
|
||||
return json;
|
||||
}
|
||||
|
|
@ -379,9 +376,7 @@ export class WechatPublicInstance {
|
|||
isPermanent?: boolean;
|
||||
}) {
|
||||
const { sceneId, sceneStr, expireSeconds, isPermanent } = options;
|
||||
if (!sceneId && !sceneStr) {
|
||||
throw new Error('Missing sceneId or sceneStr');
|
||||
}
|
||||
assert(sceneId || sceneStr);
|
||||
const scene = sceneId
|
||||
? {
|
||||
scene_id: sceneId,
|
||||
|
|
@ -534,7 +529,7 @@ export class WechatPublicInstance {
|
|||
break;
|
||||
}
|
||||
default: {
|
||||
throw new Error('当前消息类型暂不支持');
|
||||
assert(false, '当前消息类型暂不支持');
|
||||
}
|
||||
}
|
||||
const token = await this.getAccessToken();
|
||||
|
|
|
|||
Loading…
Reference in New Issue