~ev/bogbookv3

1af0b81b7b4ad1b89278bf754cba1fdfe0924d2c — Ev Bogue 9 days ago b961be3
crdt replication nearly completed
8 files changed, 239 insertions(+), 71 deletions(-)

M browserlog.js
M composer.js
M denobog.js
A log.js
M pub.js
M replicate.js
M routes/query.js
M sbog.js
M browserlog.js => browserlog.js +75 -17
@@ 1,33 1,49 @@
import { open } from './sbog.js'
import { blast } from './replicate.js'

const kv = new IdbKvStore('ssboat')

var log = []
var feeds = []
//var feeds = []
var feedlist = []

kv.get('log', function (err, file) {
  if (file) { 
    log = file
    log.sort((a,b) => a.timestamp - b.timestamp)
    kv.set('log', log)
    if (log[0]) {
      for (let i = log.length -1; i >= 0; i--) {
        if (!feedlist.includes(log[i].author)) {
          feedlist.push(log[i].author)
        }
      }
    }
  }
})

kv.get('feeds', function ( err, file) {
  if (file) { feeds = file }
})
//kv.get('feeds', function ( err, file) {
//  if (file) { feeds = file }
//})

let newData = false

export function save () {
  kv.set('log', log)
  kv.set('feeds', feeds)
//  kv.set('feeds', feeds)
}

setInterval(function () {
  if (newData) {
    kv.set('log', log)
    kv.set('feeds', feeds)
    log.sort((a,b) => a.timestamp - b.timestamp)
    for (let i = log.length -1; i >= 0; i--) {
      if (!feedlist.includes(log[i].author)) {
        feedlist.push(log[i].author)
      }
    }

  //  kv.set('feeds', feeds)
  } 
}, 10000)



