UNPKG

@mwcp/ali-oss

Version:
403 lines 12.6 kB
/* eslint-disable @typescript-eslint/no-unnecessary-condition */ /* eslint-disable max-lines-per-function */ import { AttrNames, SpanStatusCode } from '@mwcp/otel'; import { genError, genISO8601String } from '@waiting/shared-core'; import { FnKey, OssClient } from '@yuntools/ali-oss'; import { ConfigKey } from './types.js'; /** 阿里云 OSS oss-utils 命令行封装组件 */ export class AliOssComponent { config; traceService; client; querySpanMap = new WeakMap(); constructor(config) { this.config = config; const opts = { accessKeyId: config.accessKeyId, accessKeySecret: config.accessKeySecret, endpoint: config.endpoint, }; if (config.stsToken) { opts.stsToken = config.stsToken; } const client = new OssClient(opts, config.cmd); client.debug = !!config.debug; this.client = client; } /** * 拷贝文件, * - 拷贝本地文件/目录到远程建议使用 `upload()` 或者 `syncRemote()` 方法 * - 拷贝远程文件/目录到本地建议使用 `download()` 或者 `syncLocal()` 方法 * * 若 force 为空或者 false,且目标文件存在时会卡在命令行提示输入阶段(无显示)最后导致超时异常 * @link https://help.aliyun.com/document_detail/120057.html */ async cp( /** 本地文件、目录或者远程 OSS 对象 */ src, /** OSS 对象,不包括 bucket */ target, options) { const opts = { fnKey: FnKey.cp, options, target, src, }; const ret = await this.runner(opts); return ret; } /** * 上传本地文件到 OSS * 若 force 为空或 false,且目标文件存在时会卡在命令行提示输入阶段(无显示)最后导致超时异常 * @link https://help.aliyun.com/document_detail/120057.html */ async upload( /** 本地目录或文件 */ src, /** OSS 对象,不包括 bucket */ target, options) { const opts = { fnKey: FnKey.upload, options, target, src, }; const ret = await this.runner(opts); return ret; } /** * 下载远程文件到本地 * 若 force 为空或 false,且目标文件存在时会卡在命令行提示输入阶段(无显示)最后导致超时异常 * @link https://help.aliyun.com/document_detail/120057.html */ async download( /** OSS 对象,不包括 bucket */ src, /** 本地目录或文件 */ target, options) { const opts = { fnKey: FnKey.download, options, target, src, }; const ret = await this.runner(opts); return ret; } /** * 创建软链接 * @link https://help.aliyun.com/document_detail/120059.html */ async createSymlink( /** OSS 对象,不包括 bucket */ src, /** OSS 软连接对象,不包括 bucket */ target, options) { const opts = { fnKey: FnKey.link, options, target, src, }; const ret = await this.runner(opts); return ret; } /** * 创建目录 * @link https://help.aliyun.com/document_detail/120062.html */ async mkdir( /** OSS 对象,不包括 bucket */ target, options) { const opts = { fnKey: FnKey.mkdir, options, target, src: void 0, }; const ret = await this.runner(opts); return ret; } /** * 移动云端的 OSS 对象 * 流程为先 `cp()` 然后 `rm()` */ async mv( /** OSS 源对象,不包括 bucket */ src, /** OSS 目的对象,不包括 bucket */ target, options) { const opts = { fnKey: FnKey.mv, options, target, src, }; const ret = await this.runner(opts); return ret; } /** * OSS 远程路径是否存在 */ async pathExists( /** OSS 对象,不包括 bucket */ target, options) { const opts = { fnKey: FnKey.pathExists, options, target, src: void 0, }; const ret = await this.runner(opts); return ret; } /** * 删除云对象,不支持删除 bucket 本身 * 如果在 recursive 为 false 时删除目录,则目录参数值必须以 '/' 结尾,否则不会删除成功 * @link https://help.aliyun.com/document_detail/120053.html */ async rm( /** OSS 对象,不包括 bucket */ target, options) { const opts = { fnKey: FnKey.rm, options, target, src: void 0, }; const ret = await this.runner(opts); return ret; } /** * 递归删除,相当于 `rm -rf` * @link https://help.aliyun.com/document_detail/120053.html */ async rmrf( /** OSS 对象,不包括 bucket */ target, options) { const opts = { fnKey: FnKey.rmrf, options, target, src: void 0, }; const ret = await this.runner(opts); return ret; } /** * sign(生成签名URL) * @link https://help.aliyun.com/document_detail/120064.html */ async sign( /** OSS 对象,不包括 bucket */ src, options) { const opts = { fnKey: FnKey.sign, options, target: void 0, src, }; const ret = await this.runner(opts); return ret; } /** * 查看 Bucket 和 Object 信息 * @link https://help.aliyun.com/document_detail/120054.html */ async stat( /** OSS 对象,不包括 bucket */ target, options) { const opts = { fnKey: FnKey.stat, options, target, src: void 0, }; const ret = await this.runner(opts); return ret; } /** * 同步 OSS 文件到本地 * - force 参数默认 true * - 若 force 为 false,且目标文件存在时会卡在命令行提示输入阶段(无显示)最后导致超时异常 * @link https://help.aliyun.com/document_detail/256352.html */ async syncLocal( /** OSS 对象,不包括 bucket */ src, /** 本地目录 */ target, options) { const opts = { fnKey: FnKey.syncLocal, options, target, src, }; const ret = await this.runner(opts); return ret; } /** * 同步本地文件到 OSS * - force 参数默认 true * - 若 force 为 false,且目标文件存在时会卡在命令行提示输入阶段(无显示)最后导致超时异常 * @link https://help.aliyun.com/document_detail/193394.html */ async syncRemote( /** 本地目录 */ src, /** OSS 对象,不包括 bucket */ target, options) { const opts = { fnKey: FnKey.syncRemote, options, target, src, }; const ret = await this.runner(opts); return ret; } async runner(options) { const { fnKey } = options; const opts = this.genOptions(options); const id = { time: Symbol(Date.now()) }; try { await this.tracer('start', id, opts); // @ts-ignore const ret = await this.client[fnKey](opts); await this.tracer('finish', id, opts); return await ret; } catch (ex) { const err = ex instanceof Error ? ex : typeof ex === 'string' ? new Error(ex) : new Error(JSON.stringify(ex)); await this.tracer('error', id, opts, err); throw err; } } genOptions(input) { const ret = { ...this.config, src: input.src, target: input.target, ...input.options, }; return ret; } async tracer(type, id, options, err) { if (!options.enableTrace) { return; } if (!this.traceService) { return; } const end = Date.now(); const tmp = options; const opts = { acl: tmp.acl ?? '', src: tmp.src, payer: tmp.payer ?? '', recursive: tmp.recursive ?? false, sampleThrottleMs: tmp.sampleThrottleMs ?? 10000, target: tmp.target, }; switch (type) { case 'start': { const { span } = this.traceService.startScopeSpan({ name: ConfigKey.componentName, scope: id, }); const spanInfo = { span, timestamp: Date.now(), }; this.querySpanMap.set(id, spanInfo); const attrs = { [AttrNames.QueryCostThrottleInMS]: opts['sampleThrottleMs'], qid: id.time.toString(), acl: opts['acl'], payer: opts['payer'], recursive: opts['recursive'], src: opts['src'], target: opts['target'], }; this.traceService.setAttributes(span, attrs); const event = { event: AttrNames.QueryStart, time: genISO8601String(), }; this.traceService.addEvent(span, event); break; } case 'finish': { const spanInfo = this.querySpanMap.get(id); if (!spanInfo) { console.warn('Retrieve spanInfo undefined.', opts); return; } const { span } = spanInfo; const start = spanInfo.timestamp; const cost = end - start; const tags = { [AttrNames.QueryCost]: cost, }; this.traceService.setAttributes(span, tags); const event = { event: AttrNames.QueryFinish, time: genISO8601String(), [AttrNames.QueryCost]: cost, }; if (typeof opts['sampleThrottleMs'] === 'number' && opts['sampleThrottleMs'] > 0 && cost > opts['sampleThrottleMs']) { const tags2 = { // [AttrNames.SAMPLING_PRIORITY]: 50, [AttrNames.LogLevel]: 'warn', }; this.traceService.setAttributes(span, tags2); this.traceService.addEvent(span, event); } else { this.traceService.addEvent(span, event); } this.traceService.endSpan({ span, scope: id }); break; } case 'error': { const spanInfo = this.querySpanMap.get(id); if (!spanInfo) { console.warn('Retrieve spanInfo undefined.', opts); return; } const { span } = spanInfo; const start = spanInfo.timestamp; const cost = end - start; const attr = { [AttrNames.QueryCost]: cost, }; this.traceService.setAttributes(span, attr); const input = { event: AttrNames.QueryError, time: genISO8601String(), [AttrNames.LogLevel]: 'error', [AttrNames.QueryCost]: cost, // [AttrNames.Error]: err, }; this.traceService.addEvent(span, input); this.traceService.endSpan({ span, scope: id, spanStatusOptions: { code: SpanStatusCode.ERROR, error: genError({ error: err, altMessage: 'Unknown error.', }), }, }); break; } } return; } } //# sourceMappingURL=component.js.map