├── index.js ├── .gitignore ├── lib ├── core │ ├── account │ │ ├── index.js │ │ ├── rm.js │ │ ├── ls.js │ │ ├── add.js │ │ └── use.js │ ├── bucket │ │ ├── index.js │ │ ├── rm.js │ │ ├── ls.js │ │ ├── use.js │ │ └── add.js │ ├── index.js │ └── upload.js ├── db.js ├── utils.js └── cli.js ├── circle.yml ├── .eslintrc ├── package.json └── readme.md /index.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | *.log 3 | db.json -------------------------------------------------------------------------------- /lib/core/account/index.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | add: require('./add'), 3 | ls: require('./ls'), 4 | use: require('./use'), 5 | rm: require('./rm') 6 | } -------------------------------------------------------------------------------- /lib/core/bucket/index.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | add: require('./add'), 3 | ls: require('./ls'), 4 | use: require('./use'), 5 | rm: require('./rm') 6 | } -------------------------------------------------------------------------------- /circle.yml: -------------------------------------------------------------------------------- 1 | machine: 2 | node: 3 | version: 4.2.1 4 | 5 | dependencies: 6 | pre: 7 | - npm install -g eslint 8 | 9 | test: 10 | override: 11 | - npm run test -------------------------------------------------------------------------------- /lib/core/account/rm.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const db = require('../../db') 4 | const colors = require('colors') 5 | 6 | module.exports = function(name){ 7 | db('accounts').remove({name}) 8 | console.log(colors.green(`Account ${name} was removed.`)) 9 | } -------------------------------------------------------------------------------- /lib/db.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const low = require('lowdb') 4 | const storage = require('lowdb/file-sync') 5 | const path = require('path') 6 | 7 | const dbPath = path.resolve(__dirname, '..', 'db.json') 8 | const db = low(dbPath, { storage }) 9 | 10 | module.exports = db -------------------------------------------------------------------------------- /lib/core/account/ls.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const colors = require('colors') 4 | const db = require('../../db') 5 | 6 | 7 | module.exports = function(){ 8 | 9 | 10 | // let accounts = db('accounts').value() 11 | if (db('accounts').size() === 0) { 12 | console.log('No account yet! Use `$ cown add -a` to add an account.') 13 | } else { 14 | db('accounts').value().map((account)=>{ 15 | account.using ? console.log(colors.green(`* ${account.name}`)) : console.log(` ${account.name}`) 16 | }) 17 | } 18 | } -------------------------------------------------------------------------------- /lib/core/bucket/rm.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const db = require('../../db') 4 | const colors = require('colors') 5 | 6 | module.exports = function(name){ 7 | let usingAccount = db('accounts').chain().find({using: true}) 8 | 9 | const buckets = usingAccount.value().buckets 10 | 11 | buckets.map((item, index)=>{ 12 | if (item.name === name) { 13 | buckets.splice(index, 1) 14 | usingAccount.assign({ buckets }).value() 15 | } 16 | }) 17 | 18 | console.log(colors.green(`Bucket ${name} was removed.`)) 19 | } -------------------------------------------------------------------------------- /lib/core/bucket/ls.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const db = require('../../db') 4 | const colors = require('colors') 5 | 6 | module.exports = function(){ 7 | let usingAccount = 8 | db('accounts') 9 | .chain() 10 | .find({using: true}) 11 | .value() 12 | 13 | console.log(`Buckets of ${usingAccount.name}:`) 14 | 15 | usingAccount.buckets.map((bucket)=>{ 16 | if (bucket.isDefault) { 17 | console.log(colors.green(`* ${bucket.name}`)) 18 | } else { 19 | console.log(` ${bucket.name}`) 20 | } 21 | }) 22 | } -------------------------------------------------------------------------------- /lib/core/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | module.exports = { 4 | use(name, options){ 5 | options.account ? require('./account').use(name) : require('./bucket').use(name) 6 | }, 7 | ls(options){ 8 | options.account ? require('./account').ls() : require('./bucket').ls() 9 | }, 10 | add(options){ 11 | options.account ? require('./account').add() : require('./bucket').add() 12 | }, 13 | rm(name, options){ 14 | options.account ? require('./account').rm(name) : require('./bucket').rm(name) 15 | }, 16 | upload: require('./upload') 17 | } -------------------------------------------------------------------------------- /lib/utils.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const colors = require('colors') 4 | 5 | module.exports = { 6 | showError(err){ 7 | switch(err.code){ 8 | case 401: 9 | console.log(colors.red(err.name + ':'), 'Token 不正确') 10 | break 11 | case 413: 12 | console.log(colors.red(err.name + ':'), '文件过大') 13 | break 14 | case 614: 15 | console.log(colors.red(err.name + ':'), '文件已存在') 16 | break 17 | case 631: 18 | console.log(colors.red(err.name + ':'), 'Bucket 不存在') 19 | break 20 | default: 21 | console.log(colors.red('Unknown Error'), err.code) 22 | break 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "eslint:recommended", 3 | "env": { 4 | "node": true 5 | }, 6 | "ecmaFeatures": { 7 | "arrowFunctions": true, 8 | "destructuring": true, 9 | "classes": true, 10 | "defaultParams": true, 11 | "blockBindings": true, 12 | "modules": true, 13 | "objectLiteralComputedProperties": true, 14 | "objectLiteralShorthandMethods": true, 15 | "objectLiteralShorthandProperties": true, 16 | "restParams": true, 17 | "spread": true, 18 | "templateStrings": true 19 | }, 20 | "rules": { 21 | "no-console": 0, 22 | "no-eval": 2, 23 | "strict": 0, 24 | "semi": [2, "never"], 25 | "default-case": 1, 26 | "object-shorthand": 1 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /lib/core/bucket/use.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const db = require('../../db') 4 | 5 | module.exports = function(name){ 6 | 7 | let usingAccount = db('accounts').chain().find({ using: true }) 8 | 9 | if (!usingAccount.value()) { 10 | console.log('No using account! Use `$ cown use -a` to use an account.') 11 | return 12 | } 13 | 14 | function toggle(collection, symbol){ 15 | return collection.map((item)=>{ 16 | if (item.name === symbol) { 17 | item.isDefault = true 18 | } else { 19 | item.isDefault = false 20 | } 21 | return item 22 | }) 23 | } 24 | 25 | usingAccount.assign({ 26 | buckets: toggle(usingAccount.value().buckets, name) 27 | }).value() 28 | 29 | require('./ls')() 30 | } -------------------------------------------------------------------------------- /lib/core/account/add.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const inquirer = require('inquirer') 4 | const db = require('../../db') 5 | 6 | module.exports = function(){ 7 | let questions = [ 8 | { 9 | type: 'input', 10 | name: 'name', 11 | message: 'Account name (just a code name):' 12 | }, 13 | { 14 | type: 'input', 15 | name: 'accessKey', 16 | message: 'Your AccessKey:' 17 | }, 18 | { 19 | type: 'input', 20 | name: 'secretKey', 21 | message: 'Your SecretKey:' 22 | } 23 | ] 24 | 25 | inquirer.prompt(questions, (answers)=>{ 26 | db('accounts').push({ 27 | name: answers.name, 28 | accessKey: answers.accessKey, 29 | secretKey: answers.secretKey, 30 | using: db('accounts').size() === 0, 31 | buckets: [] 32 | }) 33 | }) 34 | } -------------------------------------------------------------------------------- /lib/core/account/use.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const db = require('../../db') 4 | const colors = require('colors') 5 | 6 | const ls = require('./ls') 7 | 8 | module.exports = function(name){ 9 | 10 | let usingAccount = db('accounts').chain().find({using: true}) 11 | let account = db('accounts').chain().find({ name }) 12 | 13 | function useAccount(){ 14 | account.assign({using: true}).value() 15 | ls() 16 | } 17 | 18 | if (db('accounts').size() === 0) { 19 | console.log('No account yet! Use `$ cown add` to add an account.') 20 | return 21 | } 22 | 23 | if (account.value()) { 24 | if (usingAccount.value()) { 25 | usingAccount.assign({using: false}).value() 26 | } 27 | useAccount() 28 | } else { 29 | // account not exist 30 | console.log(colors.red(`Account '${name}' not exist!`)) 31 | } 32 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cown", 3 | "version": "0.1.1", 4 | "description": "Qiniu bucket manager and uploader", 5 | "main": "index.js", 6 | "bin": { 7 | "cown": "./lib/cli.js" 8 | }, 9 | "scripts": { 10 | "test": "eslint lib/*.js lib/**/*.js" 11 | }, 12 | "keywords": [ 13 | "qiniu", 14 | "qn" 15 | ], 16 | "author": "Randy Lu ", 17 | "license": "MIT", 18 | "dependencies": { 19 | "clipboard": "^1.5.5", 20 | "colors": "^1.1.2", 21 | "commander": "^2.9.0", 22 | "copy-paste": "^1.1.4", 23 | "inquirer": "^0.11.3", 24 | "io-spin": "^0.2.2", 25 | "lodash": "^4.0.0", 26 | "lowdb": "^0.12.2", 27 | "qn": "^1.1.1" 28 | }, 29 | "devDependencies": {}, 30 | "repository": { 31 | "type": "git", 32 | "url": "git+https://github.com/djyde/cown.git" 33 | }, 34 | "bugs": { 35 | "url": "https://github.com/djyde/cown/issues" 36 | }, 37 | "homepage": "https://github.com/djyde/cown#readme" 38 | } 39 | -------------------------------------------------------------------------------- /lib/cli.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 'use strict' 3 | 4 | const cown = require('commander') 5 | const core = require('./core') 6 | 7 | cown 8 | .version(require('../package.json').version) 9 | .description(' "-a" option for account mode.') 10 | 11 | cown 12 | .command('add') 13 | .option('-a, --account', 'account action') 14 | .description('Add new bucket.') 15 | .action(core.add) 16 | cown 17 | .command('ls') 18 | .option('-a, --account', 'account action') 19 | .description('List all buckets.') 20 | .action(core.ls) 21 | 22 | cown 23 | .command('use ') 24 | .option('-a, --account', 'account action') 25 | .description('Checkout bucket.') 26 | .action(core.use) 27 | 28 | cown 29 | .command('rm ') 30 | .option('-a, --account', 'account action') 31 | .description('Remove bucket.') 32 | .action(core.rm) 33 | 34 | cown 35 | .command('upload ') 36 | .description('Upload file') 37 | .option('-b, --bucket ', 'bucket') 38 | .option('-p, --copy', 'auto copy source link') 39 | .action(core.upload) 40 | 41 | cown.parse(process.argv) -------------------------------------------------------------------------------- /lib/core/bucket/add.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const inquirer = require('inquirer') 4 | const db = require('../../db') 5 | const _ = require('lodash') 6 | 7 | module.exports = function(){ 8 | 9 | if (db('accounts').size() === 0) { 10 | console.log('No account yet! Use `$ cown add -a` to add an account.') 11 | return 12 | } 13 | 14 | let usingAccount = 15 | db('accounts') 16 | .chain() 17 | .find({using: true}) 18 | 19 | if (!usingAccount) { 20 | console.log('No using account! Use `$ cown use -a` to use an account.') 21 | return 22 | } 23 | 24 | let questions = [ 25 | { 26 | type: 'input', 27 | name: 'name', 28 | message: 'Bucket name:', 29 | // TODO: bucket name not null 30 | validate: (value)=> _.find(usingAccount.value().buckets, {name: value}) ? 'Bucket existed' : true 31 | }, 32 | { 33 | type: 'input', 34 | name: 'domain', 35 | message: 'Bucket domain:' 36 | } 37 | ] 38 | 39 | inquirer.prompt(questions, (answers)=>{ 40 | const name = answers.name // bucket name 41 | const domain = answers.domain // bucket domain 42 | 43 | usingAccount.assign({ 44 | buckets: usingAccount.value().buckets.concat([{ 45 | name, domain, isDefault: usingAccount.value().buckets.length === 0 46 | }]) 47 | }).value() 48 | console.log(`Bucket ${answers.name} was added!`) 49 | }) 50 | 51 | } -------------------------------------------------------------------------------- /lib/core/upload.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const qn = require('qn') 4 | const db = require('../db') 5 | const utils = require('../utils') 6 | const colors = require('colors') 7 | const ncp = require('copy-paste') 8 | const Spin = require('io-spin') 9 | const spin = new Spin('Box2') 10 | 11 | 12 | module.exports = function(source, options){ 13 | 14 | let usingAccount = db('accounts').chain().find({using: true}) 15 | 16 | if (!usingAccount || db('accounts').size() === 0) { 17 | console.log('No account yet! Use `$ cown add` to add an account.') 18 | return 19 | } 20 | 21 | let usingBucket, origin 22 | 23 | usingAccount.value().buckets.map((bucket) => { 24 | if (options.bucket) { 25 | if (options.bucket === bucket.name) { 26 | usingBucket = bucket.name 27 | origin = bucket.domain 28 | } 29 | } else { 30 | if (bucket.isDefault) { 31 | usingBucket = bucket.name 32 | origin = bucket.domain 33 | } 34 | } 35 | }) 36 | 37 | if (!usingBucket || !origin) { 38 | console.log('No using bucket! Use `$ cown use ` to use a bucket.') 39 | return 40 | } 41 | 42 | const accessKey = usingAccount.value().accessKey 43 | const secretKey = usingAccount.value().secretKey 44 | 45 | let client = qn.create({ 46 | accessKey, secretKey, origin, 47 | bucket: usingBucket 48 | }) 49 | 50 | let filename = source.split('/')[source.split('/').length - 1] 51 | spin.start() 52 | client.uploadFile(source, {key: filename }, (err, result) => { 53 | spin.stop() 54 | if (err) { 55 | utils.showError(err) 56 | } else { 57 | console.log(colors.green('Upload success!')) 58 | console.log(result.url) 59 | 60 | if (options.copy) { 61 | ncp.copy(result.url) 62 | } 63 | } 64 | }) 65 | } -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # cown 2 | 3 | Qiniu bucket manager && upload tool 4 | 5 | ## Screenshot 6 | 7 | ![](http://blogscdn.qiniudn.com/cown.gif) 8 | 9 | ## Install 10 | 11 | ```bash 12 | $ npm install -g cown 13 | ``` 14 | 15 | ## Quick start 16 | 17 | You need to configure a Qiniu account first: 18 | 19 | ```bash 20 | $ cown add -a 21 | ``` 22 | 23 | Configure a new bucket: 24 | 25 | ```bash 26 | $ cown add 27 | ``` 28 | 29 | Upload file: 30 | 31 | ```bash 32 | $ cown upload path/to/my.png # use -p to auto copy source link 33 | ``` 34 | 35 | List all buckets: 36 | 37 | ```bash 38 | $ cown ls 39 | ``` 40 | 41 | Use another bucket: 42 | 43 | ```bash 44 | $ cown use 45 | ``` 46 | 47 | Remove a bucket: 48 | 49 | ```bash 50 | $ cown rm 51 | ``` 52 | 53 | ## Usage 54 | 55 | ### Mode 56 | 57 | There are two modes in `cown`, one is `bucket mode`, the other is `account mode`. Using `-a` option to run in `account mode`. 58 | 59 | `cown` is running on `bucket mode` by default. Because we always operate bucket instead of account. 60 | 61 | For example, when we run `$ cown add`, you will add a new bucket. But if you run it with a `-a` option (`$ cown add -a`), you will be going to add a new account instead. 62 | 63 | All action can be run in `account mode` except `upload` action. 64 | 65 | ### Commands 66 | 67 | ```bash 68 | Commands: 69 | 70 | add [options] Add new bucket. 71 | ls [options] List all buckets. 72 | use [options] Checkout bucket. 73 | rm [options] Remove bucket. 74 | upload [options] Upload file 75 | 76 | "-a" option for account mode. 77 | 78 | Options: 79 | 80 | -h, --help output usage information 81 | -V, --version output the version number 82 | ``` 83 | 84 | # License 85 | 86 | MIT License 87 | 88 | --------------------------------------------------------------------------------