├── Dockerfile ├── CONTRIBUTING.md ├── Makefile ├── keygen.sh ├── src ├── factory │ ├── ipfs-api.js │ └── ipfs-local.js ├── cli.js └── lib │ ├── db-manager.js │ └── orbitdb-api.js ├── package.json ├── azure-pipelines.yml ├── LICENSE ├── .gitignore ├── .github └── workflows │ └── docker-publish.yml ├── CODE_OF_CONDUCT.md └── README.md /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:10 2 | 3 | RUN mkdir api 4 | 5 | WORKDIR /api 6 | 7 | COPY . . 8 | 9 | RUN npm ci --no-color --only=prod 10 | 11 | CMD ["node", "src/cli.js"] -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contribute 2 | 3 | Please contribute! Here are some things that would be great: 4 | 5 | - [Open an issue!](https://github.com/orbitdb/orbit-db-http-api/issues/new) 6 | - Open a pull request! 7 | - Say hi! :wave: 8 | 9 | Please note that we have a [Code of Conduct](CODE_OF_CONDUCT.md), and that all activity in the [@OrbitDB](https://github.com/orbitdb) organization falls under it. Read it before you contribute, as being part of this community means that you agree to abide by it. Thanks. 10 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | root-cert: 2 | mkdir -p certs 3 | openssl genrsa -des3 -out certs/orbit-db-http-api.key 2048 4 | openssl req -x509 \ 5 | -new -nodes \ 6 | -key certs/orbit-db-http-api.key \ 7 | -sha256 \ 8 | -days 1024 \ 9 | -out certs/orbit-db-http-api.pem 10 | mkdir -p /usr/local/share/ca-certificates/extra 11 | cp certs/orbit-db-http-api.pem /usr/local/share/ca-certificates/extra/orbit-db-http-api.crt 12 | update-ca-certificates 13 | 14 | uninstall-root-cert: 15 | rm -rf /usr/local/share/ca-certificates/extra/orbit-db-http-api.crt 16 | rmdir --ignore-fail-on-non-empty /usr/local/share/ca-certificates/extra 17 | update-ca-certificates 18 | -------------------------------------------------------------------------------- /keygen.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | openssl req \ 4 | -new -sha256 -nodes \ 5 | -out ./certs/localhost.csr \ 6 | -newkey rsa:2048 -keyout ./certs/localhost.key \ 7 | -subj "/C=AU/ST=WA/L=City/O=Organization/OU=OrganizationUnit/CN=localhost/emailAddress=demo@example.com" 8 | 9 | openssl x509 \ 10 | -req \ 11 | -in ./certs/localhost.csr \ 12 | -CA ./certs/orbit-db-http-api.pem -CAkey ./certs/orbit-db-http-api.key -CAcreateserial \ 13 | -out ./certs/localhost.crt \ 14 | -days 500 \ 15 | -sha256 \ 16 | -extfile <(echo " \ 17 | [ v3_ca ]\n \ 18 | authorityKeyIdentifier=keyid,issuer\n \ 19 | basicConstraints=CA:FALSE\n \ 20 | keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment\n \ 21 | subjectAltName=DNS:localhost \ 22 | ") 23 | -------------------------------------------------------------------------------- /src/factory/ipfs-api.js: -------------------------------------------------------------------------------- 1 | const IpfsApi = require('ipfs-http-client'); 2 | const OrbitDB = require('orbit-db'); 3 | const DBManager = require('../lib/db-manager.js') 4 | const OrbitApi = require('../lib/orbitdb-api.js') 5 | 6 | 7 | async function api_factory(ipfs_host, ipfs_port, orbitdb_dir, orbitdb_opts, server_opts) { 8 | let ipfs 9 | let orbitdb 10 | let dbm 11 | let orbitdb_api 12 | 13 | if (orbitdb_dir) orbitdb_opts = Object.assign({'directory': orbitdb_dir}, orbitdb_opts) 14 | ipfs = new IpfsApi({ 15 | host: ipfs_host, 16 | port: ipfs_port 17 | }) 18 | orbitdb = await OrbitDB.createInstance(ipfs, orbitdb_opts) 19 | dbm = new DBManager(orbitdb) 20 | orbitdb_api = new OrbitApi(dbm, server_opts) 21 | 22 | return orbitdb_api 23 | } 24 | 25 | module.exports = api_factory 26 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "orbit-db-http-api", 3 | "version": "0.3.0-rc.5", 4 | "description": "An HTTP API Server for the OrbitDB distributed peer-to-peer database", 5 | "main": "src/cli.js", 6 | "keywords": [ 7 | "orbitdb", 8 | "orbit-db", 9 | "http", 10 | "api", 11 | "server", 12 | "peer-to-peer", 13 | "database", 14 | "db" 15 | ], 16 | "scripts": { 17 | "test": "echo \"Error: no test specified\" && exit 1" 18 | }, 19 | "author": "Phillip Mackintosh, Hayden Young", 20 | "license": "MIT", 21 | "repository": { 22 | "type": "git", 23 | "url": "https://github.com/orbitdb/orbit-db-http-api.git" 24 | }, 25 | "engines": { 26 | "node": ">=12.0.0" 27 | }, 28 | "dependencies": { 29 | "@hapi/boom": "^9.1.0", 30 | "docopt": "~0.6.2", 31 | "hapi": "^18.1.0", 32 | "http2": "^3.2.0", 33 | "ipfs": "^0.50.2", 34 | "ipfs-http-client": "^47.0.0", 35 | "js-logger": "^1.6.0", 36 | "orbit-db": "^0.26.0", 37 | "susie": "^3.0.0" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /azure-pipelines.yml: -------------------------------------------------------------------------------- 1 | # Docker 2 | # Build a Docker image 3 | # https://docs.microsoft.com/azure/devops/pipelines/languages/docker 4 | 5 | trigger: 6 | - master 7 | 8 | resources: 9 | - repo: self 10 | 11 | variables: 12 | tag: '$(Build.BuildId)' 13 | 14 | stages: 15 | - stage: Build 16 | displayName: Build image 17 | jobs: 18 | - job: Build 19 | displayName: Build 20 | pool: 21 | vmImage: 'ubuntu-latest' 22 | steps: 23 | - task: Docker@2 24 | displayName: Build an image 25 | inputs: 26 | containerRegistry: 'OrbitDB Docker Hub' 27 | repository: 'orbitdb/orbit-db-http-api' 28 | command: 'build' 29 | Dockerfile: '**/Dockerfile' 30 | tags: | 31 | latest 32 | $(tag) 33 | - task: Docker@2 34 | displayName: Push image 35 | inputs: 36 | containerRegistry: 'OrbitDB Docker Hub' 37 | repository: 'orbitdb/orbit-db-http-api' 38 | command: 'push' 39 | tags: | 40 | latest 41 | $(tag) 42 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 phillmac 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/factory/ipfs-local.js: -------------------------------------------------------------------------------- 1 | const Ipfs = require('ipfs'); 2 | const OrbitDB = require('orbit-db'); 3 | const DBManager = require('../lib/db-manager.js') 4 | const OrbitApi = require('../lib/orbitdb-api.js') 5 | 6 | 7 | async function api_factory(ipfs_opts, orbitdb_dir, orbitdb_opts, server_opts) { 8 | let ipfs, orbitdb, dbm, orbitdb_api, ipfs_defaults 9 | 10 | ipfs_defaults = { 11 | EXPERIMENTAL: { 12 | pubsub: true 13 | }, 14 | start: true, 15 | config: { 16 | Addresses: { 17 | Swarm: [ 18 | // '/dns4/wrtc-star1.par.dwebops.pub/tcp/443/wss/p2p-webrtc-star', 19 | ] 20 | } 21 | } 22 | } 23 | if (orbitdb_dir) orbitdb_opts = Object.assign({'directory': orbitdb_dir}, orbitdb_opts) 24 | ipfs_opts = Object.assign(ipfs_defaults, ipfs_opts) 25 | ipfs = await Ipfs.create(ipfs_opts) 26 | orbitdb = await OrbitDB.createInstance(ipfs, orbitdb_opts) 27 | dbm = new DBManager(orbitdb) 28 | orbitdb_api = new OrbitApi(dbm, server_opts) 29 | 30 | return orbitdb_api 31 | } 32 | 33 | module.exports = api_factory 34 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # Bower dependency directory (https://bower.io/) 27 | bower_components 28 | 29 | # node-waf configuration 30 | .lock-wscript 31 | 32 | # Compiled binary addons (https://nodejs.org/api/addons.html) 33 | build/Release 34 | 35 | # Dependency directories 36 | node_modules/ 37 | jspm_packages/ 38 | 39 | # TypeScript v1 declaration files 40 | typings/ 41 | 42 | # Optional npm cache directory 43 | .npm 44 | 45 | # Optional eslint cache 46 | .eslintcache 47 | 48 | # Optional REPL history 49 | .node_repl_history 50 | 51 | # Output of 'npm pack' 52 | *.tgz 53 | 54 | # Yarn Integrity file 55 | .yarn-integrity 56 | 57 | # dotenv environment variables file 58 | .env 59 | 60 | # next.js build output 61 | .next 62 | 63 | # ignore certificates 64 | *.crt 65 | *.key 66 | certs/ 67 | 68 | # ignore local db repository 69 | orbitdb/ 70 | -------------------------------------------------------------------------------- /.github/workflows/docker-publish.yml: -------------------------------------------------------------------------------- 1 | name: Docker 2 | 3 | on: 4 | push: 5 | # Publish `develop` as Docker `latest` image. 6 | branches: 7 | - develop 8 | 9 | # Publish `v1.2.3` tags as releases. 10 | tags: 11 | - v* 12 | 13 | # Run tests for any PRs. 14 | pull_request: 15 | 16 | env: 17 | IMAGE_NAME: orbit-db-http-api 18 | 19 | jobs: 20 | # Push image to GitHub Packages. 21 | # See also https://docs.docker.com/docker-hub/builds/ 22 | push: 23 | runs-on: ubuntu-latest 24 | if: github.event_name == 'push' 25 | 26 | steps: 27 | - uses: actions/checkout@v2 28 | 29 | - name: Build image 30 | run: docker build . --file Dockerfile --tag $IMAGE_NAME 31 | 32 | - name: Log into registry 33 | run: echo "${{ secrets.GITHUB_TOKEN }}" | docker login docker.pkg.github.com -u ${{ github.actor }} --password-stdin 34 | 35 | - name: Push image 36 | run: | 37 | IMAGE_ID=docker.pkg.github.com/${{ github.repository }}/$IMAGE_NAME 38 | 39 | # Change all uppercase to lowercase 40 | IMAGE_ID=$(echo $IMAGE_ID | tr '[A-Z]' '[a-z]') 41 | 42 | # Strip git ref prefix from version 43 | VERSION=$(echo "${{ github.ref }}" | sed -e 's,.*/\(.*\),\1,') 44 | 45 | # Strip "v" prefix from tag name 46 | [[ "${{ github.ref }}" == "refs/tags/"* ]] && VERSION=$(echo $VERSION | sed -e 's/^v//') 47 | 48 | # Use Docker `latest` tag convention 49 | [ "$VERSION" == "develop" ] && VERSION=latest 50 | 51 | echo IMAGE_ID=$IMAGE_ID 52 | echo VERSION=$VERSION 53 | 54 | docker tag $IMAGE_NAME $IMAGE_ID:$VERSION 55 | docker push $IMAGE_ID:$VERSION 56 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, gender identity and expression, level of experience, 9 | nationality, personal appearance, race, religion, or sexual identity and 10 | orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at [community@orbitdb.org](mailto:community@orbitdb.org), which goes to all members of the @OrbitDB community team, or to [richardlitt@orbitdb.org](mailto:richardlitt@orbitdb.org), which goes only to [@RichardLitt](https://github.com/RichardLitt) or to [haadcode@orbitdb.org](mailto:haadcode@orbitdb.org), which goes only to [@haadcode](https://github.com/haadcode). All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 72 | 73 | [homepage]: https://www.contributor-covenant.org 74 | -------------------------------------------------------------------------------- /src/cli.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | const fs = require('fs'); 4 | const {docopt} = require('docopt'); 5 | const version = require('../package.json').version; 6 | 7 | class Cli { 8 | constructor() { 9 | let doc, _args 10 | doc = 11 | ` 12 | OrbitDb HTTP API v${version} 13 | 14 | Usage: 15 | cli.js local [--ipfs-conf=IPFS_CONF] [options] 16 | cli.js api [--ipfs-host=HOST] [--ipfs-port=IPFS_PORT] [options] 17 | cli.js -h | --help | --version 18 | 19 | Options: 20 | --api-port=API_PORT Listen for api calls on API_PORT 21 | --orbitdb-dir=ORBITDB_DIR Store orbit-db files in ORBITDB_DIR 22 | --orbitdb-conf=ORBITDB_CONF Load orbit-db conf options from ORBITDB_CONF 23 | --no-https Disable 24 | --http1 Use HTTP/1 instead of HTTP/2 25 | --https-cert=HTTPS_CERT Path to https cert 26 | --https-key=HTTPS_KEY Path to https cert key 27 | 28 | 29 | `; 30 | _args = docopt(doc, { 31 | 'version': version 32 | }); 33 | this.args = () => { 34 | return _args; 35 | } 36 | } 37 | } 38 | 39 | async function init () { 40 | 41 | let orbitdb_dir, orbitdb_conf, orbitdb_opts, orbitdb_api; 42 | 43 | try { 44 | cli = new Cli() 45 | args = cli.args() 46 | orbitdb_dir = args['--orbitdb-dir'] || process.env.ORBITDB_DIR 47 | orbitdb_conf = args['--orbitdb-conf'] || process.env.ORBITDB_CONF 48 | if (orbitdb_conf) { 49 | fs.readFile(orbitdb_conf, 'utf8', function (err, data) { 50 | if (err) throw err; 51 | orbitdb_opts = JSON.parse(data); 52 | }); 53 | } 54 | 55 | api_port = args['--api-port'] || process.env.API_PORT || 3000 56 | let server_opts, http2_opts = { 57 | allowHTTP1: true 58 | } 59 | 60 | let secure = !args['--no-https'] 61 | let http1 = args['--http1'] || false; 62 | 63 | if (secure) { 64 | let cert, cert_key 65 | 66 | cert = args['--https-cert'] || process.env.HTTPS_CERT 67 | cert_key = args['--https-key'] || process.env.HTTPS_KEY 68 | 69 | if (!cert) throw new Error('--https-cert is required'); 70 | if (!cert_key) throw new Error('--https-key is required'); 71 | 72 | http2_opts.key = fs.readFileSync(cert_key) 73 | http2_opts.cert = fs.readFileSync(cert) 74 | } 75 | 76 | server_opts = { 77 | api_port: api_port, 78 | http2_opts: http2_opts, 79 | secure: secure, 80 | http1: http1 81 | } 82 | 83 | switch(true){ 84 | case args['local']: 85 | const api_factory_local = require('./factory/ipfs-local.js'); 86 | let ipfs_conf, ipfs_opts; 87 | ipfs_opts = {} 88 | ipfs_conf = args['--ipfs-conf'] || process.env.IPFS_CONF 89 | if (ipfs_conf) { 90 | fs.readFile(ipfs_conf, 'utf8', function (err, data) { 91 | if (err) throw err; 92 | ipfs_opts = JSON.parse(data); 93 | }); 94 | } 95 | orbitdb_api = await api_factory_local(ipfs_opts, orbitdb_dir, orbitdb_opts, server_opts) 96 | break; 97 | 98 | case args['api']: 99 | const api_factory_remote = require('./factory/ipfs-api.js'); 100 | ipfs_host = args['--ipfs-host'] || process.env.IPFS_HOST; 101 | if (!ipfs_host) throw new Error ('--ipfs-host is required'); 102 | ipfs_port = args['--ipfs-port'] || process.env.IPFS_PORT || 5001; 103 | orbitdb_api = await api_factory_remote(ipfs_host, ipfs_port, orbitdb_dir, orbitdb_opts, server_opts) 104 | break; 105 | 106 | default: 107 | throw new Error("Unrecognised ipfs type. Please specify either 'api' or 'local'"); 108 | } 109 | 110 | await orbitdb_api.server.start() 111 | console.log(`Server running on port ${api_port}`); 112 | 113 | } catch(err) { 114 | console.error(err); 115 | process.exit(1); 116 | } 117 | } 118 | 119 | init() 120 | -------------------------------------------------------------------------------- /src/lib/db-manager.js: -------------------------------------------------------------------------------- 1 | class DBManager { 2 | constructor(orbitdb){ 3 | let _dbs = {}; 4 | 5 | let find_db = (dbn) => { 6 | let result 7 | if (dbn in _dbs) return _dbs[dbn] 8 | for (let db of Object.values(_dbs)) { 9 | if (dbn == db.id) { 10 | result = db 11 | break 12 | } else if (dbn == [db.address.root, db.address.path].join('/')) { 13 | result = db 14 | break 15 | } 16 | }; 17 | if (result) return result 18 | }; 19 | 20 | this.get = async (dbn, params) => { 21 | let db = find_db(dbn); 22 | if (db) { 23 | return db; 24 | } else { 25 | console.log(`Opening db ${dbn}`); 26 | db = await orbitdb.open(dbn, params); 27 | await db.load(); 28 | console.log(`Loaded db ${db.dbname}`); 29 | _dbs[db.dbname] = db; 30 | return db; 31 | } 32 | }; 33 | 34 | this.db_list_remove = async (dbn) => { 35 | let db = find_db(dbn) 36 | if (db) { 37 | await db.close() 38 | delete _dbs[db.dbname]; 39 | console.log(`Unloaded db ${db.dbname}`); 40 | } 41 | } 42 | 43 | this.db_list = () => { 44 | let db_info_list = {}; 45 | for (let dbn in _dbs) { 46 | if (_dbs.hasOwnProperty(dbn)) { 47 | db_info_list[dbn] = this.db_info(dbn); 48 | } 49 | } 50 | return JSON.stringify(db_info_list); 51 | }; 52 | 53 | let _db_write = (db) => { 54 | return ( 55 | db.access.write || 56 | (typeof db.access.get == 'function' && db.access.get('write')) || 57 | db.access._options.write || 58 | 'unavaliable' 59 | ); 60 | } 61 | 62 | this.db_write = (dbn) => { 63 | let db = find_db(dbn); 64 | if (!db) return {}; 65 | return _db_write(db); 66 | } 67 | 68 | this.db_info = (dbn) => { 69 | let db = find_db(dbn); 70 | if (!db) return {}; 71 | let __db_write = _db_write(db) 72 | return { 73 | address: db.address, 74 | dbname: db.dbname, 75 | id: db.id, 76 | options: { 77 | create: db.options.create, 78 | indexBy: db.options.indexBy, 79 | localOnly: db.options.localOnly, 80 | maxHistory: db.options.maxHistory, 81 | overwrite: db.options.overwrite, 82 | path: db.options.path, 83 | replicate: db.options.replicate, 84 | }, 85 | canAppend: __db_write.includes(orbitdb.identity.id), 86 | write: __db_write, 87 | type: db.type, 88 | uid: db.uid, 89 | indexLength: db.index ? (db.index.length || Object.keys(db.index).length) : -1, 90 | accessControlerType: db.access.type || 'custom', 91 | capabilities: Object.keys( //TODO: cleanup this mess once tc39 object.fromEntries aproved 92 | Object.assign ({}, ... // https://tc39.github.io/proposal-object-from-entries 93 | Object.entries({ 94 | add: typeof db.add == 'function', 95 | get: typeof db.get == 'function', 96 | inc: typeof db.inc == 'function', 97 | iterator: typeof db.iterator == 'function', 98 | put: typeof db.put == 'function', 99 | query: typeof db.query == 'function', 100 | remove: typeof (db.del || db.remove) == 'function', 101 | value: typeof db.value == 'function' 102 | }).filter(([k,v]) => v).map(([k,v]) => ({[k]:v})) 103 | ) 104 | ) 105 | }; 106 | }; 107 | 108 | this.identity = () => { 109 | return orbitdb.identity; 110 | }; 111 | } 112 | } 113 | 114 | module.exports = DBManager; 115 | -------------------------------------------------------------------------------- /src/lib/orbitdb-api.js: -------------------------------------------------------------------------------- 1 | const Hapi = require('hapi'); 2 | const Boom = require('@hapi/boom'); 3 | const Http2 = require('http2'); 4 | const Http = require('http'); 5 | const Susie = require('susie'); 6 | 7 | require('events').EventEmitter.defaultMaxListeners = 50 //Set warning higher then normal to handle many clients 8 | 9 | class OrbitdbAPI { 10 | constructor (dbm, server_opts) { 11 | let comparisons, rawiterator, getraw, unpack_contents, listener; 12 | let dbMiddleware, addEventListener; 13 | 14 | listener = (server_opts.http1 ? Http : Http2)[server_opts.secure ? 'createSecureServer' : 'createServer'](server_opts.http2_opts); 15 | this.server = new Hapi.Server({ 16 | listener, 17 | tls: server_opts.secure, 18 | port: server_opts.api_port}); 19 | 20 | comparisons = { 21 | 'ne': (a, b) => a != b, 22 | 'eq': (a, b) => a == b, 23 | 'gt': (a, b) => a > b, 24 | 'lt': (a, b) => a < b, 25 | 'gte': (a, b) => a >= b, 26 | 'lte': (a, b) => a <= b, 27 | 'mod': (a, b, c) => a % b == c, 28 | 'range': (a, b, c) => Math.max(b,c) >= a && a >= Math.min(b,c), 29 | 'all': () => true 30 | }; 31 | 32 | dbMiddleware = fn => 33 | async (request, h) => { 34 | let db 35 | db = await dbm.get(request.params.dbname) 36 | return Promise.resolve((fn(db, request, h))) 37 | .catch((err) => {throw err}); 38 | }; 39 | 40 | rawiterator = (db, request, _h) => 41 | db.iterator(request.payload).collect(); 42 | 43 | getraw = (db, request, _h) => 44 | db.get(request.params.item); 45 | 46 | unpack_contents = (contents) => { 47 | if (contents){ 48 | if (contents.map) { 49 | return contents.map((e) => { 50 | if (e.payload) return e.payload.value 51 | return e 52 | }) 53 | } else if (contents.payload) { 54 | return contents.payload.value 55 | } 56 | } 57 | return contents 58 | }; 59 | 60 | addEventListener = (db, event_name, request, h) => { 61 | let event_map = new Map(Object.entries({ 62 | 'replicated': (address) => 63 | h.event({event:'replicated', data: {address:address}}), 64 | 'replicate': (address) => 65 | h.event({event:'replicate', data: {address:address}}), 66 | 'replicate.progress': (address, hash, entry, progress, have) => 67 | h.event({event:'replicate.progress', data: {address:address, hash:hash, entry:entry, progress:progress, have:have}}), 68 | 'load': (dbname) => 69 | h.event({event:'load', data: {dbname:dbname}}), 70 | 'load.progress': (address, hash, entry, progress, total) => 71 | h.event({event:'load.progress', data: {address:address, hash:hash, entry:entry, progress:progress, total:total}}), 72 | 'ready': (dbname, heads) => 73 | h.event({event:'ready', data: {dbname:dbname, heads:heads}}), 74 | 'write': (dbname, hash, entry) => 75 | h.event({event:'write', data: {dbname:dbname, hash:hash, entry:entry}}), 76 | 'closed': (dbname) => 77 | h.event({event:'closed', data: {dbname:dbname}}) 78 | })); 79 | 80 | let event_callback = event_map.get(event_name) 81 | if(event_callback){ 82 | db.events.on(event_name, event_callback) 83 | let keepalive = setInterval(() => h.event({event:'keep-alive'}), 10000) 84 | request.events.on('disconnect', () => { 85 | db.events.removeListener(event_name, event_callback) 86 | clearInterval(keepalive) 87 | }) 88 | } else { 89 | if(this.debug) throw Boom.badRequest(`Unrecognized event name: $(event_name)`) 90 | throw Boom.badRequest('Unrecognized event name') 91 | } 92 | } 93 | 94 | Promise.resolve(this.server.register(Susie)).catch((err) => {throw err}); 95 | this.server.route([ 96 | { 97 | method: 'GET', 98 | path: '/dbs', 99 | handler: (_request, _h) => dbm.db_list() 100 | }, 101 | { 102 | method: ['POST', 'PUT'], 103 | path: '/db', 104 | handler: async (request, _h) => { 105 | let db, payload; 106 | payload = request.payload; 107 | db = await dbm.get(payload.dbname, payload); 108 | return dbm.db_info(db.dbname); 109 | } 110 | }, 111 | { 112 | method: ['POST', 'PUT'], 113 | path: '/db/{dbname}', 114 | handler: async (request, _h) => { 115 | let db; 116 | db = await dbm.get(request.params.dbname, request.payload); 117 | return dbm.db_info(db.dbname); 118 | } 119 | }, 120 | { 121 | method: 'GET', 122 | path: '/db/{dbname}', 123 | handler: (request, _h) => dbm.db_info(request.params.dbname) 124 | }, 125 | { 126 | method: 'DELETE', 127 | path: '/db/{dbname}', 128 | handler: async (request, _h) => { 129 | await dbm.db_list_remove(request.params.dbname); 130 | return {}; 131 | } 132 | }, 133 | { 134 | method: 'DELETE', 135 | path: '/db/{dbname}/{item}', 136 | handler: dbMiddleware (async (db, request, _h) => { 137 | if (db.del) { 138 | return {hash: await db.del(request.params.item)}; 139 | } else if (db.remove) { 140 | return {hash: await db.remove(request.params.item)}; 141 | } else { 142 | return Boom.methodNotAllowed(`DB type ${db.type} does not support removing data`, 143 | { 144 | dbname: db.dbname, 145 | dbtype: db.type 146 | }); 147 | } 148 | }) 149 | }, 150 | { 151 | method: ['POST', 'PUT'], 152 | path: '/db/{dbname}/put', 153 | handler: dbMiddleware( async (db, request, _h) => { 154 | let params; 155 | params = request.payload; 156 | 157 | if (db.type == 'keyvalue') { 158 | let key, value; 159 | if (!params['key']) { 160 | [key,value] = [Object.keys(params)[0], Object.values(params)[0]]; 161 | } else { 162 | ({key,value} = params); 163 | } 164 | return {hash: await db.put(key, value)}; 165 | } else { 166 | return {hash: await db.put(params)}; 167 | } 168 | }) 169 | }, 170 | { 171 | method: ['POST', 'PUT'], 172 | path: '/db/{dbname}/add', 173 | handler: dbMiddleware( async (db, request, _h) => { 174 | return {hash: await db.add(request.payload)}; 175 | }) 176 | }, 177 | { 178 | method: ['POST', 'PUT'], 179 | path: '/db/{dbname}/inc', 180 | handler: dbMiddleware( async (db, request, _h) => { 181 | let incval 182 | incval = parseInt(request.payload ? request.payload.val || 1 : 1); 183 | return {hash: await db.inc(incval)}; 184 | }) 185 | }, 186 | { 187 | method: ['POST', 'PUT'], 188 | path: '/db/{dbname}/inc/{val}', 189 | handler: dbMiddleware( async (db, request, _h) => { 190 | return {hash: await db.inc(parseInt(request.params.val || 1))}; 191 | }) 192 | }, 193 | { 194 | method: 'POST', 195 | path: '/db/{dbname}/query', 196 | handler: dbMiddleware( async (db, request, _h) => { 197 | let qparams, comparison, query; 198 | qparams = request.payload; 199 | comparison = comparisons[qparams.comp || 'all']; 200 | query = (doc) => comparison(doc[qparams.propname || '_id'], ...qparams.values); 201 | return await db.query(query); 202 | }) 203 | }, 204 | { 205 | method: 'GET', 206 | path: '/db/{dbname}/iterator', 207 | handler: dbMiddleware( async (db, request, h) => { 208 | let raw; 209 | raw = rawiterator(db, request, h); 210 | return raw.map((e) => Object.keys(e.payload.value)[0]); 211 | 212 | }) 213 | }, 214 | { 215 | method: 'GET', 216 | path: '/db/{dbname}/rawiterator', 217 | handler: dbMiddleware( async (db, request, h) => { 218 | return rawiterator(db, request, h); 219 | }) 220 | }, 221 | { 222 | method: 'GET', 223 | path: '/db/{dbname}/raw/{item}', 224 | handler: dbMiddleware( async (db, request, h) => { 225 | return getraw(db, request, h); 226 | }) 227 | }, 228 | { 229 | method: 'GET', 230 | path: '/db/{dbname}/{item}', 231 | handler: dbMiddleware( async (db, request, h) => { 232 | let raw; 233 | raw = getraw(db, request, h); 234 | return unpack_contents(raw); 235 | }) 236 | }, 237 | { 238 | method: 'GET', 239 | path: '/db/{dbname}/all', 240 | handler: dbMiddleware( async (db, _request, _h) => { 241 | if (typeof db._query == 'function') { 242 | let contents 243 | contents = db._query({limit:-1}) 244 | return contents.map((e) => Object.keys(e.payload.value)[0]) 245 | } else { 246 | return unpack_contents(db.all) 247 | } 248 | }) 249 | }, 250 | { 251 | method: 'GET', 252 | path: '/db/{dbname}/index', 253 | handler: dbMiddleware( async (db, _request, _h) => db.index) 254 | }, 255 | { 256 | method: 'GET', 257 | path: '/db/{dbname}/value', 258 | handler: dbMiddleware( async (db, _request, _h) => db.value) 259 | }, 260 | { 261 | method: 'GET', 262 | path: '/identity', 263 | handler: (_request, _h) => dbm.identity() 264 | }, 265 | { 266 | method: ['POST', 'PUT'], 267 | path: '/db/{dbname}/access/write', 268 | handler: dbMiddleware( async (db, request, _h) => { 269 | if (await db.access.grant('write', request.payload.id) === false) 270 | return new Boom.notImplemented('Access controller does not support setting write access'); 271 | return {} 272 | }) 273 | }, 274 | { 275 | method: 'GET', 276 | path: '/db/{dbname}/events/{eventname}', 277 | handler: dbMiddleware( async (db, request, h) => { 278 | let events = request.params.eventname.split(',') 279 | events.forEach((event_name) => addEventListener(db,event_name, request, h)); 280 | return h.event({event:'registered', data: {eventnames:events}}) 281 | }) 282 | }, 283 | 284 | ]); 285 | } 286 | } 287 | 288 | module.exports = OrbitdbAPI 289 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # OrbitDB HTTP API Server 2 | 3 | [![Gitter](https://img.shields.io/gitter/room/nwjs/nw.js.svg)](https://gitter.im/orbitdb/orbit-db-http-api) 4 | [![npm version](https://badge.fury.io/js/orbit-db-http-api.svg)](https://www.npmjs.com/package/orbit-db-http-api) 5 | [![node](https://img.shields.io/node/v/orbit-db-http-api.svg)](https://www.npmjs.com/package/orbit-db-http-api) 6 | 7 | > An HTTP API Server for the OrbitDB distributed peer-to-peer database. 8 | 9 | **Table of Contents** 10 | 11 | - [Install](#install) 12 | - [Setup](#setup) 13 | - [API](#api) 14 | - [GET /dbs](#get-dbs) 15 | - [GET /db/:dbname](#get-dbdbname) 16 | - [GET /db/:dbname/value](#get-dbdbnamevalue) 17 | - [GET /db/:dbname/:item](#get-dbdbnameitem) 18 | - [GET /db/:dbname/iterator](#get-dbdbnameiterator) 19 | - [GET /db/:dbname/index](#get-dbdbnameindex) 20 | - [GET /identity](#get-identity) 21 | - [POST /db/:dbname](#post-dbdbname) 22 | - [POST /db/:dbname/query](#post-dbdbnamequery) 23 | - [Modulus Query](#modulus-query) 24 | - [Range Query](#range-query) 25 | - [POST|PUT /db/:dbname/add](#postput-dbdbnameadd) 26 | - [POST|PUT /db/:dbname/put](#postput-dbdbnameput) 27 | - [POST|PUT /db/:dbname/inc](#postput-dbdbnameinc) 28 | - [POST|PUT /db/:dbname/inc/:val](#postput-dbdbnameincval) 29 | - [POST|PUT /db/:dbname/access/write](#postput-dbdbnameaccesswrite) 30 | - [DELETE /db/:dbname](#delete-dbdbname) 31 | - [DELETE /db/:dbname/:item](#delete-dbdbnameitem) 32 | - [Contribute](#contribute) 33 | - [License](#license) 34 | 35 | ## Install 36 | 37 | To install the OrbitDB HTTP Client: 38 | 39 | ```shell 40 | git clone https://github.com/orbitdb/orbit-db-http-api.git 41 | cd orbit-db-http-api 42 | npm install 43 | ``` 44 | 45 | ## Setup 46 | 47 | The OrbitDB HTTP Client can be run in two modes; local or api. 48 | 49 | In local mode, OrbitDB HTTP Client will launch its own IPFS node to replicate 50 | the OrbitDB peer: 51 | 52 | ```shell 53 | node src/cli.js local --orbitdb-dir /path/to/orbitdb --https-cert ./my.crt --https-key my.key 54 | ``` 55 | 56 | where --orbitdb-dir is the path to your OrbitDB peer. 57 | 58 | In api mode, OrbitDB HTTP Client will connect to an existing IPFS node to 59 | replicate the OrbitDB peer: 60 | 61 | ```shell 62 | node src/cli.js api --ipfs-host localhost --orbitdb-dir /path/to/orbitdb --https-cert ./my.crt --https-key my.key 63 | ``` 64 | 65 | where --ipfs-host is an external IPFS node and --orbitdb-dir is the path to 66 | your OrbitDB peer. 67 | 68 | In both modes, you will also need to specify an SSL certifcate and private key: 69 | 70 | --https-cert will be the path to your certificate. 71 | --https-key will be the path to your associated private key. 72 | 73 | You can generate an SSL certificate using an SSL certificate authority such as 74 | Let's Encrypt. Alternatively, you can create your own self-signed certificate 75 | although this is highly discouraged for production environments. 76 | 77 | When using a self-signed SSL certificate you will either need to add your 78 | certificate to your CA list or pass the ```-k``` option to curl, telling curl to 79 | ignore the the insecure connection. 80 | 81 | ## API 82 | 83 | ### GET /dbs 84 | 85 | Lists all databases on the current peer. 86 | 87 | ```shell 88 | curl https://localhost:3000/dbs 89 | ``` 90 | 91 | ```json 92 | {"docstore":{"address":{"root":"zdpuAmnfJZ6UTssG5Ns3o8ALXZJXVx5eTLTxf7gfFzHxurbJq","path":"docstore"},"dbname":"docstore","id":"/orbitdb/zdpuAmnfJZ6UTssG5Ns3o8ALXZJXVx5eTLTxf7gfFzHxurbJq/docstore","options":{"create":"true","indexBy":"_id","localOnly":false,"maxHistory":-1,"overwrite":true,"replicate":true},"type":"docstore"},"feed":{"address":{"root":"zdpuAo6DwafMiyuzhfEojXJThFPdv4Eu9hLfaWrKD6GSVzyjj","path":"feed"},"dbname":"feed","id":"/orbitdb/zdpuAo6DwafMiyuzhfEojXJThFPdv4Eu9hLfaWrKD6GSVzyjj/feed","options":{"create":"true","localOnly":false,"maxHistory":-1,"overwrite":true,"replicate":true},"type":"feed"}} 93 | ``` 94 | 95 | ### GET /db/:dbname 96 | 97 | Gets the details of a database with name :dbname. 98 | 99 | Returns information about the database as a JSON object. 100 | 101 | ```shell 102 | curl https://localhost:3000/db/docstore 103 | ``` 104 | 105 | ```json 106 | {"address":{"root":"zdpuAmnfJZ6UTssG5Ns3o8ALXZJXVx5eTLTxf7gfFzHxurbJq","path":"docstore"},"dbname":"docstore","id":"/orbitdb/zdpuAmnfJZ6UTssG5Ns3o8ALXZJXVx5eTLTxf7gfFzHxurbJq/docstore","options":{"create":"true","indexBy":"_id","localOnly":false,"maxHistory":-1,"overwrite":true,"replicate":true},"type":"docstore"} 107 | ``` 108 | 109 | ### GET /db/:dbname/value 110 | 111 | Gets the current value from counter database :dbname. 112 | 113 | Returns the current counter value. 114 | 115 | Can only be used on counter. 116 | 117 | ```shell 118 | curl -X GET https://localhost:3000/db/counter/value 119 | ``` 120 | 121 | ```json 122 | 1 123 | ``` 124 | 125 | ### GET /db/:dbname/:item 126 | 127 | Gets a record identified by :item from the database :dbname. 128 | 129 | Returns a list of found items as a JSON array. 130 | 131 | For the data type docstore, :item must be a value identified by the index field (set using indexBy). 132 | 133 | ```shell 134 | curl -X GET https://localhost:3000/db/docstore/1 135 | ``` 136 | 137 | ```json 138 | [{"_id":1, "value": "test"}] 139 | ``` 140 | 141 | ### GET /db/:dbname/iterator 142 | 143 | Gets items from an eventlog or feed database :dbname. 144 | 145 | Returns a list of matching objects as a JSON array. 146 | 147 | Can only be used on eventlog|feed. 148 | 149 | ```shell 150 | curl -X GET https://localhost:3000/db/feed/iterator 151 | ``` 152 | 153 | ```json 154 | [{"IPFS":"https://ipfs.io"}] 155 | ``` 156 | 157 | Additional options can be passed to OrbitDB to return different entries. 158 | 159 | ```shell 160 | curl -X GET https://localhost:3000/db/feed/iterator -d 'limit=-1' 161 | ``` 162 | 163 | ```json 164 | [{"OrbitDB":"https://github.com/orbitdb/orbit-db"},{"IPFS":"https://ipfs.io"}] 165 | ``` 166 | 167 | See [OrbitDB's Iterator API](https://github.com/orbitdb/orbit-db/blob/master/API.md#iteratoroptions-1) 168 | for more information. 169 | 170 | ### GET /db/:dbname/index 171 | 172 | Gets information about the data stored in :dbname. 173 | 174 | Returns information about the data stored as a JSON object. 175 | 176 | For the data store keyvalue all records are returned: 177 | 178 | ```shell 179 | curl -X GET https://localhost:3000/keyvalue/index 180 | ``` 181 | 182 | ```json 183 | {"Key":{"name1":"Value1"},"Key2":{"name":"Value2"}} 184 | ``` 185 | 186 | Docstore and feed return all records as well as iterators, signature hashes and 187 | other information about the stored data: 188 | 189 | ```json 190 | {"1":{"cid":"zdpuB1sqnXKwgAtJT7vqtrRUsyr4XUZyhume9uJgrrwZmyegu","id":"/orbitdb/zdpuAzpw8yuuMEuffMFcgXafsAye9GqBPwTjmiJijHz3akFhx/docstore","payload":{"op":"PUT","key":1,"value":{"_id":1,"name":"1"}},"next":[],"v":1,"clock":...}} 191 | ``` 192 | 193 | The eventlog returns the hash of the last stored item: 194 | 195 | ```json 196 | {"id":"/orbitdb/zdpuB1r3rfya65UUjQu6GsBXEmp5gmjvMwRGwkxd4ySwYnBSK/eventlog","heads":["zdpuAu7eTsdWoQ76CdWCbjcsGV3s6duYyUujaHQiGCAZPLWMb"]} 197 | ``` 198 | 199 | The counter data store returns information about the current counter value: 200 | 201 | ```json 202 | {"id":"04e6de9dd0e8d0069bcc6d8f3ef11cefe63bba6129c32f2cd422a0394814bc6723b26eb62731ee466020b0394d01dd08e4a5123eaad45e4d0840fd796652a22e42","counters":{"04e6de9dd0e8d0069bcc6d8f3ef11cefe63bba6129c32f2cd422a0394814bc6723b26eb62731ee466020b0394d01dd08e4a5123eaad45e4d0840fd796652a22e42":15}} 203 | ``` 204 | 205 | ### GET /identity 206 | 207 | Gets the identity information. 208 | 209 | Returns identity as a JSON object. 210 | 211 | ```shell 212 | curl -X GET https://localhost:3000/identity 213 | ``` 214 | 215 | ```json 216 | {"id":"03fc293ea95bdb5dea71d5d21cbbae2a57f2d2002c9966f0d5c7b0bda232d5934d","publicKey":"048161d9685991dc87f3e049aa04b1da461fdc5f8a280ed6234fa41c0f9bc98a1ce91f07494584a45b97160ac818e100a6b27777e0b1b09e6ba4795dcc797a6d8b","signatures":{"id":"3045022100e40ab2dcc83dde17c939d5515ce322e7f81bf47536ab342582db8c35f28d2a720220228e418cc3d2f3e0004d5f4292c0d2cf7975c93073e0cc831f0cb849e4ac920a","publicKey":"3045022100ad18ba66006e19e2952eabc9ffb532dd69d60593f90448a05d6f4903c2931e3502204009975030b839522c668cd693d357bf1f3d0423d604a6bc10645425a0a3dd1b"},"type":"orbitdb"} 217 | ``` 218 | 219 | ### POST /db/:dbname 220 | 221 | Creates a new database and returns information about the newly created database 222 | or opens an existing database with the same name. 223 | 224 | Returns information about the database as a JSON object. 225 | 226 | The OrbitDB options ```create=true``` and ```type=eventlog|feed|docstore|keyvalue|counter``` 227 | must be sent with the POST otherwise an error is thrown. 228 | 229 | ```shell 230 | curl https://localhost:3000/db/docstore -d "create=true" -d "type=docstore" 231 | ``` 232 | 233 | ```json 234 | {"address":{"root":"zdpuAmnfJZ6UTssG5Ns3o8ALXZJXVx5eTLTxf7gfFzHxurbJq","path":"docstore"},"dbname":"docstore","id":"/orbitdb/zdpuAmnfJZ6UTssG5Ns3o8ALXZJXVx5eTLTxf7gfFzHxurbJq/docstore","options":{"create":"true","indexBy":"_id","localOnly":false,"maxHistory":-1,"overwrite":true,"replicate":true},"type":"docstore"} 235 | ``` 236 | 237 | Additional OrbitDB-specific flags can also be passed. For example, if the index 238 | field must be changed then the indexBy flag can be specified as an additional 239 | POST param (this would apply to type docstore only): 240 | 241 | ```shell 242 | curl https://localhost:3000/db/docstore -d "create=true" -d "type=docstore" -d "indexBy=name" 243 | ``` 244 | 245 | Parameters can also be passed as JSON. This is useful if additional parameters 246 | such as accessController need to be specified: 247 | 248 | ```shell 249 | curl -H "Content-Type: application/json" --data '{"create":"true","type":"feed","accessController":{"type": "orbitdb","write": ["1234"]}}' 250 | ``` 251 | 252 | To open an existing database, specify the address of the database. If the 253 | database does not exist locally it will be fetched from the swarm. 254 | 255 | The address MUST be URL escaped. 256 | 257 | ```shell 258 | curl -X POST https://localhost:3000/db/zdpuAmnfJZ6UTssG5Ns3o8ALXZJXVx5eTLTxf7gfFzHxurbJq%2Fdocstore 259 | ``` 260 | 261 | By default, OrbitDB will open the database if one already exists with the same 262 | name. To always overwrite the existing database with a new one, pass the 263 | overwrite flag: 264 | 265 | ```shell 266 | curl https://localhost:3000/db/docstore -d "create=true" -d "type=docstore" -d "overwrite=true" 267 | ``` 268 | 269 | ### POST /db/:dbname/query 270 | 271 | Queries the database :dbname. 272 | 273 | Returns a list of found items as a JSON array. 274 | 275 | ```shell 276 | curl https://localhost:3000/db/docstore/query -X GET -H "Content-Type: application/json" --data '{"values":[]}' 277 | ``` 278 | 279 | ```json 280 | [{"project":"OrbitDB","site":"https://github.com/orbitdb/orbit-db","likes":200},{"project":"IPFS","site":"https://ipfs.io","likes":400}] 281 | ``` 282 | 283 | To query a subset of data, a condition can be specified. For example, to 284 | retrieve only those entries which have a total number of likes above 300: 285 | 286 | ```shell 287 | curl https://localhost:3000/db/docstore/query -H "Content-Type: application/json" --data '{"propname":"likes","comp":"gt","values":[300]}' 288 | ``` 289 | 290 | ```json 291 | [{"project":"IPFS","site":"https://ipfs.io","likes":400}] 292 | ``` 293 | 294 | Available operator short-codes are: 295 | 296 | ```eq``` propname equals value. Equivalent to "==" 297 | 298 | ```ne``` propname is not equals to value. Equivalent to "!=" 299 | 300 | ```gt``` propname is greater than value. Equivalent to ">" 301 | 302 | ```lt``` propname is less than value. Equivalent to "<" 303 | 304 | ```gte``` propname is greater than or equal to value. Equivalent to ">=" 305 | 306 | ```lte``` propname is less than or equal to value. Equivalent to "<=" 307 | 308 | ```mod``` Perform a modulus calculation on propname using value. Equivalent to "%" 309 | 310 | ```range``` Perform a range query, comparing propname to min and max. 311 | 312 | ```all``` Fetch all records for field propname. Equivalent to "*" 313 | 314 | #### Modulus Query 315 | 316 | When using a modulus query, you must supply the divisor and the remainder. For example, to obtain all likes which are multiples of 100, you would specify a divisor 100 and a remainder 0: 317 | 318 | ```shell 319 | curl -X POST https://localhost:3000/db/docstore/query -H "Content-Type:application/json" --data '{"propname":"likes", "comp":"mod", "values":[100,0]}' 320 | ``` 321 | 322 | ```json 323 | [{"site":"https://ipfs.io","likes":400,"project":"IPFS"},{"site":"https://github.com/orbitdb/orbit-db","likes":200,"project":"OrbitDB"}] 324 | ``` 325 | 326 | #### Range Query 327 | 328 | When specifying a range query, you must supply the min and max 329 | values. For example, to obtain all likes greater than 250 but less than 1000 the min and max must be supplied: 330 | 331 | ```shell 332 | curl -X GET https://localhost:3000/db/docstore/query -H "Content-Type:application/json" --data '{"propname":"likes", "comp":"range", "values":[250,1000]}' 333 | ``` 334 | 335 | ```json 336 | [{"site":"https://ipfs.io","likes":400,"project":"IPFS"},{"site":"https://github.com/orbitdb/orbit-db","likes":200,"project":"OrbitDB"}] 337 | ``` 338 | 339 | ### POST|PUT /db/:dbname/add 340 | 341 | Adds a new entry to the eventlog or feed database :dbname. 342 | 343 | Returns the multihash of the new record entry. 344 | 345 | Can only be used on eventlog|feed 346 | 347 | ```shell 348 | curl -X POST https://localhost:3000/db/feed/add -d 'feed-item-1' 349 | ``` 350 | 351 | ```json 352 | zdpuArB1ZQUQGGpZgJrhy6xyxwxMCE898kDrQW2x6KbnRNbAn 353 | ``` 354 | 355 | ### POST|PUT /db/:dbname/put 356 | 357 | Puts a record to the database :dbname. 358 | 359 | Returns a multihash of the record entry. 360 | 361 | ```shell 362 | curl -X POST https://localhost:3000/db/docstore/put -H "Content-Type: application/json" -d '{"_id":1, "value": "test"}' 363 | ``` 364 | 365 | ```json 366 | zdpuAkkFaimxyRE2bsiLRSiybkku3oDi4vFHqPZh29BABZtZU 367 | ``` 368 | 369 | For the keyvalue store, a JSON object containing the variables `key` and 370 | `value` must be passed in the POST data: 371 | 372 | ```shell 373 | curl -X POST https://localhost:3000/db/keyvalue/put -H "Content-Type: application/json" -d '{"key":"Key","value":{ "name": "Value" }}' 374 | ``` 375 | 376 | ### POST|PUT /db/:dbname/inc 377 | 378 | Increments the counter database :dbname by 1. 379 | 380 | Returns a multihash of the new counter value. 381 | 382 | ```shell 383 | curl -X POST https://localhost:3000/db/counter/inc 384 | ``` 385 | 386 | ```json 387 | zdpuAmHw9Tcc4pyVjcVX3rJNJ7SGffmu4EwjodzmaPBVGGzbd 388 | ``` 389 | 390 | ### POST|PUT /db/:dbname/inc/:val 391 | 392 | Increments the counter database :dbname by :val. 393 | 394 | Returns a multihash of the new counter value. 395 | 396 | ```shell 397 | curl -X POST https://localhost:3000/db/counter/inc/100 398 | ``` 399 | 400 | ```json 401 | zdpuAmHw9Tcc4pyVjcVX3rJNJ7SGffmu4EwjodzmaPBVGGzbd 402 | ``` 403 | 404 | ### POST|PUT /db/:dbname/access/write 405 | 406 | Adds the id to the list of peers who have write access to the :dbname data store. 407 | 408 | ```shell 409 | curl -X POST https://localhost:3000/db/docstore/access/write -d 'id=045757bffcc7a4...' 410 | ``` 411 | 412 | ```json 413 | zdpuAmHw9Tcc4pyVjcVX3rJNJ7SGffmu4EwjodzmaPBVGGzbd 414 | ``` 415 | 416 | ### DELETE /db/:dbname 417 | 418 | Deletes the local database :dbname. This does not delete any data from peers. 419 | 420 | ```shell 421 | curl -X DELETE https://localhost:3000/db/docstore 422 | ``` 423 | 424 | ### DELETE /db/:dbname/:item 425 | 426 | Deletes the item specified by :item from the database :dbname. 427 | 428 | Returns the multihash of the item entry deleted or an error if no item is found. 429 | 430 | ```shell 431 | curl -X DELETE https://localhost:3000/db/docstore/1 432 | ``` 433 | 434 | ```json 435 | zdpuB39Yv1LV6CMYuNUgRi125utDpUoiP7PDsumjn1T4ASkzN 436 | ``` 437 | 438 | ## Contribute 439 | 440 | We would be happy to accept PRs! If you want to work on something, it'd be good to talk beforehand to make sure nobody else is working on it. You can reach us [on Gitter](https://gitter.im/orbitdb/Lobby), or in the [issues section](https://github.com/orbitdb/orbit-db-http-api/issues). 441 | 442 | We also have **regular community calls**, which we announce in the issues in [the @orbitdb welcome repository](https://github.com/orbitdb/welcome/issues). Join us! 443 | 444 | If you want to code but don't know where to start, check out the issues labelled ["help wanted"](https://github.com/orbitdb/orbit-db-http-api/issues?q=is%3Aopen+is%3Aissue+label%3A%22help+wanted%22+sort%3Areactions-%2B1-desc). 445 | 446 | For specific guidelines for contributing to this repository, check out the [Contributing guide](CONTRIBUTING.md). For more on contributing to OrbitDB in general, take a look at the [@OrbitDB welcome repository](https://github.com/orbitdb/welcome). Please note that all interactions in [@OrbitDB](https://github.com/orbitdb) fall under our [Code of Conduct](CODE_OF_CONDUCT.md). 447 | 448 | ## License 449 | 450 | [MIT](LICENSE) © 2019 phillmac 451 | --------------------------------------------------------------------------------