From ca951546b6d011e50b5e880e5cdc41c141798084 Mon Sep 17 00:00:00 2001 From: emizzle Date: Wed, 12 Sep 2018 11:55:22 +1000 Subject: [PATCH] Rename to swarm-api, swarmapi CLI updated Renamed library to swarm-api to appease npm swarmapi NodeJS CLI bin updated to use all of the newest functions in the library README updated with API doc --- .babelrc | 7 +- .gitignore | 4 +- README.md | 102 +++++++++++++++++++------ bin/swarmapi | 162 +++++++++++++++++++++++++++++----------- index.html | 2 +- package.json | 20 ++--- src/shared.js | 5 ++ src/standalone/index.js | 2 +- webpack.config.js | 2 +- 9 files changed, 219 insertions(+), 87 deletions(-) diff --git a/.babelrc b/.babelrc index a33209a..6e1d7c9 100644 --- a/.babelrc +++ b/.babelrc @@ -4,11 +4,9 @@ "env": { "browser": { "ignore": [ - "src/swarmapi.js", "src/index.js", "src/node/index.js", - "src/standalone/index.js", - "bin/swarmapi" + "src/standalone/index.js" ], "plugins": [ ["@babel/plugin-transform-runtime", { @@ -26,8 +24,7 @@ "node": { "ignore": [ "src/browser.js", - "src/standalone/index.js", - "bin/swarmapi" + "src/standalone/index.js" ], "plugins": [ ["@babel/plugin-transform-runtime", { diff --git a/.gitignore b/.gitignore index 0e34146..25fd632 100644 --- a/.gitignore +++ b/.gitignore @@ -33,8 +33,8 @@ package .vscode .eslintrc.json -swarmapi.min.js -swarmapi-*.tgz +swarm-api.min.js +swarm-api-*.tgz NOTES npm-debug.log TODO diff --git a/README.md b/README.md index 2dfc46c..f73b105 100644 --- a/README.md +++ b/README.md @@ -1,58 +1,57 @@ -# SwarmAPI +# SwarmJS A javascript library for interacting with [Swarm](https://swarm-guide.readthedocs.io/en/latest/), a decentralised and distributed storage platform. -Under the hood, SwarmAPI uses the Swarm HTTP API to communicate with the Swarm gateway. +Under the hood, SwarmJS uses the Swarm HTTP API to communicate with the Swarm gateway. ## Installation ``` -npm install swarmapi --save +npm install swarmjs --save ``` -## Basic usage -First, import SwarmAPI. +## JS usage +First, import SwarmJS. Using **CommonJS**: ``` -const SwarmAPI = require('swarmapi'); +const SwarmJS = require('swarmjs'); ``` Or, with **ES6**: ``` -import SwarmAPI from 'swarmapi'; +import SwarmJS from 'swarmjs'; ``` -Then instantiate SwarmAPI, specifying the gateway you'd like to connect to. *If you are [running your own node](https://swarm-guide.readthedocs.io/en/latest/gettingstarted.html) (recommended), the node should be started before proceeding.* +Then instantiate SwarmJS, specifying the gateway you'd like to connect to. *If you are [running your own node](https://swarm-guide.readthedocs.io/en/latest/gettingstarted.html) (recommended), the node should be started before proceeding.* ``` -// instantiate SwarmAPI -const swarmapi = new SwarmAPI({ gateway: 'http://localhost:8500' }); +// instantiate SwarmJS +const swarmjs = new SwarmJS({ gateway: 'http://localhost:8500' }); ``` Available options: | Option | Description | Default | | -----| ------------| ------- | -| `gateway` | URL of the Swarm gateway, ie `http://localhost:8500`. | `https://swarm-gateways.net` | +| `gateway` | URL of the Swarm gateway, ie `http://localhost:8500`. | `swarm-gateways.net` | +| `mode` | Protocol of the default gateway URL. If `gateway` is provided, this has no effect. | `https` | -NOTE: If no options are provided, the default gateway URL will be `https://swarm-gateways.net`. This means you don't necessarily need to [run your own Swarm node](https://swarm-guide.readthedocs.io/en/latest/gettingstarted.html), however there is an upload limit of ~2.5MB and no guarantees regarding permanence. *It is recommended to run your own Swarm node.* +NOTE: If no options are provided, the default gateway URL will be `https://swarm-gateways.net`. This means you don't necessarily need to [run your own Swarm node](https://swarm-guide.readthedocs.io/en/latest/gettingstarted.html), however there is an upload limit of ~2.5MB and no guarantees about permanence. It is recommended to run your own Swarm node. ##### Check gateway availability ``` -// Check gateway availability -swarmapi.isAvailable((err, isAvailable) => { +swarmjs.isAvailable((err, isAvailable) => { if(err) return console.error('Error checking Swarm availability', err); console.log(`Gateway at 'http://localhost:8500' is ${isAvailable ? '' : 'un'}available`); }); -// > Gateway at 'http://localhost:8500' is available +// Gateway at 'http://localhost:8500' is available ``` ##### Upload of raw content ``` -// Upload of raw content let testHash; -swarmapi.uploadRaw('test', (err, hash) => { +swarmjs.uploadRaw('test', (err, hash) => { if(err) return console.error('Error uploading contents', err); testHash = hash; console.log(`test can now be accessed from 'http://localhost:8500/bzz-raw:/${hash}'`); }); -// > test can now be accessed from 'http://localhost:8500/bzz-raw:/6de1faa7d29b1931b4ba3d44befcf7a5e43e947cd0bf2db154172bac5ecac3a6' +// test can now be accessed from 'http://localhost:8500/bzz-raw:/6de1faa7d29b1931b4ba3d44befcf7a5e43e947cd0bf2db154172bac5ecac3a6' ``` ##### Upload file +If you want to upload a file, first read the file's contents, then upload as the raw contents ``` -// If you want to upload a file, first read the file's contents, then upload as the raw contents const fs = require('fs'); fs.readFile('./index.html', (err, data) => { if (err) throw err; @@ -61,24 +60,79 @@ fs.readFile('./index.html', (err, data) => { console.log(`file contents can now be accessed from 'http://localhost:8500/bzz-raw:/${hash}'`); }); }); -// > file contents can now be accessed from 'http://localhost:8500/bzz-raw:/178739cbbd084e90ae0cef3f95e4b92baa85e83edb1a52d28dc370277db9d457' +// file contents can now be accessed from 'http://localhost:8500/bzz-raw:/178739cbbd084e90ae0cef3f95e4b92baa85e83edb1a52d28dc370277db9d457' ``` ##### Upload directory (only available in NodeJS, not available in the browser!) Once uploaded, the hash of **the manifest** is returned. Accessing the manifest from `bzz:/` will not return anything. Instead, you can use the hash as the root of your directory and access the individual files by appending them to the end of the URL. So, if we uploaded a directory containing `index.html`, we will be able to access it via `/bzz://index.html`. ``` // upload the dist folder, and for this example, we can assume this folder contains index.html and dapp.css -swarm.uploadDirectory('dist/', (err, hash) => { +swarm.uploadDirectory('dist/', null, (err, hash) => { if(err) return console.error('Error uploading directory', err); console.log(`We can now access our directory via 'http://localhost:8500/bzz:/${hash}/index.html' and 'http://localhost:8500/bzz:/${hash}/dapp.css'`); }); -// > We can now access our directory via 'http://localhost:8500/bzz:/26089099a5f473dfb7b172de6558972989f8db4d3948daedbb974025be7c8534/index.html' and 'http://localhost:8500/bzz:/26089099a5f473dfb7b172de6558972989f8db4d3948daedbb974025be7c8534/dapp.css' +// We can now access our directory via 'http://localhost:8500/bzz:/26089099a5f473dfb7b172de6558972989f8db4d3948daedbb974025be7c8534/index.html' and 'http://localhost:8500/bzz:/26089099a5f473dfb7b172de6558972989f8db4d3948daedbb974025be7c8534/dapp.css' ``` ##### Download content ``` // Download content via hash -swarmapi.downloadRaw(testHash, (err, content) => { +swarmjs.downloadRaw(testHash, (err, content) => { if(err) return console.error(err); console.log(`contents of our test: ${content}`); }); -// > contents of our test: test +// contents of our test: test ``` +## CLI +### Installation +SwarmAPI can be installed globally to be accessed from the CLI +``` +npm install -g swarm-api +``` +Or installed locally to be accessed from the current folder +``` +npm install swarm-api +``` +### CLI Usage +##### Check gateway availability +``` +swarmapi ping --gateway http://localhost:8500 +// [http://localhost:8500] The Swarm gateway at http://localhost:8500 is available +``` +##### Upload of raw content +``` +swarmapi uploadraw 'Hello World' --gateway http://localhost:8500 +// [http://localhost:8500] Swarm hash: a294d3a33dc0b4d9a12fdb48f5a7aed5f8dd2d8fc6a4253487c63012210e210e +``` +##### Upload file +``` +swarmapi uploadfile index.html --gateway http://localhost:8500 +// [http://localhost:8500] Swarm hash: 91a3e25c9cec6d470eeaff24f2e5b5e56b87482eb289da8041b1f97a3dbbd39f +``` +##### Upload directory (only available in NodeJS, not available in the browser!) +Once uploaded, the hash of **the manifest** is returned. Accessing the manifest from `bzz:/` will 404. Instead, you can use the hash as the root of your directory and access the individual files by appending them to the end of the URL, then call `swarmapi download `. +So, if our uploaded directory looked like: +``` +├── dist +│ └── root.html +│ ├── folder +│ │ └── index.js +``` +Run the upload directory command +``` +swarmapi uploaddir dist --gateway http://localhost:8500 +// [http://localhost:8500] Swarm manifest hash: 06da636c60c4a5472b209c71e35170044e8cf939aca6acfec765514f33a72b87. Resources can be retreived using bzz:///, for example (assuming /index.html was uploaded): +// swarmapi download bzz:/06da636c60c4a5472b209c71e35170044e8cf939aca6acfec765514f33a72b87/index.html -g http://localhost:8500 +``` +We will be able to access the `root.html` file via +``` +swarmapi download bzz:/06da636c60c4a5472b209c71e35170044e8cf939aca6acfec765514f33a72b87/root.html -g http://localhost:8500 +``` +And the `folder/index.js` file via +``` +swarmapi download bzz:/06da636c60c4a5472b209c71e35170044e8cf939aca6acfec765514f33a72b87/folder/index.js -g http://localhost:8500 +``` + +##### Download content +``` +swarmapi downloadraw a294d3a33dc0b4d9a12fdb48f5a7aed5f8dd2d8fc6a4253487c63012210e210e -g http://localhost:8500 +// Hello World +``` \ No newline at end of file diff --git a/bin/swarmapi b/bin/swarmapi index c4e24f6..9ee52c9 100755 --- a/bin/swarmapi +++ b/bin/swarmapi @@ -1,68 +1,144 @@ #!/usr/bin/env node -const SwarmAPI = require('../src/index.js'); -const swarmhash = require('swarmhash'); -const fs = require('fs'); +const SwarmAPI = require('../dist/node/index.js'); +require('colors'); const yargs = require('yargs') .usage('Usage: $0 [command]') - .command('get ', 'Download content', { - gateway: { - alias: 'g', - default: 'http://swarm-gateways.net' - }, - hash: { - alias: 'h' + .command('ping', 'Check if the gateway is running and available') + .command('download ', 'Download content from a given URI', { + uri: { + alias: 'u', + description: 'GETs a resource at the given URI, ie bzz:/', + demandOption: true } }) - .command('put ', 'Upload file', { - gateway: { - alias: 'g', - default: 'http://swarm-gateways.net' - }, + .command('downloadraw ', 'Download content') + .command('upload ', 'POST content to the given URI', { + uri: { + alias: 'u', + description: 'POSTS a resource to the given URI, ie bzz-raw:/' + } + }) + .command('uploadraw ', 'Upload content (ie file contents)', { + content: { + alias: 'c' + } + }) + .command('uploadfile ', 'Upload file', { filename: { alias: 'f' } }) - .command('calculate ', 'Calculate the hash only without uploading') + .command('uploaddir ', 'Upload directory', { + dirpath: { + alias: 'd', + description: 'path to directory, relative to the currrent working directory, ie \'dist\'' + } + }) + .command('hash', 'Calculate the hash only without uploading. If both --filename and --content are provided, --filename takes precendence.', + function (yargs) { + return yargs.option('f', { + alias: 'filename', + describe: 'calculate the hash of the specified file' + }) + .option('c', { + alias: 'content', + describe: 'calculate the hash of the specified string content. Wrap your string in quotes to get the correct hash' + }); + }, + function (argv) { + argv.content = (argv.filename || argv.content); + }) + .option('gateway', { + alias: 'g', + describe: 'Swarm gateway URI ', + demandOption: true, + default: 'https://swarm-gateways.net', + defaultDescription: 'https://swarm-gateways.net' + }) + //.demandOption(['gateway'], 'Please specify the gateway parameter with --gateway or -g, ie swarami -g http://localhost:8500 ping') + .coerce('filename', (filename) => { + return require('fs').readFileSync(filename); + }) + .alias('h', 'hash') .strict() .version() .showHelpOnFail(false, 'Specify --help for available options') .help() - .demand(1, 'Must provide a command'); + .demand(1, 'Must provide a command') + .epilogue('For more information, please see https://github.com/embark-framework/swarm-api'); const argv = yargs.argv; const gateway = argv.gateway; const command = argv._[0]; const swarmapi = new SwarmAPI({gateway}); -function abort(msg) { - console.log(msg || 'Error occured'); +function abort(msg, err) { + console.error(`[${gateway}] ${(msg || 'Error occured')}`.red, err); process.exit(1); } -switch (command) { - case 'get': - swarmapi.downloadRaw(argv.hash, function (err, ret) { - if (err) { - abort('Failed to download: ' + err); - } else { - console.log(ret); - } - }); - break; - case 'put': - swarmapi.uploadRaw(fs.readFileSync(argv.filename), function (err, ret) { - if (err) { - console.log('Failed to upload: ' + err); - } else { - console.log('Swarm hash: ' + ret); - } - }); - break; - case 'calculate': - console.log('Swarm hash: ' + swarmhash(fs.readFileSync(argv.filename)).toString('hex')); - break; - default: - console.log('Invalid arguments. Type \'swarmapi --help\' for valid options'); +function success(msg) { + console.log(`[${gateway}] ${msg}`.green); +} + +try { + switch (command) { + case 'ping': + swarmapi.isAvailable((err, isAvailable) => { + if (err) return abort('Failed to check gateway availability: ', err); + success(`The Swarm gateway at ${gateway} is ${isAvailable ? '' : 'un'}available`); + }); + break; + case 'download': + swarmapi.download(argv.uri, (err, content) => { + if (err) return abort('Failed to download: ', err); + success(content); + }); + break; + case 'downloadraw': + swarmapi.downloadRaw(argv.hash, (err, content) => { + if (err) return abort('Failed to download: ', err); + success(content); + }); + break; + case 'upload': + swarmapi.upload(argv.uri, (err, hash) => { + if (err) return abort('Failed to upload: ', err); + success(`Swarm hash: ${hash}`); + }); + break; + case 'uploadraw': + swarmapi.uploadRaw(argv.content, (err, hash) => { + if (err) { + return abort(`Failed to upload: ${err}`); + } + success(`Swarm hash: ${hash}`); + }); + break; + case 'uploadfile': + swarmapi.uploadRaw(argv.filename, (err, hash) => { + if (err) return abort('Failed to upload file: ', err); + success(`Swarm hash: ${hash}`); + }); + break; + case 'uploaddir': + swarmapi.uploadDirectory(argv.dirpath, null, (err, hash) => { + if (err) return abort('Failed to upload directory: ', err); + success(`Swarm manifest hash: ${hash}. Resources can be retreived using bzz:///, for example (assuming /index.html was uploaded):\nswarmapi download bzz:/${hash}/index.html -g ${gateway}`); + }); + break; + case 'hash': + swarmapi.hash(argv.content, (err, hash) => { + if (err) return abort('Failed to get hash of content: ', err); + success(`Swarm hash: ${hash}`); + }); + break; + default: + abort(`Invalid arguments. Type 'swarmapi --help' for valid options`); + } +} +catch (error) { + abort('Error occurred', error); } diff --git a/index.html b/index.html index 9c20d23..5ce24d9 100644 --- a/index.html +++ b/index.html @@ -3,7 +3,7 @@ SwarmAPI — standalone - + diff --git a/package.json b/package.json index 78e9c16..bb5878f 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "name": "swarmapi", + "name": "swarm-api", "version": "0.0.1", "description": "JavaScript library for easily interacting with Swarm distributed storage.", "main": "dist/node/index.js", @@ -8,7 +8,7 @@ "./dist/node/index.js": "./dist/browser/browser.js" }, "bin": { - "swarmapi": "./bin/swarmapi" + "swarmapi": "./dist/bin/swarmapi" }, "browserslist": [ "last 1 version", @@ -17,8 +17,7 @@ ], "files": [ "dist", - "swarmapi.min.js", - "bin/swarmapi", + "swarm-api.min.js", "src" ], "scripts": { @@ -27,7 +26,7 @@ "babel:browser": "cross-env BABEL_ENV=browser babel --out-dir dist/browser src", "babel:node": "cross-env BABEL_ENV=node babel --out-dir dist src", "build": "npm run clean && npm run babel && npm run webpack", - "clean": "rimraf dist swarmapi-*.tgz package", + "clean": "rimraf dist swarm-api-*.tgz package", "http-server": "http-server", "prepare": "npm run build", "test": "echo \"Error: no test specified\" && exit 1", @@ -35,7 +34,7 @@ }, "repository": { "type": "git", - "url": "git+https://github.com/embark-framework/swarmapi.git" + "url": "git+https://github.com/embark-framework/swarm-api.git" }, "author": "", "license": "MIT", @@ -47,11 +46,12 @@ "serverless" ], "bugs": { - "url": "https://github.com/embark-framework/swarmapi/issues" + "url": "https://github.com/embark-framework/swarm-api/issues" }, - "homepage": "https://github.com/embark-framework/swarmapi#readme", + "homepage": "https://github.com/embark-framework/swarm-api#readme", "dependencies": { "@babel/runtime-corejs2": "7.0.0-rc.1", + "colors": "1.3.2", "klaw": "^3.0.0", "request": "^2.87.0", "swarmhash": "^0.1.0", @@ -66,11 +66,11 @@ "@babel/preset-env": "7.0.0-rc.1", "ajv": "6.5.2", "cross-env": "5.2.0", + "eslint": "4.19.1", "http-server": "0.11.1", "rimraf": "2.6.2", "webpack": "4.16.1", - "webpack-cli": "3.0.8", - "eslint": "^4.13.1" + "webpack-cli": "3.0.8" }, "engines": { "node": ">=8.11.3" diff --git a/src/shared.js b/src/shared.js index 39511fa..edcf380 100644 --- a/src/shared.js +++ b/src/shared.js @@ -1,4 +1,5 @@ const request = require('request'); +const swarmhash = require('swarmhash'); class SwarmAPI { @@ -82,5 +83,9 @@ class SwarmAPI { cb(e); } } + + hash(content, cb){ + cb(null, swarmhash(content).toString('hex')); + } } export default SwarmAPI; diff --git a/src/standalone/index.js b/src/standalone/index.js index 7dda107..6bd26b3 100644 --- a/src/standalone/index.js +++ b/src/standalone/index.js @@ -1 +1 @@ -module.exports = require('../../smarmjs.min'); +module.exports = require('../../smarm-api.min'); diff --git a/webpack.config.js b/webpack.config.js index aaac6b4..628bb11 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -7,7 +7,7 @@ const standalone = { // minimize: false // }, output: { - filename: 'swarmapi.min.js', + filename: 'swarm-api.min.js', globalObject: 'typeof self !== \'undefined\' ? self : this', library: 'SwarmAPI', libraryTarget: 'umd',