const assert = require('assert'); const { isArray } = require('./common/utils/isArray'); const { checkBucketName: _checkBucketName } = require('../lib/common/utils/checkBucketName'); const { formatTag } = require('../lib/common/utils/formatTag'); const proto = exports; function toArray(obj) { if (!obj) return []; if (isArray(obj)) return obj; return [obj]; } /** * Bucket opertaions */ proto.listBuckets = async function listBuckets(query = {}, options = {}) { // prefix, marker, max-keys const { subres = {} } = query; const rest = {}; for (const key in query) { if (key !== 'subres') { rest[key] = query[key]; } } const params = this._bucketRequestParams('GET', '', Object.assign(subres, options.subres), options); params.query = rest; const result = await this.request(params); if (result.status === 200) { const data = await this.parseXML(result.data); let buckets = data.Buckets || null; if (buckets) { if (buckets.Bucket) { buckets = buckets.Bucket; } if (!isArray(buckets)) { buckets = [buckets]; } buckets = buckets.map(item => ({ name: item.Name, region: item.Location, creationDate: item.CreationDate, storageClass: item.StorageClass, StorageClass: item.StorageClass, tag: formatTag(item) })); } return { buckets, owner: { id: data.Owner.ID, displayName: data.Owner.DisplayName }, isTruncated: data.IsTruncated === 'true', nextMarker: data.NextMarker || null, res: result.res }; } throw await this.requestError(result); }; proto.useBucket = function useBucket(name) { _checkBucketName(name); return this.setBucket(name); }; proto.setBucket = function useBucket(name) { _checkBucketName(name); this.options.bucket = name; return this; }; proto.getBucket = function getBucket() { return this.options.bucket; }; proto.getBucketLocation = async function getBucketLocation(name, options) { _checkBucketName(name); name = name || this.getBucket(); const params = this._bucketRequestParams('GET', name, 'location', options); params.successStatuses = [200]; params.xmlResponse = true; const result = await this.request(params); return { location: result.data, res: result.res }; }; proto.getBucketInfo = async function getBucketInfo(name, options) { _checkBucketName(name); name = name || this.getBucket(); const params = this._bucketRequestParams('GET', name, 'bucketInfo', options); params.successStatuses = [200]; params.xmlResponse = true; const result = await this.request(params); return { bucket: result.data.Bucket, res: result.res }; }; proto.deleteBucket = async function deleteBucket(name, options) { _checkBucketName(name); const params = this._bucketRequestParams('DELETE', name, '', options); const result = await this.request(params); if (result.status === 200 || result.status === 204) { return { res: result.res }; } throw await this.requestError(result); }; // acl proto.putBucketACL = async function putBucketACL(name, acl, options) { _checkBucketName(name); const params = this._bucketRequestParams('PUT', name, 'acl', options); params.headers = { 'x-oss-acl': acl }; params.successStatuses = [200]; const result = await this.request(params); return { bucket: (result.headers.location && result.headers.location.substring(1)) || null, res: result.res }; }; proto.getBucketACL = async function getBucketACL(name, options) { _checkBucketName(name); const params = this._bucketRequestParams('GET', name, 'acl', options); params.successStatuses = [200]; params.xmlResponse = true; const result = await this.request(params); return { acl: result.data.AccessControlList.Grant, owner: { id: result.data.Owner.ID, displayName: result.data.Owner.DisplayName }, res: result.res }; }; // logging proto.putBucketLogging = async function putBucketLogging(name, prefix, options) { _checkBucketName(name); const params = this._bucketRequestParams('PUT', name, 'logging', options); let xml = `${'\n\n\n'}${name}\n`; if (prefix) { xml += `${prefix}\n`; } xml += '\n'; params.content = xml; params.mime = 'xml'; params.successStatuses = [200]; const result = await this.request(params); return { res: result.res }; }; proto.getBucketLogging = async function getBucketLogging(name, options) { _checkBucketName(name); const params = this._bucketRequestParams('GET', name, 'logging', options); params.successStatuses = [200]; params.xmlResponse = true; const result = await this.request(params); const enable = result.data.LoggingEnabled; return { enable: !!enable, prefix: (enable && enable.TargetPrefix) || null, res: result.res }; }; proto.deleteBucketLogging = async function deleteBucketLogging(name, options) { _checkBucketName(name); const params = this._bucketRequestParams('DELETE', name, 'logging', options); params.successStatuses = [204, 200]; const result = await this.request(params); return { res: result.res }; }; proto.putBucketCORS = async function putBucketCORS(name, rules, options) { _checkBucketName(name); rules = rules || []; assert(rules.length, 'rules is required'); rules.forEach(rule => { assert(rule.allowedOrigin, 'allowedOrigin is required'); assert(rule.allowedMethod, 'allowedMethod is required'); }); const params = this._bucketRequestParams('PUT', name, 'cors', options); let xml = '\n'; const parseOrigin = val => { xml += `${val}`; }; const parseMethod = val => { xml += `${val}`; }; const parseHeader = val => { xml += `${val}`; }; const parseExposeHeader = val => { xml += `${val}`; }; for (let i = 0, l = rules.length; i < l; i++) { const rule = rules[i]; xml += ''; toArray(rule.allowedOrigin).forEach(parseOrigin); toArray(rule.allowedMethod).forEach(parseMethod); toArray(rule.allowedHeader).forEach(parseHeader); toArray(rule.exposeHeader).forEach(parseExposeHeader); if (rule.maxAgeSeconds) { xml += `${rule.maxAgeSeconds}`; } xml += ''; } xml += ''; params.content = xml; params.mime = 'xml'; params.successStatuses = [200]; const result = await this.request(params); return { res: result.res }; }; proto.getBucketCORS = async function getBucketCORS(name, options) { _checkBucketName(name); const params = this._bucketRequestParams('GET', name, 'cors', options); params.successStatuses = [200]; params.xmlResponse = true; const result = await this.request(params); const rules = []; if (result.data && result.data.CORSRule) { let { CORSRule } = result.data; if (!isArray(CORSRule)) CORSRule = [CORSRule]; CORSRule.forEach(rule => { const r = {}; Object.keys(rule).forEach(key => { r[key.slice(0, 1).toLowerCase() + key.slice(1, key.length)] = rule[key]; }); rules.push(r); }); } return { rules, res: result.res }; }; proto.deleteBucketCORS = async function deleteBucketCORS(name, options) { _checkBucketName(name); const params = this._bucketRequestParams('DELETE', name, 'cors', options); params.successStatuses = [204]; const result = await this.request(params); return { res: result.res }; }; // referer proto.putBucketReferer = async function putBucketReferer(name, allowEmpty, referers, options) { _checkBucketName(name); const params = this._bucketRequestParams('PUT', name, 'referer', options); let xml = '\n\n'; xml += ` ${allowEmpty ? 'true' : 'false'}\n`; if (referers && referers.length > 0) { xml += ' \n'; for (let i = 0; i < referers.length; i++) { xml += ` ${referers[i]}\n`; } xml += ' \n'; } else { xml += ' \n'; } xml += ''; params.content = xml; params.mime = 'xml'; params.successStatuses = [200]; const result = await this.request(params); return { res: result.res }; }; proto.getBucketReferer = async function getBucketReferer(name, options) { _checkBucketName(name); const params = this._bucketRequestParams('GET', name, 'referer', options); params.successStatuses = [200]; params.xmlResponse = true; const result = await this.request(params); let referers = result.data.RefererList.Referer || null; if (referers) { if (!isArray(referers)) { referers = [referers]; } } return { allowEmpty: result.data.AllowEmptyReferer === 'true', referers, res: result.res }; }; proto.deleteBucketReferer = async function deleteBucketReferer(name, options) { _checkBucketName(name); return await this.putBucketReferer(name, true, null, options); }; // private apis proto._bucketRequestParams = function _bucketRequestParams(method, bucket, subres, options) { return { method, bucket, subres, headers: options && options.headers, additionalHeaders: options && options.additionalHeaders, timeout: options && options.timeout, ctx: options && options.ctx }; };