oak-external-sdk/lib/service/ctyun/CTYun.js

318 lines
9.9 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.CTYunInstance = void 0;
const tslib_1 = require("tslib");
// import AWS from 'aws-sdk';
const crypto_1 = tslib_1.__importDefault(require("crypto"));
const Exception_1 = require("oak-domain/lib/types/Exception");
const CTYun_ENDPOINT_LIST = {
hazz: {
ul: 'oos-hazz.ctyunapi.cn',
},
lnsy: {
ul: 'oos-lnsy.ctyunapi.cn',
},
sccd: {
ul: 'oos-sccd.ctyunapi.cn',
},
xjwlmq: {
ul: 'oos-xjwlmq.ctyunapi.cn',
},
gslz: {
ul: 'oos-gslz.ctyunapi.cn',
},
sdqd: {
ul: 'oos-sdqd.ctyunapi.cn',
},
gzgy: {
ul: 'oos-gzgy.ctyunapi.cn',
},
hbwh: {
ul: 'oos-hbwh.ctyunapi.cn',
},
xzls: {
ul: 'oos-xzls.ctyunapi.cn',
},
ahwh: {
ul: 'oos-ahwh.ctyunapi.cn',
},
gdsz: {
ul: 'oos-gdsz.ctyunapi.cn',
},
jssz: {
ul: 'oos-jssz.ctyunapi.cn',
},
sh2: {
ul: 'oos-sh2.ctyunapi.cn',
},
};
const serviceName = 's3';
const v4Identifier = 'aws4_request';
const expiresHeader = 'presigned-expires';
const unsignableHeaders = [
'authorization',
'x-ctyun-data-location',
'content-length',
'user-agent',
expiresHeader,
'expect',
'x-amzn-trace-id',
];
class CTYunInstance {
accessKey;
secretKey;
constructor(accessKey, secretKey) {
this.accessKey = accessKey;
this.secretKey = secretKey;
}
getUploadInfo(bucket, zone, key) {
try {
const signInfo = this.getSignInfo(bucket);
return {
key,
accessKey: this.accessKey,
policy: signInfo.encodePolicy,
signature: signInfo.signature,
uploadHost: `https://${bucket}.${CTYun_ENDPOINT_LIST[zone].ul}`,
bucket,
};
}
catch (err) {
throw err;
}
}
getSignInfo(bucket) {
// 对于policy里的expiration我在天翼云的文档里没有找到具体的说明但是这个字段不填入就会请求失败
// 设置一个明天过期的时间
const expiration = new Date();
expiration.setDate(expiration.getDate() + 1);
const policy = {
expiration: expiration.toISOString(),
conditions: [
{
bucket: bucket,
},
['starts-with', '$key', 'extraFile'],
],
Version: '2012-10-17',
Statement: [
{
Effect: 'Allow',
Action: ['oos:*'],
Resource: `arn:ctyun:oos:::${bucket} /*`,
},
],
};
const encodePolicy = this.urlSafeBase64Encode(JSON.stringify(policy));
const signature = this.hmacSha1(encodePolicy, this.secretKey);
return {
encodePolicy,
signature,
};
}
base64ToUrlSafe(v) {
return v.replace(/\//g, '_').replace(/\+/g, '-');
}
hmacSha1(encodedFlags, secretKey) {
const hmac = crypto_1.default.createHmac('sha1', secretKey);
hmac.update(encodedFlags);
return hmac.digest('base64');
}
urlSafeBase64Encode(jsonFlags) {
const encoded = Buffer.from(jsonFlags).toString('base64');
return this.base64ToUrlSafe(encoded);
}
async removeFile(bucket, zone, key) {
const path = `/${key}`;
const host = `${bucket}.${CTYun_ENDPOINT_LIST[zone].ul}`;
const url = `https://${host}${path}`;
const payload = '';
const method = 'DELETE';
const service = 's3';
const date = new Date()
.toISOString()
.replace(/\.\d{3}Z$/, 'Z')
.replace(/[:\-]|\.\d{3}/g, '');
const headers = {
'Content-Type': 'application/xml; charset=utf-8',
Host: host,
'x-amz-content-sha256': this.calculatePayloadHash(payload),
'x-amz-date': date,
};
const reqOptions = {
headers,
method: method,
payload: payload,
host,
path,
queryParameters: {},
service,
date,
};
const authorization = this.getAuthorization(reqOptions, zone);
let response;
try {
response = await fetch(url, {
method,
headers: {
...headers,
Authorization: authorization,
},
});
}
catch (err) {
throw new Exception_1.OakNetworkException();
}
if (response.status === 204) {
return;
}
const text = await response.text();
throw new Exception_1.OakExternalException('ctyun', response.status.toString(), text, {
status: response.status,
});
}
async isExistObject(srcBucket, zone, srcKey) {
const path = `/${srcKey}`;
const host = `${srcBucket}.${CTYun_ENDPOINT_LIST[zone].ul}`;
const url = `https://${host}${path}`;
const payload = '';
const method = 'GET';
const service = 's3';
const date = new Date()
.toISOString()
.replace(/\.\d{3}Z$/, 'Z')
.replace(/[:\-]|\.\d{3}/g, '');
const headers = {
Host: host,
'x-amz-content-sha256': this.calculatePayloadHash(payload),
'x-amz-date': date,
};
const reqOptions = {
headers,
method: method,
payload: payload,
host,
path,
queryParameters: {},
service,
date,
};
const authorization = this.getAuthorization(reqOptions, zone);
let response;
try {
response = await fetch(url, {
method,
headers: {
...headers,
Authorization: authorization,
},
});
}
catch (err) {
throw new Exception_1.OakNetworkException();
}
if (response.status === 204) {
return true;
}
const text = await response.text();
if (response.status === 404 && text.includes('NoSuchKey')) {
return false;
}
throw new Exception_1.OakExternalException('ctyun', response.status.toString(), text, {
status: response.status,
});
}
getAuthorization(reqOptions, zone) {
const { headers, host, path, method, payload, queryParameters, service, date, } = reqOptions;
const payloadHash = this.calculatePayloadHash(payload);
// Step 2: Build canonical request
const { canonicalRequest, signedHeaders } = this.buildCanonicalRequest(method, path, queryParameters, headers, payloadHash);
// Step 3: Calculate canonical request hash
const canonicalRequestHash = crypto_1.default
.createHash('sha256')
.update(canonicalRequest)
.digest('hex');
// Step 4: Build string to sign
const dateStamp = date.substr(0, 8);
const credentialScope = `${dateStamp}/${zone}/${service}/aws4_request`;
const stringToSign = [
'AWS4-HMAC-SHA256',
date,
credentialScope,
canonicalRequestHash,
].join('\n');
// Step 5: Get signing key
const signingKey = this.getSignatureKey(dateStamp, zone, service);
// Step 6: Calculate signature
const signature = crypto_1.default
.createHmac('sha256', signingKey)
.update(stringToSign)
.digest('hex');
const authorizationHeader = [
'AWS4-HMAC-SHA256 Credential=' +
this.accessKey +
'/' +
credentialScope,
'SignedHeaders=' + signedHeaders,
'Signature=' + signature,
].join(', ');
return authorizationHeader;
}
getSignatureKey(dateStamp, zone, service) {
const kDate = crypto_1.default
.createHmac('sha256', `AWS4${this.secretKey}`)
.update(dateStamp)
.digest();
const kRegion = crypto_1.default
.createHmac('sha256', kDate)
.update(zone)
.digest();
const kService = crypto_1.default
.createHmac('sha256', kRegion)
.update(service)
.digest();
const kSigning = crypto_1.default
.createHmac('sha256', kService)
.update('aws4_request')
.digest();
return kSigning;
}
buildCanonicalRequest(method, path, queryParameters, headers, payloadHash) {
const canonicalQuerystring = this.buildCanonicalQueryString(queryParameters);
const canonicalHeaders = Object.keys(headers)
.sort()
.map((key) => `${key.toLowerCase()}:${headers[key].trim()}`)
.join('\n');
const signedHeaders = Object.keys(headers)
.sort()
.map((key) => key.toLowerCase())
.join(';');
const canonicalRequest = [
method,
path,
canonicalQuerystring,
canonicalHeaders,
'',
signedHeaders,
payloadHash,
].join('\n');
return {
canonicalRequest,
signedHeaders,
};
}
buildCanonicalQueryString(queryParameters) {
const keys = Object.keys(queryParameters).sort();
const canonicalQueryString = keys
.map((key) => `${encodeURIComponent(key)}=${encodeURIComponent(queryParameters[key])}`)
.join('&');
return canonicalQueryString;
}
calculatePayloadHash(payload) {
const hash = crypto_1.default.createHash('sha256');
hash.update(payload);
return hash.digest('hex');
}
}
exports.CTYunInstance = CTYunInstance;