235 lines
12 KiB
JavaScript
235 lines
12 KiB
JavaScript
"use strict";
|
||
Object.defineProperty(exports, "__esModule", { value: true });
|
||
const tslib_1 = require("tslib");
|
||
const assert_1 = require("oak-domain/lib/utils/assert");
|
||
const aliyun_1 = tslib_1.__importDefault(require("./aliyun"));
|
||
const oak_external_sdk_1 = require("oak-external-sdk");
|
||
const Exception_1 = require("oak-domain/lib/types/Exception");
|
||
const sts_1 = require("oak-external-sdk/lib/service/ali/sts");
|
||
const types_1 = require("oak-domain/lib/types");
|
||
class ALiYunBackend extends aliyun_1.default {
|
||
getConfigAndInstance(application) {
|
||
const { config, account } = this.getConfig(application);
|
||
const instance = oak_external_sdk_1.ALiYunSDK.getInstance(account.accessKeyId, account.accessKeySecret);
|
||
return {
|
||
config,
|
||
instance,
|
||
account,
|
||
};
|
||
}
|
||
async composeFileUrlBackend(options) {
|
||
const { application, extraFile, context, style } = options;
|
||
const { config: aliyunCosConfig } = this.getConfig(application);
|
||
if (aliyunCosConfig) {
|
||
let bucket = aliyunCosConfig.buckets.find((ele) => ele.name === extraFile.bucket);
|
||
if (bucket) {
|
||
const { domain, protocol } = bucket;
|
||
let protocol2 = protocol;
|
||
if (protocol instanceof Array) {
|
||
// protocol存在https: 说明域名有证书
|
||
const index = protocol.includes('https:')
|
||
? protocol.findIndex((ele) => ele === 'https:')
|
||
: 0;
|
||
protocol2 = protocol[index];
|
||
}
|
||
return `${protocol2}//${domain}/${this.formKey(extraFile)}${style ? style : ''}`;
|
||
}
|
||
}
|
||
return '';
|
||
}
|
||
async formUploadMeta(application, extraFile) {
|
||
const { bucket, enableChunkedUpload } = extraFile;
|
||
// 构造文件上传所需的key
|
||
const key = this.formKey(extraFile);
|
||
const { instance, config: aliyunCosConfig } = this.getConfigAndInstance(application);
|
||
const { buckets } = aliyunCosConfig;
|
||
let bucket2 = bucket;
|
||
if (!bucket2) {
|
||
const { defaultBucket } = aliyunCosConfig;
|
||
bucket2 = defaultBucket;
|
||
}
|
||
(0, assert_1.assert)(bucket2);
|
||
const b = buckets.find((ele) => ele.name === bucket2);
|
||
(0, assert_1.assert)(b, `${bucket2}不是一个有效的桶配置`);
|
||
Object.assign(extraFile, {
|
||
bucket: bucket2,
|
||
uploadMeta: !enableChunkedUpload ? instance.getUploadInfo(bucket2, b.zone, key) : {},
|
||
});
|
||
}
|
||
async checkWhetherSuccess(application, extraFile) {
|
||
const key = this.formKey(extraFile);
|
||
const { instance, config: aliyunCosConfig } = this.getConfigAndInstance(application);
|
||
const b = aliyunCosConfig.buckets.find((ele) => ele.name === extraFile.bucket);
|
||
(0, assert_1.assert)(b, `extraFile中的bucket名称在阿里云配置中找不到「${extraFile.bucket}」`);
|
||
try {
|
||
const result = await instance.isExistObject(extraFile.bucket, b.zone, key);
|
||
return result;
|
||
}
|
||
catch (err) {
|
||
throw err;
|
||
}
|
||
}
|
||
async removeFile(application, extraFile) {
|
||
const key = this.formKey(extraFile);
|
||
const { instance, config: aliyunCosConfig } = this.getConfigAndInstance(application);
|
||
const b = aliyunCosConfig.buckets.find((ele) => ele.name === extraFile.bucket);
|
||
(0, assert_1.assert)(b, `extraFile中的bucket名称在阿里云配置中找不到「${extraFile.bucket}」`);
|
||
try {
|
||
await instance.removeFile(extraFile.bucket, b.zone, key);
|
||
}
|
||
catch (err) {
|
||
throw err;
|
||
}
|
||
}
|
||
async composeChunkUploadInfo(application, extraFile, context) {
|
||
const key = this.formKey(extraFile);
|
||
const { instance, config: aliyunCosConfig, account } = this.getConfigAndInstance(application);
|
||
let useSts = true;
|
||
let stsInfo = {};
|
||
if (!account.stsEndpoint || !account.roleArn || !account.roleSessionName) {
|
||
useSts = false;
|
||
console.warn("阿里云Cos配置中缺少sts相关配置,无法使用sts方式上传分片,将使用账号授权进行上传,可能存在安全风险,请检查确保不会暴露accessKey");
|
||
}
|
||
else {
|
||
try {
|
||
const res = await (0, sts_1.stsAssumeRole)({
|
||
accessKeyId: account.accessKeyId,
|
||
accessKeySecret: account.accessKeySecret,
|
||
endpoint: account.stsEndpoint,
|
||
roleArn: account.roleArn,
|
||
roleSessionName: account.roleSessionName,
|
||
});
|
||
stsInfo = {
|
||
stsToken: res.Credentials.SecurityToken,
|
||
accessKeyId: res.Credentials.AccessKeyId,
|
||
accessKeySecret: res.Credentials.AccessKeySecret,
|
||
};
|
||
}
|
||
catch (err) {
|
||
console.error("Failed to assume role for STS:", err);
|
||
throw new Exception_1.OakPreConditionUnsetException("获取阿里云STS临时凭证失败,请检查配置是否正确", 'extraFile');
|
||
}
|
||
}
|
||
// 大部分校验都在formUploadMeta中完成,这里可以不多做判断了
|
||
const b = aliyunCosConfig.buckets.find((ele) => ele.name === extraFile.bucket);
|
||
const preInit = await instance.prepareMultipartUpload(extraFile.bucket, b.zone, key, extraFile.chunkInfo?.partCount, {
|
||
timeout: 30 * 1000, // 30 seconds
|
||
...(useSts ? stsInfo
|
||
: {}),
|
||
expires: 3 * 24 * 60 * 60, // 3 days
|
||
});
|
||
return {
|
||
uploadId: preInit.uploadId,
|
||
chunkSize: extraFile.chunkInfo?.chunkSize,
|
||
partCount: preInit.parts.length,
|
||
parts: preInit.parts.map((part) => ({
|
||
partNumber: part.partNumber,
|
||
uploadUrl: part.uploadUrl,
|
||
formData: {}, // 阿里云不需要额外的formData
|
||
})),
|
||
};
|
||
}
|
||
/**
|
||
* 完成分片上传后的合并操作
|
||
*/
|
||
async mergeChunkedUpload(application, extraFile, parts, context) {
|
||
const key = this.formKey(extraFile);
|
||
const { instance, config: aliyunCosConfig } = this.getConfigAndInstance(application);
|
||
const b = aliyunCosConfig.buckets.find((ele) => ele.name === extraFile.bucket);
|
||
(0, assert_1.assert)(b, `extraFile中的bucket名称在阿里云配置中找不到「${extraFile.bucket}」`);
|
||
(0, assert_1.assert)(extraFile.chunkInfo?.uploadId, 'extraFile缺少chunkInfo.uploadId,无法完成分片上传的合并操作');
|
||
(0, assert_1.assert)(parts.length > 0, 'parts不能为空,无法完成分片上传的合并操作');
|
||
try {
|
||
await instance.completeMultipartUpload(extraFile.bucket, b.zone, key, extraFile.chunkInfo.uploadId, parts.map(part => ({
|
||
number: part.partNumber,
|
||
etag: part.etag,
|
||
})));
|
||
}
|
||
catch (err) {
|
||
throw new types_1.OakException('合并分片上传失败' + 'extraFile' + err);
|
||
}
|
||
}
|
||
async abortMultipartUpload(application, extraFile, context) {
|
||
const key = this.formKey(extraFile);
|
||
const { instance, config: aliyunCosConfig } = this.getConfigAndInstance(application);
|
||
const b = aliyunCosConfig.buckets.find((ele) => ele.name === extraFile.bucket);
|
||
(0, assert_1.assert)(b, `extraFile中的bucket名称在阿里云配置中找不到「${extraFile.bucket}」`);
|
||
(0, assert_1.assert)(extraFile.chunkInfo?.uploadId, 'extraFile缺少chunkInfo.uploadId,无法中止分片上传操作');
|
||
await instance.abortMultipartUpload(extraFile.bucket, b.zone, key, extraFile.chunkInfo.uploadId);
|
||
}
|
||
async listMultipartUploads(application, extraFile, context) {
|
||
const key = this.formKey(extraFile);
|
||
const { instance, config: aliyunCosConfig } = this.getConfigAndInstance(application);
|
||
const b = aliyunCosConfig.buckets.find((ele) => ele.name === extraFile.bucket);
|
||
(0, assert_1.assert)(b, `extraFile中的bucket名称在阿里云配置中找不到「${extraFile.bucket}」`);
|
||
(0, assert_1.assert)(extraFile.chunkInfo?.uploadId, 'extraFile缺少chunkInfo.uploadId,无法列出分片上传信息');
|
||
return await instance.listParts(extraFile.bucket, b.zone, key, extraFile.chunkInfo.uploadId);
|
||
}
|
||
async presignFile(methods, application, extraFile, context) {
|
||
const key = this.formKey(extraFile);
|
||
const { instance, config: aliyunCosConfig } = this.getConfigAndInstance(application);
|
||
const b = aliyunCosConfig.buckets.find((ele) => ele.name === extraFile.bucket);
|
||
(0, assert_1.assert)(b, `extraFile中的bucket名称在阿里云配置中找不到「${extraFile.bucket}」`);
|
||
return await instance.presignObjectUrl(methods, extraFile.bucket, b.zone, key, {
|
||
expires: 24 * 60 * 60, // 1 day
|
||
});
|
||
}
|
||
/**
|
||
* 对一段文件的分片上传进行预签名
|
||
* @param extraFileId extraFile的id
|
||
* @param from 起始partNumber
|
||
* @param to 结束partNumber
|
||
*/
|
||
async presignMultiPartUpload(application, extraFile, from, to, context) {
|
||
const key = this.formKey(extraFile);
|
||
const { instance, config: aliyunCosConfig } = this.getConfigAndInstance(application);
|
||
const b = aliyunCosConfig.buckets.find((ele) => ele.name === extraFile.bucket);
|
||
(0, assert_1.assert)(b, `extraFile中的bucket名称在阿里云配置中找不到「${extraFile.bucket}」`);
|
||
const res = await instance.presignMulti(extraFile.bucket, b.zone, key, extraFile.chunkInfo.uploadId, from, to, {
|
||
expires: 24 * 60 * 60, // 1 day
|
||
});
|
||
return res;
|
||
}
|
||
async prepareChunkedUpload(application, extraFile, context) {
|
||
const key = this.formKey(extraFile);
|
||
const { instance, config: aliyunCosConfig, account } = this.getConfigAndInstance(application);
|
||
let useSts = true;
|
||
let stsInfo = {};
|
||
if (!account.stsEndpoint || !account.roleArn || !account.roleSessionName) {
|
||
useSts = false;
|
||
console.warn("阿里云Cos配置中缺少sts相关配置,无法使用sts方式上传分片,将使用账号授权进行上传,可能存在安全风险,请检查确保不会暴露accessKey");
|
||
}
|
||
else {
|
||
try {
|
||
const res = await (0, sts_1.stsAssumeRole)({
|
||
accessKeyId: account.accessKeyId,
|
||
accessKeySecret: account.accessKeySecret,
|
||
endpoint: account.stsEndpoint,
|
||
roleArn: account.roleArn,
|
||
roleSessionName: account.roleSessionName,
|
||
});
|
||
stsInfo = {
|
||
stsToken: res.Credentials.SecurityToken,
|
||
accessKeyId: res.Credentials.AccessKeyId,
|
||
accessKeySecret: res.Credentials.AccessKeySecret,
|
||
};
|
||
}
|
||
catch (err) {
|
||
console.error("Failed to assume role for STS:", err);
|
||
throw new Exception_1.OakPreConditionUnsetException("获取阿里云STS临时凭证失败,请检查配置是否正确", 'extraFile');
|
||
}
|
||
}
|
||
// 大部分校验都在formUploadMeta中完成,这里可以不多做判断了
|
||
const b = aliyunCosConfig.buckets.find((ele) => ele.name === extraFile.bucket);
|
||
const preInit = await instance.initiateMultipartUpload(extraFile.bucket, b.zone, key, {
|
||
timeout: 30 * 1000, // 30 seconds
|
||
...(useSts ? stsInfo
|
||
: {}),
|
||
});
|
||
return {
|
||
uploadId: preInit.uploadId,
|
||
};
|
||
}
|
||
}
|
||
exports.default = ALiYunBackend;
|