├── .gitignore ├── CHANGELOG.txt ├── Gruntfile.js ├── LICENSE ├── README.md ├── lib ├── helpers.js └── node_dropbox.js ├── package.json └── test ├── config └── config.json └── test.js /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules -------------------------------------------------------------------------------- /CHANGELOG.txt: -------------------------------------------------------------------------------- 1 | VERSION 0.1.0: 2 | 3 | - Initial Commit 4 | - Created README 5 | - Tests are passing 6 | 7 | VERSION 0.1.1: 8 | 9 | - Added removeFile() 10 | - Renamed api.fileops.removeFolder to just remove. 11 | - Updated README -------------------------------------------------------------------------------- /Gruntfile.js: -------------------------------------------------------------------------------- 1 | module.exports = function(grunt) { 2 | 3 | grunt.loadNpmTasks('grunt-mocha-test'); 4 | grunt.loadNpmTasks('grunt-contrib-watch'); 5 | grunt.loadNpmTasks('grunt-clear'); 6 | 7 | grunt.initConfig({ 8 | mochaTest: { 9 | test: { 10 | options: { 11 | reporter: 'spec' 12 | }, 13 | src: ['test/test.js'] 14 | } 15 | }, 16 | watch: { 17 | files: ['test/**', 'lib/**'], 18 | tasks: ['clear', 'mochaTest'] 19 | } 20 | }); 21 | 22 | grunt.registerTask('default', 'mochaTest'); 23 | 24 | } 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 2 | Version 2, December 2004 3 | 4 | Copyright (C) 2004 Sam Hocevar 5 | 6 | Everyone is permitted to copy and distribute verbatim or modified 7 | copies of this license document, and changing it is allowed as long 8 | as the name is changed. 9 | 10 | DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 11 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 12 | 13 | 0. You just DO WHAT THE FUCK YOU WANT TO. 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Node Dropbox 2 | 3 | A simple node.js API client for the Dropbox API. 4 | 5 | ## Installation 6 | 7 | npm install node-dropbox --save 8 | 9 | ## Usage: 10 | 11 | Before starting anything you need to go create your app over at: https://www.dropbox.com/developers/apps then go ahead and grab your Key and Secret. 12 | 13 | #### Authentication 14 | 15 | Just use the Authenticate method to generate a url for your user to go to. This will be redirected back to another page. 16 | 17 | var node_dropbox = require('node-dropbox'); 18 | node_dropbox.Authenticate('key', 'secret', 'redirect_url', function(err, url){ 19 | // redirect user to the url. 20 | // looks like this: "https://www.dropbox.com/1/oauth2/authorize?client_id=&response_type=code&redirect_uri=" 21 | }); 22 | 23 | On the page where you redirected to, you will need to use the AccessToken method to get the users access token for api use. The redirect_url this time is only for validation, it will not need to redirect again. 24 | 25 | node_dropbox.AccessToken('key', 'secret', 'access_code', 'redirect_url', function(err, body) { 26 | access_token = body.access_token; 27 | }); 28 | 29 | #### Make API Calls 30 | 31 | When you have the access token in hand, all you need to do is make api calls. That's all it takes to get started with this client. 32 | 33 | api = node_dropbox.api(access_token); 34 | api.account(function(err, res, body) { 35 | console.log(body); 36 | }); 37 | 38 | The above output will be something like: 39 | 40 | { 41 | "referral_link": "https://www.dropbox.com/referrals/r1a2n3d4m5s6t7", 42 | "display_name": "John P. User", 43 | "uid": 12345678, 44 | "team": { 45 | "name": "Acme Inc." 46 | }, 47 | "country": "US", 48 | "quota_info": { 49 | "shared": 253738410565, 50 | "quota": 107374182400000, 51 | "normal": 680031877871 52 | } 53 | } 54 | 55 | ## Available API Calls: 56 | 57 | api.account(callback); // Fetches the account information. 58 | api.createDir(path, callback); // Creates a directory. 59 | api.removeDir(path, callback); // Deletes a directory. 60 | api.createFile(path, contents, callback); // Creates a new file. 61 | api.removeFile(path, callback) // Deletes a file. 62 | api.moveSomething(from_path, to_path, callback); // Moves/renames a file. 63 | api.getMetadata(path, callback) // Retrieves file and folder metadata. Can be used to list a folder's content. 64 | api.getFile(path, callback) // Downloads a file. 65 | api.getDelta(cursor, path, callback) // Gets changes to files and folders in a user's Dropbox. 66 | 67 | // Each callback will return the error message, response, and body(json data). 68 | api.account(function(error, response, body){ 69 | console.log(body.display_name); 70 | }); 71 | 72 | ## Planned Features: 73 | 74 | I plan on implementing the Dropbox Core API features(https://www.dropbox.com/developers/core/docs) and revamping the code a little bit to make it integrate with express easily or possibly just make it more friendly to use. Right now, it's not the best, but it works. 75 | -------------------------------------------------------------------------------- /lib/helpers.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/g33kidd/node-dropbox/b8ec8c298696919d28b65b6d7ad4e0973e5aedec/lib/helpers.js -------------------------------------------------------------------------------- /lib/node_dropbox.js: -------------------------------------------------------------------------------- 1 | var request = require('request'); 2 | 3 | var authUrl = "https://www.dropbox.com/1/oauth2/authorize?"; 4 | var apiRoot = "https://api.dropbox.com/1/"; 5 | var apiContentRoot = "https://api-content.dropbox.com/1/"; 6 | var apiLongPoll = "https://notify.dropboxapi.com/1/longpoll_delta"; 7 | var api = { 8 | auth: { 9 | disable_token: '/disable_access_token' 10 | }, 11 | account: '/account/info', 12 | metadata: '/metadata', 13 | fileops: { 14 | createFolder: '/fileops/create_folder', 15 | remove: '/fileops/delete', 16 | copy: '/fileops/copy', 17 | move: '/fileops/move' 18 | }, 19 | putFiles: '/files_put', 20 | getFiles: '/files' 21 | }; 22 | 23 | exports.Authenticate = function(ckey, csecret, redirect_uri, cb) { 24 | var err = ""; 25 | var redirect_url = ""; 26 | 27 | if(ckey === "" || csecret === "" || redirect_uri === "") { 28 | err = "Missing client key and/or client secret key."; 29 | }else{ 30 | redirect_url = authUrl + "client_id=" + ckey + "&response_type=code&redirect_uri=" + redirect_uri; 31 | } 32 | 33 | cb(err, redirect_url); 34 | } 35 | 36 | exports.AccessToken = function(ckey, csecret, auth_code, redirect_url, cb) { 37 | var url = apiRoot + '/oauth2/token'; 38 | var body = { 39 | "code": auth_code, 40 | "grant_type": "authorization_code", 41 | "redirect_uri": redirect_url, 42 | "client_id": ckey, 43 | "client_secret": csecret 44 | }; 45 | 46 | request.post(url, {form: body, json: true}, function(err, res, body) { 47 | cb(err, body); 48 | }); 49 | } 50 | 51 | exports.api = function(access_token) { 52 | 53 | var access_token = access_token; 54 | 55 | return { 56 | account: function(cb) { 57 | options = optionsBuilder(apiRoot + api.account, access_token); 58 | request.get(options, function(err, res, body) { 59 | cb(err, res, body); 60 | }) 61 | }, 62 | 63 | createDir: function(path, cb) { 64 | options = postBuilder(apiRoot + api.fileops.createFolder, 65 | {root:"auto", path:path}, access_token); 66 | request.post(options, function(err, res, body) { 67 | cb(err, res, body); 68 | }) 69 | }, 70 | 71 | removeDir: function(path, cb) { 72 | options = postBuilder(apiRoot + api.fileops.remove, 73 | {root:"auto", path:path}, access_token); 74 | request.post(options, function(err, res, body) { 75 | cb(err, res, body); 76 | }); 77 | }, 78 | 79 | moveSomething: function(from, to, cb) { 80 | options = postBuilder(apiRoot + api.fileops.move, 81 | {root:"auto", from_path:from, to_path:to}, access_token); 82 | request.post(options, function(err, res, body) { 83 | cb(err, res, body); 84 | }); 85 | }, 86 | 87 | createFile: function(path, body, cb) { 88 | options = { 89 | method: "PUT", 90 | url: apiContentRoot + api.putFiles + "/auto/" + path, 91 | headers: { 92 | "Content-Length": getByteLength(body), 93 | "Authorization": "Bearer " + access_token 94 | }, 95 | json: true, 96 | body: body 97 | } 98 | 99 | request(options, function(err, res, body) { 100 | cb(err, res, body); 101 | }) 102 | }, 103 | 104 | removeFile: function(path, cb) { 105 | options = postBuilder(apiRoot + api.fileops.remove, 106 | {root: "auto", path:path}, access_token); 107 | request.post(options, function(err, res, body) { 108 | cb(err, res, body); 109 | }); 110 | }, 111 | 112 | /** 113 | * get the metadata of a file or folder 114 | * the result body.contents can be used to list a folder's content 115 | */ 116 | getMetadata: function(path, cb) { 117 | options = optionsBuilder(apiRoot + api.metadata + "/auto/" + path, access_token); 118 | request.get(options, function(err, res, body) { 119 | cb(err, res, body); 120 | }); 121 | }, 122 | 123 | /** 124 | * Downloads a file. 125 | */ 126 | getFile: function(path, cb) { 127 | options = optionsBuilder(apiContentRoot + api.getFiles + '/auto' + path, access_token); 128 | 129 | request(options, function(err, res, body) { 130 | cb(err, res, body); 131 | }) 132 | }, 133 | 134 | /** 135 | * Let connect pipe to downloaded file. 136 | */ 137 | getFilePipe: function(path, cb) { 138 | options = optionsBuilder(apiContentRoot + api.getFiles + '/auto' + path, access_token); 139 | 140 | return request(options, cb) 141 | }, 142 | 143 | /** 144 | * Gets changes to files and folders in a user's Dropbox. 145 | */ 146 | getDelta: function(cursor, path, cb) { 147 | options = postBuilder(apiRoot + '/delta', 148 | {cursor:cursor, path_prefix:path}, access_token); 149 | request.post(options, function(err, res, body) { 150 | cb(err, res, body); 151 | }) 152 | }, 153 | 154 | /** 155 | * Gets notifications from server 156 | */ 157 | getNotifications: function(cursor, timeout, cb) { 158 | var cursor = cursor || null, 159 | timeout = timeout || 480, 160 | url = apiLongPoll + '?cursor=' + cursor + '&timeout=' + timeout; 161 | 162 | request.get(url, function(err, res, body) { 163 | cb(err, res, body); 164 | }); 165 | } 166 | } 167 | 168 | } 169 | 170 | function optionsBuilder(url, access_token) { 171 | return { 172 | url: url, 173 | json: true, 174 | headers: { 175 | "Authorization": "Bearer " + access_token 176 | } 177 | } 178 | } 179 | 180 | function postBuilder(url, data, access_token) { 181 | return { 182 | url: url, 183 | json: true, 184 | headers: { 185 | "Authorization": "Bearer " + access_token 186 | }, 187 | form: data 188 | } 189 | } 190 | 191 | function getByteLength(normal_val) { 192 | // Force string type 193 | normal_val = String(normal_val); 194 | 195 | var byteLen = 0; 196 | for (var i = 0; i < normal_val.length; i++) { 197 | var c = normal_val.charCodeAt(i); 198 | byteLen += c < (1 << 7) ? 1 : 199 | c < (1 << 11) ? 2 : 200 | c < (1 << 16) ? 3 : 201 | c < (1 << 21) ? 4 : 202 | c < (1 << 26) ? 5 : 203 | c < (1 << 31) ? 6 : Number.NaN; 204 | } 205 | return byteLen; 206 | } 207 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "node-dropbox", 3 | "description": "A simple Dropbox API client for node.js", 4 | "version": "0.1.8", 5 | "author": "Joshua Kidd", 6 | "keywords": [ 7 | "dropbox", 8 | "api", 9 | "node" 10 | ], 11 | "main": "./lib/node_dropbox.js", 12 | "repository": { 13 | "type": "git", 14 | "url": "https://github.com/g33kidd/node-dropbox" 15 | }, 16 | "dependencies": { 17 | "request": "^2.40.0" 18 | }, 19 | "devDependencies": { 20 | "grunt": "^0.4.5", 21 | "grunt-clear": "^0.2.1", 22 | "grunt-contrib-watch": "^0.6.1", 23 | "grunt-mocha-test": "^0.11.0", 24 | "mocha": "^1.21.4", 25 | "should": "^4.0.4" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /test/config/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "app_key": "0jr750as3jczzcg", 3 | "app_secret": "jqpeifmo57q5fh4", 4 | "access_token": "your-key-here", 5 | "redirect_url": "http://localhost:3000/dropbox_callback" 6 | } 7 | -------------------------------------------------------------------------------- /test/test.js: -------------------------------------------------------------------------------- 1 | var fs = require("fs"); 2 | var should = require("should"); 3 | var node_dropbox = require('../'); 4 | 5 | describe("Node Dropbox", function() { 6 | var config = JSON.parse(fs.readFileSync(__dirname + '/config/config.json')) 7 | var api, ref; 8 | 9 | before(function(done) { 10 | api = node_dropbox.api(config.access_token); 11 | done(); 12 | }) 13 | 14 | describe("#Authenticate", function() { 15 | 16 | it("should create valid auth url", function(done) { 17 | link = "https://www.dropbox.com/1/oauth2/authorize?client_id=" + config.app_key + "&response_type=code&redirect_uri=" + config.redirect_url; 18 | node_dropbox.Authenticate(config.app_key, config.app_secret, config.redirect_url, function(err, url) { 19 | url.should.eql(link); 20 | err.should.eql(""); 21 | done(); 22 | }) 23 | }) 24 | 25 | it("should validate params", function(done) { 26 | node_dropbox.Authenticate(config.app_key, "", config.redirect_url, function(err, url) { 27 | err.should.eql("Missing client key and/or client secret key."); 28 | done(); 29 | }) 30 | }) 31 | 32 | }) 33 | 34 | describe("#API", function() { 35 | this.timeout(15000); 36 | 37 | it("should get account object", function(done) { 38 | api.account(function(err, res, body) { 39 | res.statusCode.should.eql(200); 40 | body.should.have.property("display_name"); 41 | body.should.have.property("email"); 42 | done(); 43 | }) 44 | }) 45 | 46 | it("should create a directory", function(done) { 47 | api.createDir("myfirstdir", function(err, res, body) { 48 | res.statusCode.should.eql(200); 49 | body.should.have.property("path", "/myfirstdir"); 50 | done(); 51 | }) 52 | }) 53 | 54 | it("should get metadata of directory", function(done) { 55 | api.getMetadata("myfirstdir", function(err, res, body) { 56 | res.statusCode.should.eql(200); 57 | body.should.have.property("is_dir", true); 58 | done(); 59 | }) 60 | }) 61 | 62 | it("should remove a directory", function(done) { 63 | api.removeDir("myfirstdir", function(err, res, body) { 64 | res.statusCode.should.eql(200); 65 | body.should.have.property("path", "/myfirstdir"); 66 | done(); 67 | }) 68 | }) 69 | 70 | it("should create a file", function(done) { 71 | api.createFile("test_file.txt", "Test Content", function(err, res, body) { 72 | res.statusCode.should.eql(200); 73 | body.should.have.property("path", "/test_file.txt"); 74 | done(); 75 | }) 76 | }) 77 | 78 | it("should get metadata of file", function(done) { 79 | api.getMetadata("test_file.txt", function(err, res, body) { 80 | res.statusCode.should.eql(200); 81 | body.should.have.property("is_dir", false); 82 | done(); 83 | }) 84 | }) 85 | 86 | it("should move a file", function(done) { 87 | api.moveSomething("test_file.txt", "renamed_testfile.txt", function(err, res, body) { 88 | res.statusCode.should.eql(200); 89 | body.should.have.property("path", "/renamed_testfile.txt"); 90 | done(); 91 | }) 92 | }) 93 | 94 | it("should remove a file", function(done) { 95 | api.removeFile("renamed_testfile.txt", function(err, res, body) { 96 | res.statusCode.should.eql(200); 97 | body.should.have.property("path", "/renamed_testfile.txt"); 98 | body.should.have.property("is_deleted", true); 99 | done(); 100 | }) 101 | }) 102 | 103 | it.skip("should get delta of file", function(done) {}) 104 | 105 | }) 106 | 107 | }); 108 | --------------------------------------------------------------------------------