2015-11-11 15:51:07 +01:00
|
|
|
/*
|
|
|
|
This file contains the IPFS Boards API. It's a simple abstraction over the
|
|
|
|
js-ipfs-api that also provides an additional level of caching for this
|
|
|
|
particular application. Let's hope it turns out decent
|
2015-11-14 15:03:38 +01:00
|
|
|
|
|
|
|
Needs to be browserified to work in the browser
|
2015-11-11 15:51:07 +01:00
|
|
|
*/
|
|
|
|
|
2015-11-14 18:26:56 +01:00
|
|
|
// EventEmitter used to communicate with clients
|
2015-11-14 15:03:38 +01:00
|
|
|
var EventEmitter = require('wolfy87-eventemitter')
|
|
|
|
|
2015-11-11 09:18:36 +01:00
|
|
|
function asObj(str,done){
|
|
|
|
var obj
|
|
|
|
try {
|
|
|
|
obj = JSON.parse(str)
|
|
|
|
} catch (e) {
|
2015-11-11 12:38:10 +01:00
|
|
|
return done(e,null)
|
2015-11-11 09:18:36 +01:00
|
|
|
}
|
2015-11-11 12:38:10 +01:00
|
|
|
done(null,obj)
|
2015-11-11 09:18:36 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
function replyAsObj(res,isJson,done){
|
|
|
|
if(res.readable){
|
|
|
|
// Is a stream
|
2015-11-14 12:06:37 +01:00
|
|
|
console.log('got stream')
|
2015-11-11 09:18:36 +01:00
|
|
|
res.setEncoding('utf8')
|
|
|
|
var data = ''
|
|
|
|
res.on('data',d => {
|
2015-11-14 12:06:37 +01:00
|
|
|
console.log('got stream data:',d)
|
2015-11-11 09:18:36 +01:00
|
|
|
data += d
|
|
|
|
})
|
|
|
|
res.on('end',() => {
|
|
|
|
if(isJson) {
|
|
|
|
asObj(data,done)
|
|
|
|
} else {
|
|
|
|
done(null,data)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
} else {
|
2015-11-14 16:26:03 +01:00
|
|
|
//console.log('got string:',res)
|
2015-11-11 09:18:36 +01:00
|
|
|
// Is a string
|
|
|
|
if(isJson){
|
|
|
|
asObj(res,done)
|
|
|
|
} else {
|
|
|
|
done(null,res)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function BoardsAPI(ipfs){
|
|
|
|
this.ipfs = ipfs
|
|
|
|
this.version = 'dev'
|
2015-11-16 11:56:25 +01:00
|
|
|
this.baseurl = '/ipfs-boards-profile/'
|
2015-11-11 15:22:58 +01:00
|
|
|
this.users = {} // userID : profileHash
|
2015-11-14 18:38:46 +01:00
|
|
|
this.resolving_ipns = {} // to check if a resolve is already in progress
|
2015-11-14 18:26:56 +01:00
|
|
|
this.ee = new EventEmitter()
|
2015-11-14 23:55:55 +01:00
|
|
|
if(localStorage !== undefined){
|
|
|
|
// Use localStorage to store the IPNS cache
|
|
|
|
var stored = localStorage.getItem('ipfs-boards-user-cache')
|
|
|
|
try {
|
|
|
|
this.users = JSON.parse(stored)
|
|
|
|
if(this.users === null || this.users === undefined){
|
|
|
|
this.users = {}
|
|
|
|
}
|
|
|
|
} catch(e){
|
|
|
|
this.users = {}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
BoardsAPI.prototype.backupCache = function(){
|
|
|
|
if(localStorage !== undefined){
|
|
|
|
// Use localStorage to store the IPNS cache
|
|
|
|
localStorage.setItem('ipfs-boards-user-cache',JSON.stringify(this.users))
|
|
|
|
}
|
2015-11-11 15:22:58 +01:00
|
|
|
}
|
|
|
|
|
2015-11-14 18:26:56 +01:00
|
|
|
// Rewrote this to use event emitters. Should also add periodic rechecking
|
|
|
|
BoardsAPI.prototype.resolveIPNS = function(n,handler){
|
2015-11-16 13:20:15 +01:00
|
|
|
if(handler && handler.apply) this.ee.on(n,handler)
|
2015-11-11 12:38:10 +01:00
|
|
|
var cached = this.users[n]
|
2015-11-14 16:26:03 +01:00
|
|
|
//console.log('Cached is',cached)
|
2015-11-11 12:38:10 +01:00
|
|
|
if(cached){
|
2015-11-14 18:26:56 +01:00
|
|
|
this.ee.emit(n,cached)
|
2015-11-11 12:38:10 +01:00
|
|
|
}
|
2015-11-14 18:38:46 +01:00
|
|
|
if(this.resolving_ipns[n] != true){
|
|
|
|
this.resolving_ipns[n] = true
|
|
|
|
this.ipfs.name.resolve(n,(err,r) => {
|
2015-11-16 13:20:15 +01:00
|
|
|
this.resolving_ipns[n] = false
|
2015-11-14 18:38:46 +01:00
|
|
|
setTimeout(_ => {
|
2015-11-16 13:20:15 +01:00
|
|
|
console.log('Launched automatic check for IPNS address',n)
|
2015-11-14 23:33:50 +01:00
|
|
|
this.resolveIPNS(n)
|
2015-11-16 13:20:15 +01:00
|
|
|
},10*1000)
|
2015-11-14 18:38:46 +01:00
|
|
|
if(!err) console.log('Resolved',n,'to',r.Path)
|
|
|
|
if(err){
|
|
|
|
// Communicate error
|
|
|
|
this.ee.emit(n,undefined,err)
|
2015-11-14 23:55:55 +01:00
|
|
|
} else if(cached !== r.Path){
|
2015-11-14 18:38:46 +01:00
|
|
|
//console.log('oldcache',this.users)
|
|
|
|
//console.log('Setting cache for',n,'to',r.Path)
|
|
|
|
this.users[n] = r.Path
|
2015-11-14 23:55:55 +01:00
|
|
|
this.backupCache()
|
2015-11-16 13:20:15 +01:00
|
|
|
console.log('Address for',n,'was updated to',r.Path)
|
2015-11-14 18:38:46 +01:00
|
|
|
this.ee.emit(n,r.Path)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
2015-11-14 18:26:56 +01:00
|
|
|
return this.ee
|
2015-11-11 12:38:10 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
BoardsAPI.prototype.isUserProfile = function(addr,done){
|
2015-11-16 11:56:25 +01:00
|
|
|
this.ipfs.cat(addr+this.baseurl+'ipfs-boards-version.txt',(err,r) => {
|
2015-11-11 12:38:10 +01:00
|
|
|
if(err) return done(false)
|
|
|
|
replyAsObj(r,false,(_,res) => {
|
|
|
|
var v = res.trim()
|
|
|
|
console.log('Version for',addr,'is',v)
|
|
|
|
done(v)
|
|
|
|
})
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2015-11-14 15:03:38 +01:00
|
|
|
BoardsAPI.prototype.isUser = function(s,done){
|
2015-11-11 12:38:10 +01:00
|
|
|
var ss = s.split('/')
|
|
|
|
var addr = ss[ss.length-1]
|
|
|
|
// Try to see if they run IPFS Boards
|
2015-11-14 18:26:56 +01:00
|
|
|
this.resolveIPNS(addr,(url,err) => {
|
2015-11-14 15:03:38 +01:00
|
|
|
if(err){
|
|
|
|
if(done) done(false)
|
|
|
|
return console.log('Cannot resolve',addr,':',err)
|
|
|
|
}
|
2015-11-11 12:38:10 +01:00
|
|
|
this.isUserProfile(url,isit => {
|
|
|
|
if(isit == this.version){
|
|
|
|
console.log(addr,'is a user')
|
|
|
|
this.users[addr] = url
|
2015-11-14 15:03:38 +01:00
|
|
|
if(done) done(true,addr,url)
|
|
|
|
} else if(done) done(false)
|
2015-11-11 12:38:10 +01:00
|
|
|
})
|
2015-11-14 18:26:56 +01:00
|
|
|
return true // remove myself from listeners
|
2015-11-11 12:38:10 +01:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
BoardsAPI.prototype.searchUsers = function(){
|
2015-11-11 09:18:36 +01:00
|
|
|
// Look at our peers
|
2015-11-11 12:38:10 +01:00
|
|
|
this.ipfs.swarm.peers((err,r) => {
|
|
|
|
if(err) return console.log(err)
|
|
|
|
replyAsObj(r,true,(e,reply) => {
|
2015-11-11 15:22:58 +01:00
|
|
|
console.log('Checking',reply.Strings.length,'peers')
|
2015-11-14 15:03:38 +01:00
|
|
|
reply.Strings.forEach(item => {
|
|
|
|
this.isUser(item,(isit,addr,url) => {
|
|
|
|
if(isit){
|
2015-11-14 18:26:56 +01:00
|
|
|
this.ee.emit('found user',addr,url)
|
2015-11-14 15:03:38 +01:00
|
|
|
}
|
|
|
|
})
|
|
|
|
})
|
2015-11-11 09:18:36 +01:00
|
|
|
})
|
|
|
|
})
|
2015-11-14 18:26:56 +01:00
|
|
|
return this.ee
|
2015-11-11 09:18:36 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
BoardsAPI.prototype.getProfile = function(userID,done){
|
2015-11-14 12:06:37 +01:00
|
|
|
console.log('profile requested for',userID)
|
2015-11-14 18:26:56 +01:00
|
|
|
this.resolveIPNS(userID,(url,err) => {
|
2015-11-11 09:18:36 +01:00
|
|
|
if(err){
|
2015-11-16 11:56:25 +01:00
|
|
|
this.ee.emit('error',err)
|
2015-11-11 09:18:36 +01:00
|
|
|
done(err,null)
|
2015-11-14 15:03:38 +01:00
|
|
|
} else {
|
|
|
|
// Download actual profile
|
2015-11-16 11:56:25 +01:00
|
|
|
this.ipfs.cat(url+this.baseurl+'profile.json',(err2,res) => {
|
2015-11-14 15:03:38 +01:00
|
|
|
if(err2){
|
2015-11-16 11:56:25 +01:00
|
|
|
this.ee.emit('error',err2)
|
2015-11-14 15:03:38 +01:00
|
|
|
done(err2,null)
|
|
|
|
} else {
|
|
|
|
// It already returns a JSON?
|
|
|
|
done(null,res)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
// Get other info
|
2015-11-16 11:56:25 +01:00
|
|
|
this.ipfs.ls(url+this.baseurl+'boards/',(err2,res) => {
|
2015-11-14 15:03:38 +01:00
|
|
|
if(!err2){
|
|
|
|
console.log('RES',res)
|
|
|
|
var l = res.Objects[0].Links.map(i => {
|
|
|
|
return { name: i.Name, hash: i.Hash }
|
|
|
|
})
|
2015-11-16 13:20:15 +01:00
|
|
|
this.ee.emit('boards for '+userID,l)
|
2015-11-16 11:56:25 +01:00
|
|
|
} else {
|
|
|
|
this.ee.emit('error',err2)
|
|
|
|
}
|
2015-11-14 15:03:38 +01:00
|
|
|
})
|
|
|
|
}
|
2015-11-14 18:26:56 +01:00
|
|
|
return true // remove myself from listeners
|
2015-11-11 09:18:36 +01:00
|
|
|
})
|
2015-11-14 18:26:56 +01:00
|
|
|
return this.ee
|
2015-11-11 09:18:36 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
BoardsAPI.prototype.getBoardSettings = function(userID,board,done){
|
2015-11-14 18:26:56 +01:00
|
|
|
this.resolveIPNS(userID,(r,e) => {
|
2015-11-14 16:26:03 +01:00
|
|
|
if(e){
|
2015-11-16 11:56:25 +01:00
|
|
|
this.ee.emit('error',e)
|
2015-11-14 16:26:03 +01:00
|
|
|
done(e)
|
2015-11-11 09:18:36 +01:00
|
|
|
} else {
|
2015-11-16 11:56:25 +01:00
|
|
|
var url = r+this.baseurl+'boards/'+board+'/settings.json'
|
2015-11-14 16:26:03 +01:00
|
|
|
this.ipfs.cat(url,(err,res) => {
|
|
|
|
if(err){
|
2015-11-16 11:56:25 +01:00
|
|
|
this.ee.emit('error',err)
|
2015-11-14 16:26:03 +01:00
|
|
|
done(err)
|
|
|
|
} else {
|
|
|
|
// It's already json...
|
|
|
|
done(err,res)
|
|
|
|
}
|
|
|
|
})
|
2015-11-11 09:18:36 +01:00
|
|
|
}
|
2015-11-14 18:26:56 +01:00
|
|
|
return true // remove myself from listeners
|
2015-11-11 09:18:36 +01:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2015-11-14 15:03:38 +01:00
|
|
|
BoardsAPI.prototype.getPostsInBoard = function(adminID,board){
|
|
|
|
/*
|
2015-11-11 15:22:58 +01:00
|
|
|
this.getBoardSettings(administratorID,board,(err,res) => {
|
|
|
|
// NEEDS: board settings structure definition
|
2015-11-14 15:03:38 +01:00
|
|
|
// For now we only list admin's posts
|
2015-11-11 15:22:58 +01:00
|
|
|
})
|
2015-11-14 15:03:38 +01:00
|
|
|
*/
|
2015-11-16 11:56:25 +01:00
|
|
|
this.getUserPostListInBoard(adminID,board,(err,res) => {
|
2015-11-14 15:03:38 +01:00
|
|
|
if(err){
|
|
|
|
console.log(err)
|
|
|
|
} else res.forEach(item => {
|
|
|
|
this.ipfs.cat(item.hash,(err2,r) => {
|
|
|
|
if(err2){
|
2015-11-16 11:56:25 +01:00
|
|
|
this.ee.emit('error',err2)
|
2015-11-14 15:03:38 +01:00
|
|
|
console.log('Could not download post',item,'of',board+'@'+adminID)
|
|
|
|
} else {
|
|
|
|
// It already returns a JSON?
|
2015-11-14 18:26:56 +01:00
|
|
|
this.ee.emit('post',r,item.hash)
|
2015-11-14 15:03:38 +01:00
|
|
|
}
|
|
|
|
})
|
|
|
|
})
|
|
|
|
})
|
2015-11-14 18:26:56 +01:00
|
|
|
return this.ee
|
2015-11-11 09:18:36 +01:00
|
|
|
}
|
|
|
|
|
2015-11-14 15:03:38 +01:00
|
|
|
BoardsAPI.prototype.getUserPostListInBoard = function(user,board,done){
|
2015-11-14 18:26:56 +01:00
|
|
|
this.resolveIPNS(user,(url,err) => {
|
2015-11-11 15:22:58 +01:00
|
|
|
if(err){
|
2015-11-16 11:56:25 +01:00
|
|
|
this.ee.emit('error',err)
|
2015-11-11 15:22:58 +01:00
|
|
|
done(err)
|
2015-11-16 11:56:25 +01:00
|
|
|
} else this.ipfs.ls(url+this.baseurl+'posts/'+board,(e,r) => {
|
2015-11-11 15:22:58 +01:00
|
|
|
if(e){
|
2015-11-16 11:56:25 +01:00
|
|
|
this.ee.emit('error',e)
|
2015-11-11 15:22:58 +01:00
|
|
|
done(e)
|
2015-11-14 15:03:38 +01:00
|
|
|
} else if(r && !r.split){
|
|
|
|
console.log('Found',r.Objects[0].Links.length,'posts in',board,'at',user)
|
2015-11-16 11:56:25 +01:00
|
|
|
this.ee.emit('post count',board,user,r.Objects[0].Links.length)
|
2015-11-14 15:03:38 +01:00
|
|
|
var l = r.Objects[0].Links.map(i => {
|
2015-11-11 15:22:58 +01:00
|
|
|
return { date: i.Name, hash: i.Hash }
|
|
|
|
})
|
2015-11-14 15:03:38 +01:00
|
|
|
done(null,l)
|
2015-11-11 15:22:58 +01:00
|
|
|
}
|
|
|
|
})
|
2015-11-14 18:26:56 +01:00
|
|
|
return true // remove myself from listeners
|
2015-11-11 15:22:58 +01:00
|
|
|
})
|
2015-11-16 11:56:25 +01:00
|
|
|
return this.ee
|
2015-11-11 09:18:36 +01:00
|
|
|
}
|
|
|
|
|
2015-11-16 11:56:25 +01:00
|
|
|
BoardsAPI.prototype.getCommentsFor = function(parent,board,adminID){
|
2015-11-14 23:55:55 +01:00
|
|
|
// Create an EventEmitter, start looking and emit an event for every new comment
|
2015-11-16 11:56:25 +01:00
|
|
|
// Consider the rules of @adminID#board
|
2015-11-11 09:18:36 +01:00
|
|
|
}
|
|
|
|
|
2015-11-14 23:55:55 +01:00
|
|
|
// API for publishing content and managing to be done later...
|
2015-11-11 09:18:36 +01:00
|
|
|
|
|
|
|
// Initialize API
|
|
|
|
BoardsAPI.prototype.init = function(done){
|
|
|
|
this.ipfs.id( (err, res) => {
|
|
|
|
if(err){
|
2015-11-16 11:56:25 +01:00
|
|
|
console.log('Error while getting OWN ID:',err)
|
|
|
|
this.ee.emit('error',err)
|
2015-11-11 09:18:36 +01:00
|
|
|
done(err)
|
2015-11-14 23:55:55 +01:00
|
|
|
} else if(res.ID){
|
2015-11-11 09:18:36 +01:00
|
|
|
console.log('I am',res.ID)
|
|
|
|
this.id = res.ID
|
2015-11-11 12:38:10 +01:00
|
|
|
this.isUser(res.ID)
|
2015-11-11 09:18:36 +01:00
|
|
|
console.log('Version is',this.version)
|
2015-11-16 11:56:25 +01:00
|
|
|
this.ipfs.add(new Buffer('ipfs:boards:version:'+this.version),(err2,r) => {
|
|
|
|
if(err2){
|
|
|
|
this.ee.emit('error',err2)
|
|
|
|
console.log('Error while calculating version hash:',err2)
|
|
|
|
done(err2)
|
2015-11-14 23:55:55 +01:00
|
|
|
} else {
|
2015-11-15 21:10:58 +01:00
|
|
|
if(r && r[0] && r[0].Hash) this.version_hash = r[0].Hash
|
2015-11-14 23:55:55 +01:00
|
|
|
console.log('Version hash is',this.version_hash)
|
|
|
|
done(null)
|
2015-11-14 15:03:38 +01:00
|
|
|
}
|
2015-11-11 09:18:36 +01:00
|
|
|
})
|
|
|
|
}
|
|
|
|
})
|
2015-11-16 13:20:15 +01:00
|
|
|
this.ipfs.version((err,res) => {
|
|
|
|
if(err){
|
|
|
|
this.ee.emit('error',err)
|
|
|
|
} else {
|
|
|
|
this.ipfs_version = res.Version
|
|
|
|
console.log('IPFS Version is',res.Version)
|
|
|
|
}
|
|
|
|
})
|
2015-11-11 09:18:36 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
module.exports = BoardsAPI
|