Update dependencies

Add access-controllers, identity

Update build command

Add static createInstance
This commit is contained in:
shamb0t 2019-02-20 13:16:29 +00:00
parent 4e07e9299d
commit 6226842691
9 changed files with 3178 additions and 3228 deletions

6064
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -9,45 +9,51 @@
"url": "https://github.com/orbitdb/orbit-db"
},
"engines": {
"node": ">=8.0.0"
"node": ">=10.0.0"
},
"main": "src/OrbitDB.js",
"dependencies": {
"cids": "^0.5.7",
"ipfs-pubsub-1on1": "~0.0.4",
"ipld-dag-pb": "0.14.11",
"localstorage-down": "^0.6.7",
"logplease": "^1.2.14",
"multihashes": "^0.4.12",
"orbit-db-access-controllers": "github:orbitdb/orbit-db-access-controllers",
"orbit-db-cache": "~0.2.4",
"orbit-db-counterstore": "~1.4.0",
"orbit-db-docstore": "~1.4.3",
"orbit-db-eventstore": "~1.4.0",
"orbit-db-feedstore": "~1.4.0",
"orbit-db-keystore": "~0.1.0",
"orbit-db-kvstore": "~1.4.0",
"orbit-db-counterstore": "github:orbitdb/orbit-db-counterstore",
"orbit-db-docstore": "github:orbitdb/orbit-db-docstore",
"orbit-db-eventstore": "github:orbitdb/orbit-db-eventstore",
"orbit-db-feedstore": "github:orbitdb/orbit-db-feedstore",
"orbit-db-identity-provider": "github:orbitdb/orbit-db-identity-provider",
"orbit-db-io": "~0.0.1",
"orbit-db-keystore": "github:orbitdb/orbit-db-keystore",
"orbit-db-kvstore": "github:orbitdb/orbit-db-kvstore",
"orbit-db-pubsub": "~0.5.5",
"orbit-db-store": "~2.5.3"
"orbit-db-store": "github:orbitdb/orbit-db-store",
"pify": "^4.0.1"
},
"devDependencies": {
"babel-core": "^6.26.0",
"babel-loader": "^7.1.2",
"babel-plugin-transform-runtime": "^6.23.0",
"babel-polyfill": "^6.26.0",
"babel-preset-es2015": "^6.24.1",
"datastore-level": "~0.8.0",
"go-ipfs-dep": "0.4.13",
"ipfs": "~0.30.0",
"ipfs-repo": "~0.22.1",
"ipfsd-ctl": "^0.37.5",
"babel-preset-env": "^1.7.0",
"datastore-level": "~0.10.0",
"go-ipfs-dep": "^0.4.18",
"ipfs": "^0.34.4",
"ipfs-repo": "^0.26.2",
"ipfsd-ctl": "^0.42.0",
"markdown-toc": "^1.2.0",
"mocha": "^4.0.1",
"mocha": "^5.2.0",
"p-each-series": "^1.0.0",
"p-map": "^1.2.0",
"p-map-series": "^1.0.0",
"p-whilst": "^1.0.0",
"remark-cli": "^5.0.0",
"remark-validate-links": "^7.0.0",
"rimraf": "^2.6.2",
"uglifyjs-webpack-plugin": "^1.1.4",
"webpack": "^3.8.1"
"webpack": "^4.25.1",
"webpack-cli": "^3.1.2"
},
"scripts": {
"examples": "npm run examples:node",
@ -61,6 +67,6 @@
"build:dist": "webpack --config conf/webpack.config.js --sort-modules-by size && mkdir -p examples/browser/lib && cp dist/orbitdb.min.js examples/browser/lib/orbitdb.min.js",
"build:debug": "webpack --config conf/webpack.debug.config.js --sort-modules-by size && mkdir -p examples/browser/lib && cp dist/orbitdb.js examples/browser/lib/orbitdb.js && cp dist/orbitdb.js.map examples/browser/lib/orbitdb.js.map",
"build:docs/toc": "markdown-toc --no-first1 -i README.md && markdown-toc --no-first1 -i API.md && markdown-toc --no-first1 -i GUIDE.md && markdown-toc --no-first1 -i CHANGELOG.md && markdown-toc --no-first1 -i FAQ.md ",
"build:es5": "babel src --out-dir ./dist/es5/ --presets babel-preset-es2015 --plugins babel-plugin-transform-runtime"
"build:es5": "babel src --out-dir ./dist/es5/ --presets babel-preset-env --plugins babel-plugin-transform-runtime"
}
}

View File

