diff --git a/package-lock.json b/package-lock.json index 66dd9e3..eade9c7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "qiniuclient", - "version": "0.5.0", + "version": "0.5.4", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -3916,6 +3916,11 @@ "color-name": "^1.0.0" } }, + "colorful": { + "version": "2.1.0", + "resolved": "https://registry.nlark.com/colorful/download/colorful-2.1.0.tgz", + "integrity": "sha1-aovcvC2kKlDbO0iC+iUmOqofLY4=" + }, "colormin": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/colormin/-/colormin-1.1.2.tgz", @@ -3944,8 +3949,7 @@ "commander": { "version": "2.17.1", "resolved": "https://registry.npmjs.org/commander/-/commander-2.17.1.tgz", - "integrity": "sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg==", - "dev": true + "integrity": "sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg==" }, "commondir": { "version": "1.0.1", @@ -4284,6 +4288,11 @@ "resolved": "https://registry.npm.taobao.org/crypt/download/crypt-0.0.2.tgz", "integrity": "sha1-iNf/fsDfuG9xPch7u0LQRNPmxBs=" }, + "crypto": { + "version": "0.0.3", + "resolved": "https://registry.nlark.com/crypto/download/crypto-0.0.3.tgz", + "integrity": "sha1-RwqBuGvkxe4XrMggeh9TFa4g27A=" + }, "crypto-browserify": { "version": "3.12.0", "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz", @@ -7443,8 +7452,7 @@ "ini": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", - "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", - "dev": true + "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==" }, "internal-ip": { "version": "4.3.0", @@ -8001,6 +8009,40 @@ "ms": "^2.0.0" } }, + "ks3": { + "version": "0.5.1", + "resolved": "https://registry.nlark.com/ks3/download/ks3-0.5.1.tgz", + "integrity": "sha1-6IOn3ImgfiEykwqC/PbVS5Byx34=", + "requires": { + "async": "^0.9.0", + "colorful": "^2.1.0", + "commander": "^2.5.0", + "crypto": "0.0.3", + "debug": "^2.0.0", + "mime": "^1.2.11", + "nconf": "^0.6.9", + "progress": "^1.1.8", + "promptly": "^0.2.0", + "urllib": "^2.0.1" + }, + "dependencies": { + "async": { + "version": "0.9.2", + "resolved": "https://registry.nlark.com/async/download/async-0.9.2.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.nlark.com%2Fasync%2Fdownload%2Fasync-0.9.2.tgz", + "integrity": "sha1-rqdNXmHB+JlhO/ZL2mbUx48v0X0=" + }, + "mime": { + "version": "1.6.0", + "resolved": "https://registry.nlark.com/mime/download/mime-1.6.0.tgz", + "integrity": "sha1-Ms2eXGRVO9WNGaVor0Uqz/BJgbE=" + }, + "progress": { + "version": "1.1.8", + "resolved": "https://registry.npm.taobao.org/progress/download/progress-1.1.8.tgz", + "integrity": "sha1-4mDHj2Fhzdmw5WzD4Khd4Xx6V74=" + } + } + }, "latest-version": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-5.1.0.tgz", @@ -8864,6 +8906,11 @@ } } }, + "mute-stream": { + "version": "0.0.8", + "resolved": "https://registry.npm.taobao.org/mute-stream/download/mute-stream-0.0.8.tgz", + "integrity": "sha1-FjDEKyJR/4HiooPelqVJfqkuXg0=" + }, "mz": { "version": "2.7.0", "resolved": "https://registry.npm.taobao.org/mz/download/mz-2.7.0.tgz", @@ -8940,6 +8987,23 @@ "xml-char-classes": "^1.0.0" } }, + "nconf": { + "version": "0.6.9", + "resolved": "https://registry.nlark.com/nconf/download/nconf-0.6.9.tgz", + "integrity": "sha1-lXDvFe1vmuays8jV5xtm0xk81mE=", + "requires": { + "async": "0.2.9", + "ini": "1.x.x", + "optimist": "0.6.0" + }, + "dependencies": { + "async": { + "version": "0.2.9", + "resolved": "https://registry.nlark.com/async/download/async-0.2.9.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.nlark.com%2Fasync%2Fdownload%2Fasync-0.2.9.tgz", + "integrity": "sha1-32MGD789Myhqdqr21Vophtn/hhk=" + } + } + }, "negotiator": { "version": "0.6.2", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", @@ -9344,6 +9408,15 @@ "is-wsl": "^1.1.0" } }, + "optimist": { + "version": "0.6.0", + "resolved": "https://registry.npm.taobao.org/optimist/download/optimist-0.6.0.tgz", + "integrity": "sha1-aUJIJvNAX3nxQub8PZrljU27kgA=", + "requires": { + "minimist": "~0.0.1", + "wordwrap": "~0.0.2" + } + }, "optionator": { "version": "0.8.2", "resolved": "https://registry.npm.taobao.org/optionator/download/optionator-0.8.2.tgz", @@ -10417,6 +10490,14 @@ "integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM=", "dev": true }, + "promptly": { + "version": "0.2.1", + "resolved": "https://registry.nlark.com/promptly/download/promptly-0.2.1.tgz", + "integrity": "sha1-ZETnyk29mJnn7rXsOSKCfr3CKzs=", + "requires": { + "read": "~1.0.4" + } + }, "proto-list": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz", @@ -10698,6 +10779,14 @@ } } }, + "read": { + "version": "1.0.7", + "resolved": "https://registry.nlark.com/read/download/read-1.0.7.tgz", + "integrity": "sha1-s9oZvQUkMal2cdRKQmNK33ELQMQ=", + "requires": { + "mute-stream": "~0.0.4" + } + }, "read-config-file": { "version": "5.0.2", "resolved": "https://registry.npmjs.org/read-config-file/-/read-config-file-5.0.2.tgz", @@ -15102,8 +15191,7 @@ "wordwrap": { "version": "0.0.2", "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz", - "integrity": "sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8=", - "dev": true + "integrity": "sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8=" }, "worker-farm": { "version": "1.7.0", diff --git a/package.json b/package.json index c72ccd4..7f9009f 100644 --- a/package.json +++ b/package.json @@ -41,6 +41,7 @@ "electron-json-storage": "^4.1.7", "fs-extra": "^7.0.1", "klaw-sync": "^6.0.0", + "ks3": "^0.5.1", "log4js": "^3.0.6", "mime-types": "^2.1.24", "multispinner": "^0.2.1", diff --git a/src/main/index.dev.js b/src/main/index.dev.js index 1b887c6..8920a1f 100644 --- a/src/main/index.dev.js +++ b/src/main/index.dev.js @@ -10,6 +10,7 @@ // Set babel `env` and install `babel-register` // process.env.NODE_ENV = 'development'; // process.env.BABEL_ENV = 'main'; +import { BrowserWindow } from 'electron'; require("babel-register")({ ignore: /node_modules/ diff --git a/src/renderer/cos/CloudObjectStorage.js b/src/renderer/cos/CloudObjectStorage.js index 2796e4e..d942745 100644 --- a/src/renderer/cos/CloudObjectStorage.js +++ b/src/renderer/cos/CloudObjectStorage.js @@ -9,6 +9,7 @@ import ali from '../cos/ali' import upyun from '../cos/upyun' import aws from '../cos/aws' import jd from '../cos/jd' +import ks3 from '../cos/ks3' import brand from '../cos/brand' @@ -55,6 +56,9 @@ export default class CloudObjectStorage { case brand.jd.key: this.cos = jd break + case brand.ks3.key: + this.cos = ks3 + break case brand.minio.key: this.cos = aws break diff --git a/src/renderer/cos/brand.js b/src/renderer/cos/brand.js index b1ca441..c189ed1 100644 --- a/src/renderer/cos/brand.js +++ b/src/renderer/cos/brand.js @@ -49,6 +49,11 @@ export default { name: '京东云', features: ['paging', 'manualPrivateBucket'], }, + ks3: { + key: 'ks3', + name: '金山云', + features: ['paging'], + }, minio: { key: 'minio', name: 'MinIO', diff --git a/src/renderer/cos/ks3.js b/src/renderer/cos/ks3.js new file mode 100644 index 0000000..f1bcf10 --- /dev/null +++ b/src/renderer/cos/ks3.js @@ -0,0 +1,34 @@ +const KS3 = require('ks3/lib/ks3') +import KS3Bucket from './ks3Bucket' + +let client + +function init(param) { + client = new KS3(param.access_key, param.secret_key); + client.config({ + dataType: 'json' + }) + console.log('client: ', client) +} + +function getBuckets(callback) { + _getBuckets(client, callback) +} + +function _getBuckets(client, callback) { + client.service.get(function(error, result) { + let data = result.ListAllMyBucketsResult.Buckets + data.Bucket.forEach((item, index) => { + data.Bucket[index].name = data.Bucket[index].Name + data.Bucket[index].region = data.Bucket[index].Region + }) + // 有错误则回调错误 + callback && callback(error || null, data.Bucket) + }) +} + +function generateBucket(bucketInfo) { + return new KS3Bucket(bucketInfo, client) +} + +export default { init, getBuckets, _getBuckets, generateBucket } \ No newline at end of file diff --git a/src/renderer/cos/ks3Bucket.js b/src/renderer/cos/ks3Bucket.js new file mode 100644 index 0000000..e3f0d84 --- /dev/null +++ b/src/renderer/cos/ks3Bucket.js @@ -0,0 +1,183 @@ +import { util } from '../service/index' +import baseBucket from './baseBucket' +import brand from './brand' + +const fs = require('fs') + +class Bucket extends baseBucket { + constructor(bucketInfo, cos) { + super(bucketInfo, cos, brand.ks3.key) + const regionMap = { + BEIJING: 'ks3-cn-beijing', + SHANGHAI: 'ks3-cn-shanghai', + GUANGZHOU: 'ks3-cn-guangzhou', + HONGKONG: 'ks3-cn-hk-1', + RUSSIA: 'ks3-rus', + SINGAPORE: 'ks3-sgp' + } + + cos.endpoint = regionMap[bucketInfo.region] + '.ksyuncs.com' + cos.config({ + baseUrl: cos.endpoint + }) + console.log('KS3 BUCKET', this) + } + + /** + * 获取bucket访问权限 + * 获取资源 + * @param vm => page + */ + bindPage(vm) { + this.vm = vm + this.paging = this.vm.paging + + this.getACL() + } + + getACL() { + this.cos.bucket.getACL({ + Bucket: this.name + }, (err, data) => { + if (err) { + console.error("Error", err); + } else if (data) { + let isPrivate = true + const grant = data.AccessControlPolicy.AccessControlList.Grant + const grantArray = Array.isArray(grant) ? grant : [grant] + grantArray.forEach(item => { + if(item.Grantee.URI && item.Grantee.URI.indexOf('AllUsers') !== -1) { + isPrivate = false + return + } + }) + this.setPermission(isPrivate ? 1 : 0); + this.getResources(); + } + }) + } + + createFile(_param, type, callback) { + let params = { + Bucket: this.name, + Key: _param.key, + // Body: fs.createReadStream(_param.path), + filePath: _param.path, + ContentLength: fs.statSync(_param.path).size, + } + this.cos.object + .put(params, function (err, data) { + callback(err, { key: _param.key }) + }) + } + + // todo 支持批量 + removeFile(items, callback) { + let count = items.length; + console.log(items) + items.forEach((item) => { + let params = { + Bucket: this.name, + Key: item.Key + } + this.cos.object.del(params, function(){ + count = count - 1; + console.log(count) + if(count === 0) { + callback && callback() + } + }) + }) + } + + async renameFile(items, callback) { + const item = items[0] + this.cos.object.put({ + Bucket: this.name, + Key: item._key // new key + }, (err, data) => { + if(err) { + callback && callback({error: '重命名|创建拷贝失败'}) + } else { + this.cos.object.del({ + Bucket: this.name, + Key: item.Key + }, function(err, data){ + if(err) { + callback && callback({error: '重命名|删除旧文件失败'}) + return + } + callback && callback() + }) + } + }, { + 'x-kss-copy-source': `/${this.name}/${item.Key}` + }) + } + + async getResources(option = {}) { + await super.preResources() + //delimiter + let params = { + Bucket: this.name, + } + + this._handleParams(params, option, { + limit: 'max-keys', + }) + + this.cos.bucket.get(params, (error, data) => { + console.log('list objects: ', data) + let files = [] + if(data.ListBucketResult.Contents) { + if(!Array.isArray(data.ListBucketResult.Contents)){ + data.ListBucketResult.Contents = [data.ListBucketResult.Contents] + } + data.ListBucketResult.Contents.forEach(item => { + if (parseInt(item.Size) !== 0) { + files.push(util.convertMeta(item, brand.ks3.key)) + } + }) + } + + data.ListBucketResult.CommonPrefixes && data.ListBucketResult.CommonPrefixes.forEach(item => { + files.push(this._getFolder(item.Prefix)) + }) + + this.postResources( + { + items: files, + marker: JSON.parse(data.ListBucketResult.IsTruncated) && data.ListBucketResult.Contents.slice(-1)[0].Key + }, + option + ) + }) + } + + /** + * 返回资源真实链接 + * @param index + * @param key + * @param deadline 私有模式,文件有效时间 + * @returns {*} + */ + generateUrl(key, expires) { + let url = `${this.cos.endpoint}/${this.name}/${key}` + + if (this.permission === 1) { + // kind of hack + const bucketName = this.name; + const objectName = encodeURIComponent(key); + const ak = this.cos.ak; + const sk = this.cos.sk; + const deadline = Math.ceil(new Date().getTime()/1000) + expires; + const token = encodeURIComponent(this.cos.auth.getQueryStringSignature(sk, deadline, bucketName, objectName)) + + url = `http://${this.cos.endpoint}/${bucketName}/${objectName}?AccessKeyId=${ak}&Expires=${deadline}&Signature=` + token + } + + return super.generateUrl(url) + } +} + +export default Bucket diff --git a/src/renderer/pages/Login.vue b/src/renderer/pages/Login.vue index 53c6d26..adcd2c2 100644 --- a/src/renderer/pages/Login.vue +++ b/src/renderer/pages/Login.vue @@ -143,6 +143,10 @@ {{ item.name }}->Access Key + @@ -199,6 +203,8 @@ export default { this.regions = Regions.s3 } else if (key === this.brands.jd.key) { this.regions = Regions.jd + } else if (key === this.brands.ks3.key) { + this.regions = Regions.ks3 } }, handleSubmit(key) { diff --git a/src/renderer/service/util.js b/src/renderer/service/util.js index 323a399..7493187 100644 --- a/src/renderer/service/util.js +++ b/src/renderer/service/util.js @@ -212,6 +212,11 @@ export function convertMeta(item, brandKey = 'qiniu') { item.fsize = parseInt(item.Size) item.putTime = new Date(item.LastModified).getTime() break + case brand.ks3.key: + item.key = item.Key + item.fsize = parseInt(item.Size) + item.putTime = new Date(item.LastModified).getTime() + break } item.mimeType = mime.lookup(item.key) || '' diff --git a/static/iconfont.css b/static/iconfont.css index 5ab98e5..7ad94cc 100644 --- a/static/iconfont.css +++ b/static/iconfont.css @@ -10,6 +10,10 @@ -moz-osx-font-smoothing: grayscale; } +.icon-ks3:before { + content: "ks3"; +} + .icon-jd:before { content: "\e701"; }