@@ 37,13 53,32 @@ export const logs = function logs (query) {
      return log
    },
    getFeeds: function () {
      return feeds
      return feedlist
    },
    getFeed: function (query) {
      if (feeds[query]) {
        return feeds[query]
    //getFeed: function (query) {
    //  if (feeds[query]) {
    //    return feeds[query]
    //  }
    //},
    getLatest: async function (pubkey) {
      if (log.length) {
        for (let i = log.length -1; i >= 0; i--) {
          if (log[i].raw.substring(44, 88) === pubkey) {
            console.log(log[i])
            return log[i]
          }
        }
      }
    },
    get: async function (hash) {
      if (log.length) {
        for (let i = log.length -1; i >= 0; i--) {
          if (log[i].raw.includes(hash)) {
            return log[i]
          }
        }
      }
    }, 
    query: async function (query) {
      if (query) {
        let querylog = []


@@ 66,20 101,43 @@ export const logs = function logs (query) {
        }
      } 
    },
    addMsg: function (msg) {
    add: function (msg) {
      open(msg).then(opened => {
        if (opened) {  
          if (msg.substring(0, 44) === msg.substring(88, 132)) {
            feeds[msg.substring(44,88)] = [msg]
            log.push(opened)
            newData = true
        if (opened) {
          let got = false
          if (log[0]) {
            for (let i = log.length -1; i >= 0; i--) {
              if (log[i].raw.substring(0, 44) === query) {
                 got = true
              }
              if (i === 0 && got === false) {
                blast(opened.raw.substring(0,44))
                log.push(opened)
                newData = true
              }
            }
          } else {
            feeds[msg.substring(44,88)].unshift(msg)
            blast(opened.raw.substring(0,44))
            log.push(opened)
            newData = true
          }
        }
      })
    }
    //addMsg: function (msg) {
    //  open(msg).then(opened => {
    //    if (opened) {  
    //      if (msg.substring(0, 44) === msg.substring(88, 132)) {
    //        feeds[msg.substring(44,88)] = [msg]
    //        log.push(opened)
    //        newData = true
    //      } else {
    //        feeds[msg.substring(44,88)].unshift(msg)
    //        log.push(opened)
    //        newData = true
    //      }
    //    }
    //  })
    //}
  }
}()

M composer.js => composer.js +33 -33
@@ 8,37 8,37 @@ import { getName, getImage } from './avatar.js'

const kv = new IdbKvStore('ssboat')

function getContacts (textarea, preview) {
  const feeds = logs.getFeeds()
  var span = h('span')

  var button = h('button', {onclick: function () {
    if (!span.childNodes[1]) {
      var addrs = h('span')
      span.appendChild(addrs)
      Object.keys(feeds).forEach(function (key, index) {
        addrs.appendChild(h('button', {onclick: function () {
          kv.get('name:' + key).then(name => {
            if (textarea.selectionStart || textarea.selectionEnd) {
              textarea.value = textarea.value.substring(0, textarea.selectionStart)
                + ' [' + name + '](' + key + ') ' +
                textarea.value.substring(textarea.selectionEnd, textarea.value.length)
            } else {
              textarea.value = textarea.value + ' [' + name + '](' + key + ')'
            }
            preview.innerHTML = marked(textarea.value)
          })
        }}, [getImage(key), getName(key)]))
      })
    } else {
      span.removeChild(span.childNodes[1])
    }
  }}, ['📇 '])

  span.appendChild(button)

  return span
}
//function getContacts (textarea, preview) {
//  const feeds = logs.getFeeds()
//  var span = h('span')
//
//  var button = h('button', {onclick: function () {
//    if (!span.childNodes[1]) {
//      var addrs = h('span')
//      span.appendChild(addrs)
//      Object.keys(feeds).forEach(function (key, index) {
//        addrs.appendChild(h('button', {onclick: function () {
//          kv.get('name:' + key).then(name => {
//            if (textarea.selectionStart || textarea.selectionEnd) {
//              textarea.value = textarea.value.substring(0, textarea.selectionStart)
//                + ' [' + name + '](' + key + ') ' +
//                textarea.value.substring(textarea.selectionEnd, textarea.value.length)
//            } else {
//              textarea.value = textarea.value + ' [' + name + '](' + key + ')'
//            }
//            preview.innerHTML = marked(textarea.value)
//          })
//        }}, [getImage(key), getName(key)]))
//      })
//    } else {
//      span.removeChild(span.childNodes[1])
//    }
//  }}, ['📇 '])
//
//  span.appendChild(button)
//
//  return span
//}

function photoAdder (textarea, preview) {



@@ 151,8 151,8 @@ export function composer (msg) {
    preview,
    textarea,
    publishButton,
    photoAdder(textarea, preview),
    getContacts(textarea, preview)
    photoAdder(textarea, preview)
    //getContacts(textarea, preview)
  ])

  if (src != 'home') {

M denobog.js => denobog.js +5 -2
@@ 5,8 5,11 @@ import { decode, encode } from './lib/base64.js'
export async function open (msg) {
  const author = msg.substring(44, 88)
  const sig = msg.substring(132)
  const hash = sha256(decode(sig))
   
  const hash = new Uint8Array(await crypto.subtle.digest(
    "SHA-256",
    decode(sig)
  ))

  if (encode(hash) === msg.substring(0, 44)) {
    const opened = nacl.sign.open(decode(sig), decode(author))
    const message = JSON.parse(new TextDecoder().decode(opened))

A log.js => log.js +76 -0
@@ 0,0 1,76 @@
import { open } from './sbog.js'
import { ensureDir, exists } from 'https://deno.land/std@0.129.0/fs/mod.ts'

if (await exists(path + 'log')) {
  log = JSON.parse(await Deno.readTextFile(path + 'log'))
  log.sort((a,b) => a.timestamp - b.timestamp)
}

let newData = false

setInterval(function () {
  if (newData) {
    Deno.writeTextFile(path + 'log', JSON.stringify(log))
    Deno.writeTextFile(path + 'config.json', JSON.stringify(config))
    for (var key in feeds) {
      var value = feeds[key]
      Deno.writeTextFile(path + 'bogs/' + key, JSON.stringify(value))
    }
    newData = false
  } else {
    //console.log('No new data')
  }
}, 10000)

export const logs = function logs (query) {
  return {
    getLog: async function () {
      return log
    },
    //getFeeds: function () {
    //  return feeds
    //},
    //getFeed: function (query) {
    //  if (feeds[query]) {
    //    return feeds[query]
    //  }
    //},
    query: async function (query) {
      if (query) {
        let querylog = []
        if (log.length) {
          for (let i = log.length -1; i >= 0; i--) {
            
            if ((log[i].raw.substring(0, 44) === query) || (log[i].raw.substring(44,88) == query)) {
              querylog.unshift(log[i])
            }
            if (query.startsWith('?')) {
              const search = query.substring(1).replace(/%20/g, ' ').toUpperCase()
              if (log[i].text && log[i].text.toUpperCase().includes(search)) {
                querylog.unshift(log[i])
              }
            }
            if (i === 0) {
              return querylog
            }
          }
        }
      } 
    },
    add: function (msg) {
      open(msg).then(opened => {
        if (opened) {  
          if (msg.substring(0, 44) === msg.substring(88, 132)) {
            feeds[msg.substring(44,88)] = [msg]
            log.push(opened)
            newData = true
          } else {
            feeds[msg.substring(44,88)].unshift(msg)
            log.push(opened)
            newData = true
          }
        }
      })
    }
  }
}()

M pub.js => pub.js +33 -3
@@ 1,10 1,39 @@
import { keys } from './keys.js'
import { open } from './denobog.js'

const sockets = new Set()

export async function servePub (e) {

const log = []

function processReq (req, ws) {
  if (req.length === 44) { 
    if (log[0]) {
      let got = false
      for (let i = log.length; i >= 0; i--) {
        if (log[i].raw.includes(req)) {
          console.log('SEND THIS TO PEER')
          got = true
          console.log(log[i].raw)
          ws.send(log[i].raw)
        }
        if (i === 0 && !got) {
          console.log('WE DO NOT HAVE IT')
          ws.send(req)
        }
      }
    } else {
      console.log('WE HAVE NO DATA AT ALL')
      ws.send(req)    
    }
  }
  if (req.length > 44) {
    open(req).then(opened => {
      console.log(opened)
    })
  }
}

export async function servePub (e) {
  const { socket, response } = Deno.upgradeWebSocket(e.request)
  const ws = socket
  sockets.add(ws)


@@ 13,7 42,8 @@ export async function servePub (e) {
  }

  ws.onmessage = (e) => {
    console.log(JSON.parse(e.data))
    console.log(e.data)
    processReq(e.data, ws)
  }

  ws.onclose = function () {

M replicate.js => replicate.js +13 -12
@@ 13,23 13,17 @@ export function blast (msg) {
function replicate (ws) {
  // first check for my feed
  logs.query(keys.pubkey()).then(log => {
    if (!log) {
      ws.send(JSON.stringify({req: keys.pubkey(), seq: -1}))
    } else {
      ws.send(JSON.stringify({req: keys.pubkey(), seq: log.length}))
    }
    ws.send(keys.pubkey())
  })

  // next check for the route feed

  const feeds = logs.getFeeds()

  var src = window.location.hash.substring(1)
  if (src && !feeds[src]) {
  if (src.length === 44) {
    console.log(src)
    logs.query(src).then(query => {
      if (!query.length) {
        ws.send(JSON.stringify({req: src, seq: -1}))  
        ws.send(src)  
      }
    })
  } 


@@ 41,9 35,9 @@ function replicate (ws) {
    timer = setInterval(function () {
      //console.log('timer')
      const feeds = logs.getFeeds()
      //console.log(feeds)
      Object.keys(feeds).forEach(function (key, index) {
        ws.send(JSON.stringify({req: key, seq: feeds[key].length}))
      console.log(feeds)
      feeds.forEach(function (feed) {
        ws.send(feed)
      })
    }, 10000)
  }


@@ 76,6 70,13 @@ export function connect (server) {
  }
  
  ws.onmessage = (msg) => {
    if (msg.data.length === 44) {
      logs.get(msg.data).then(got => {
        if (got) {
          ws.send(got.raw)
        }
      })
    }
    console.log(msg.data)
  }


M routes/query.js => routes/query.js +1 -1
@@ 76,7 76,7 @@ export function query (scroller, src) {
    if (log[0]) {
      adder(log, src, scroller)
    } else {
      blast(JSON.stringify({req: src, seq: -1}))
      blast(src)
    }
  })  
} 

M sbog.js => sbog.js +3 -3
@@ 11,13 11,13 @@ export async function publish (obj) {
  const sig = nacl.sign(tosign, decode(keys.privkey()))
  const hash = sha256.hash(sig) 

  let authorfeed = logs.getFeed(obj.author)
  let authorfeed = await logs.getLatest(obj.author)
  let previous
  if (!authorfeed) {
    previous = encode(hash)
  } else { previous = authorfeed[0].substring(0, 44)}
  } else { previous = authorfeed.raw.substring(0, 44)}
  const msg = encode(hash) + obj.author + previous + encode(sig) 
  logs.addMsg(msg)
  logs.add(msg)
  return msg
}