@ -9,10 +9,12 @@ const DocumentStore = require('orbit-db-docstore')
const Pubsub = require('orbit-db-pubsub')
const Cache = require('orbit-db-cache')
const Keystore = require('orbit-db-keystore')
const AccessController = require('./ipfs-access-controller')
const Identities = require('orbit-db-identity-provider')
let AccessControllers = require('orbit-db-access-controllers')
const OrbitDBAddress = require('./orbit-db-address')
const createDBManifest = require('./db-manifest')
const exchangeHeads = require('./exchange-heads')
const { isDefined, io } = require('./utils')
const Logger = require('logplease')
const logger = Logger.create("orbit-db")
@ -27,19 +29,49 @@ let databaseTypes = {
'keyvalue': KeyValueStore,
}
class OrbitDB {
constructor(ipfs, directory, options = {}) {
class OrbitDB {
constructor(ipfs, identity, options = {}) {
if (!isDefined(ipfs))
throw new Error('IPFS is a required argument. See https://github.com/orbitdb/orbit-db/blob/master/API.md#createinstance')
if (!isDefined(identity))
throw new Error('identity is a required argument. See https://github.com/orbitdb/orbit-db/blob/master/API.md#createinstance')
this._ipfs = ipfs
this.id = options.peerId || (this._ipfs._peerInfo ? this._ipfs._peerInfo.id._idB58String : 'default')
this.identity = identity
this.id = options.peerId
this._pubsub = options && options.broker
? new options.broker(this._ipfs)
: new Pubsub(this._ipfs, this.id)
this.stores = {}
this.directory = directory || './orbitdb'
this.keystore = options.keystore || Keystore.create(path.join(this.directory, this.id, '/keystore'))
this.directory = options.directory || './orbitdb'
this.keystore = options.keystore
this.cache = options.cache || Cache
this.key = this.keystore.getKey(this.id) || this.keystore.createKey(this.id)
this.stores = {}
this._directConnections = {}
// AccessControllers module can be passed in to enable
// testing with orbit-db-access-controller
AccessControllers = options.AccessControllers || AccessControllers
}
static async createInstance (ipfs, options = {}) {
if (!isDefined(ipfs))
throw new Error('IPFS is a required argument. See https://github.com/orbitdb/orbit-db/blob/master/API.md#createinstance')
const { id } = await ipfs.id()
const directory = options.directory || './orbitdb'
const keystore = options.keystore || Keystore.create(path.join(directory, id, '/keystore'))
const identity = options.identity || await Identities.createIdentity({
id: options.id || id,
keystore: keystore,
})
options = Object.assign({}, options, {
peerId: id ,
directory: directory,
keystore: keystore
})
const orbitdb = new OrbitDB(ipfs, identity, options)
return orbitdb
}
/* Databases */
@ -48,7 +80,7 @@ class OrbitDB {
return this.open(address, options)
}
async log (address, options) {
async log (address, options = {}) {
options = Object.assign({ create: true, type: 'eventlog' }, options)
return this.open(address, options)
}
@ -57,12 +89,12 @@ class OrbitDB {
return this.log(address, options)
}
async keyvalue (address, options) {
async keyvalue (address, options = {}) {
options = Object.assign({ create: true, type: 'keyvalue' }, options)
return this.open(address, options)
}
async kvstore (address, options) {
async kvstore (address, options = {}) {
return this.keyvalue(address, options)
}
@ -121,8 +153,7 @@ class OrbitDB {
let accessController
if (options.accessControllerAddress) {
accessController = new AccessController(this._ipfs)
await accessController.load(options.accessControllerAddress)
accessController = await AccessControllers.resolve(this, options.accessControllerAddress, options.accessController)
}
const cache = await this._loadCache(this.directory, address)
@ -134,9 +165,8 @@ class OrbitDB {
onClose: this._onClose.bind(this),
})
const store = new Store(this._ipfs, this.id, address, opts)
const store = new Store(this._ipfs, this.identity, address, opts)
store.events.on('write', this._onWrite.bind(this))
// ID of the store is the address as a string
const addr = address.toString()
this.stores[addr] = store
@ -211,25 +241,9 @@ class OrbitDB {
if (OrbitDBAddress.isValid(name))
throw new Error(`Given database name is an address. Please give only the name of the database!`)
// Create an AccessController
const accessController = new AccessController(this._ipfs)
/* Disabled temporarily until we do something with the admin keys */
// Add admins of the database to the access controller
// if (options && options.admin) {
// options.admin.forEach(e => accessController.add('admin', e))
// } else {
// // Default is to add ourselves as the admin of the database
// accessController.add('admin', this.key.getPublic('hex'))
// }
// Add keys that can write to the database
if (options && options.write && options.write.length > 0) {
options.write.forEach(e => accessController.add('write', e))
} else {
// Default is to add ourselves as the admin of the database
accessController.add('write', this.key.getPublic('hex'))
}
// Save the Access Controller in IPFS
const accessControllerAddress = await accessController.save(onlyHash)
// Create an AccessController, use IPFS AC as the default
options.accessController = Object.assign({}, { type: 'ipfs' }, options.accessController)
const accessControllerAddress = await AccessControllers.create(this, options.accessController.type, options.accessController || {})
// Save the manifest to IPFS
const manifestHash = await createDBManifest(this._ipfs, name, type, accessControllerAddress, onlyHash)
@ -242,8 +256,7 @@ class OrbitDB {
/*
options = {
admin: [], // array of keys that are the admins of this database (same as write access)
write: [], // array of keys that can write to this database
accessController: { write: [] } // array of keys that can write to this database
directory: './orbitdb', // directory in which to place the database files
overwrite: false, // whether we should overwrite the existing database if it exists
}
@ -268,7 +281,7 @@ class OrbitDB {
throw new Error(`Database '${dbAddress}' already exists!`)
// Save the database locally
await this._saveDBManifest(directory, dbAddress)
await this._addManifestToCache(directory, dbAddress)
logger.debug(`Created database '${dbAddress}'`)
@ -291,6 +304,7 @@ class OrbitDB {
*/
async open (address, options = {}) {
logger.debug(`open()`)
options = Object.assign({ localOnly: false, create: false }, options)
logger.debug(`Open database '${address}'`)
@ -319,6 +333,7 @@ class OrbitDB {
// Check if we have the database
const haveDB = await this._haveLocalData(cache, dbAddress)
logger.debug((haveDB ? 'Found' : 'Didn\'t find') + ` database '${dbAddress}'`)
// If we want to try and open the database local-only, throw an error
@ -331,8 +346,7 @@ class OrbitDB {
logger.debug(`Loading Manifest for '${dbAddress}'`)
// Get the database manifest from IPFS
const dag = await this._ipfs.object.get(dbAddress.root)
const manifest = JSON.parse(dag.toJSON().data)
const manifest = await io.read(this._ipfs, dbAddress.root)
logger.debug(`Manifest for '${dbAddress}':\n${JSON.stringify(manifest, null, 2)}`)
// Make sure the type from the manifest matches the type that was given as an option
@ -340,7 +354,7 @@ class OrbitDB {
throw new Error(`Database '${dbAddress}' is type '${manifest.type}' but was opened as '${options.type}'`)
// Save the database locally
await this._saveDBManifest(directory, dbAddress)
await this._addManifestToCache(directory, dbAddress)
// Open the the database
options = Object.assign({}, options, { accessControllerAddress: manifest.accessController })
@ -348,13 +362,12 @@ class OrbitDB {
}
// Save the database locally
async _saveDBManifest (directory, dbAddress) {
async _addManifestToCache (directory, dbAddress) {
const cache = await this._loadCache(directory, dbAddress)
await cache.set(path.join(dbAddress.toString(), '_manifest'), dbAddress.root)
logger.debug(`Saved manifest to IPFS as '${dbAddress.root}'`)
}
// Loads the locally saved database information (manifest, head hashes)
async _loadCache (directory, dbAddress) {
let cache
try {

View File

@ -1,96 +0,0 @@
'use strict'
class AccessController {
constructor () {
this._access = {
admin: [],
write: [],
read: [], // Not used atm
}
}
/* Overridable functions */
async load (address) {}
async save () {}
async canAppend(entry, identityProvider){
//verify identity?
if (this._access.write.includes('*'))
return true
if (this._access.write.includes(entry.identity.publicKey))
return true
return false
}
/* Properties */
get admin () {
return this._access.admin
}
get write () {
// Both admins and write keys can write
return this._access.write.concat(this._access.admin)
}
// Not used atm
get read () {
return this._access.read
}
/* Public Methods */
add (access, key) {
// if(!Object.keys(this._access).includes(access))
// throw new Error(`unknown access level: ${access}`)
// if (!this._access[access].includes(key))
// this._access[access].push(key)
// TODO: uniques only
switch (access) {
case 'admin':
this._access.admin.push(key)
break
case 'write':
this._access.write.push(key)
break
case 'read':
this._access.read.push(key)
break
default:
break
}
}
remove (access, key) {
const without = (arr, e) => {
const reducer = (res, val) => {
if (val !== key)
res.push(val)
return res
}
return arr.reduce(reducer, [])
}
// if(!Object.keys(this._access).includes(access))
// throw new Error(`unknown access level: ${access}`)
// if (this._access[access].includes(key))
// this._access[access] = without(this._access[access], key)
switch (access) {
case 'admin':
this._access.admin = without(this._access.admin, key)
break
case 'write':
this._access.write = without(this._access.write, key)
break
case 'read':
this._access.read = without(this._access.read, key)
break
default:
break
}
}
}
module.exports = AccessController

View File

@ -1,5 +1,5 @@
const path = require('path')
const { DAGNode } = require('ipld-dag-pb')
const io = require('orbit-db-io')
// Creates a DB manifest file and saves it in IPFS
const createDBManifest = async (ipfs, name, type, accessControllerAddress, onlyHash) => {
@ -8,21 +8,8 @@ const createDBManifest = async (ipfs, name, type, accessControllerAddress, onlyH
type: type,
accessController: path.join('/ipfs', accessControllerAddress),
}
let dag
const manifestJSON = JSON.stringify(manifest)
if (onlyHash) {
dag = await new Promise(resolve => {
DAGNode.create(Buffer.from(manifestJSON), (err, n) => {
if (err) {
throw err
}
resolve(n)
})
})
} else {
dag = await ipfs.object.put(Buffer.from(manifestJSON))
}
return dag.toJSON().multihash.toString()
return io.write(ipfs, 'dag-cbor', manifest, { onlyHash })
}
module.exports = createDBManifest

View File

@ -1,53 +0,0 @@
'use strict'
const AccessController = require('./access-controller')
const { DAGNode } = require('ipld-dag-pb')
class IPFSAccessController extends AccessController {
constructor (ipfs) {
super()
this._ipfs = ipfs
}
async load (address) {
// Transform '/ipfs/QmPFtHi3cmfZerxtH9ySLdzpg1yFhocYDZgEZywdUXHxFU'
// to 'QmPFtHi3cmfZerxtH9ySLdzpg1yFhocYDZgEZywdUXHxFU'
if (address.indexOf('/ipfs') === 0)
address = address.split('/')[2]
try {
const dag = await this._ipfs.object.get(address)
const obj = JSON.parse(dag.toJSON().data)
this._access = obj
} catch (e) {
console.log("ACCESS ERROR:", e)
}
}
async save (onlyHash) {
let hash
try {
const access = JSON.stringify(this._access, null, 2)
let dag
if (onlyHash) {
dag = await new Promise(resolve => {
DAGNode.create(Buffer.from(access), (err, n) => {
if (err) {
throw err
}
resolve(n)
})
})
} else {
dag = await this._ipfs.object.put(new Buffer(access))
}
hash = dag.toJSON().multihash.toString()
} catch (e) {
console.log("ACCESS ERROR:", e)
}
return hash
}
}
module.exports = IPFSAccessController

View File

@ -1,7 +1,9 @@
'use strict'
const path = require('path')
const multihash = require('multihashes')
const CID = require('cids')
const notEmpty = e => e !== '' && e !== ' '
class OrbitDBAddress {
constructor (root, path) {
@ -14,14 +16,19 @@ class OrbitDBAddress {
}
static isValid (address) {
const containsProtocolPrefix = (e, i) => !((i === 0 || i === 1) && address.toString().indexOf('/orbit') === 0 && e === 'orbitdb')
const parts = address.toString()
.split('/')
.filter((e, i) => !((i === 0 || i === 1) && address.toString().indexOf('/orbit') === 0 && e === 'orbitdb'))
.filter(e => e !== '' && e !== ' ')
.filter(containsProtocolPrefix)
.filter(notEmpty)
let accessControllerHash
const accessControllerHash = parts[0].indexOf('Qm') > -1 ? multihash.fromB58String(parts[0]) : null
try {
multihash.validate(accessControllerHash)
accessControllerHash = (parts[0].indexOf('zd') > -1 || parts[0].indexOf('Qm') > -1)
? new CID(parts[0]).toBaseEncodedString()
: null
} catch (e) {
return false
}
@ -30,7 +37,7 @@ class OrbitDBAddress {
}
static parse (address) {
if (!address)
if (!address)
throw new Error(`Not a valid OrbitDB address: ${address}`)
if (!OrbitDBAddress.isValid(address))

9
src/utils/index.js Normal file
View File

@ -0,0 +1,9 @@
'use strict'
const isDefined = require('./is-defined')
const io = require('orbit-db-io')
module.exports = {
isDefined,
io
}

5
src/utils/is-defined.js Normal file
View File

@ -0,0 +1,5 @@
'use strict'
const isDefined = (arg) => arg !== undefined && arg !== null
module.exports = isDefined