diff options
| author | Ev Bogue <ev@evbogue.com> | 2019-04-25 15:33:48 -0500 | 
|---|---|---|
| committer | Ev Bogue <ev@evbogue.com> | 2019-04-25 15:33:48 -0500 | 
| commit | bb78d6fb69e32fcb403c592e11f33283f9ff632e (patch) | |
| tree | 5fdb6dd19d8ef33d7143d9c5cd1f2394207ec331 | |
| parent | 6c60ac6e55da99aac1958c87ee36a6acb4d399b6 (diff) | |
major refactor -- new look and messages are stored as signatures - 1.3.0
| -rw-r--r-- | app.js | 224 | ||||
| -rw-r--r-- | bog.js | 128 | ||||
| -rw-r--r-- | css/style.css | 109 | ||||
| -rw-r--r-- | index.html | 19 | ||||
| -rw-r--r-- | lib.js | 230 | ||||
| -rw-r--r-- | package-lock.json | 64 | ||||
| -rw-r--r-- | package.json | 4 | ||||
| -rw-r--r-- | render.js | 143 | ||||
| -rw-r--r-- | server.js | 56 | ||||
| -rw-r--r-- | views.js | 59 | ||||
| -rw-r--r-- | welcome.js | 67 | 
11 files changed, 406 insertions, 697 deletions
| @@ -1,176 +1,94 @@  var screen = h('div', {id: 'screen'})  document.body.appendChild(screen) -function keyPage (keys) { -  var scroller = document.getElementById('scroller')  - -  var message = h('div', {classList: 'message'}) - -  message.appendChild(h('p', {innerHTML: marked('This is your ed25519 public/private keypair. It was generated using [Tweetnacl.js](https://tweetnacl.js.org/#/). Your public key is your identity when using [Bogbook](http://bogbook.com/), save your key in a safe place so that you can continue to use the same identity.')})) - -  // print stringified keypair -  message.appendChild(h('pre', {style: 'width: 80%'}, [h('code', [JSON.stringify(keys)])])) - -  // delete key button -  message.appendChild(h('button', { -    onclick: function () { -     localforage.removeItem('id', function () { -       location.hash = '' -       location.reload() -     }) -    } -  }, ['Delete Key'])) - -  var textarea = h('textarea', {placeholder: 'Import your existing ed25519 keypair'}) -  message.appendChild(textarea) -  message.appendChild(h('button', { -    onclick: function () { -      if (textarea.value) { -        localforage.setItem('id', JSON.parse(textarea.value)) -        location.reload() -      } -    } -  }, ['Import Key'])) - -  scroller.appendChild(message) -} - -function profilePage (src, keys) { -  var scroller = document.getElementById('scroller') - +function composer (keys, reply) { +  var messageDiv = h('div')    var message = h('div', {classList: 'message'}) -  var identify = h('input', {placeholder: 'Identify ' + src.substring(0, 10) + '...'}) +  var textarea = h('textarea', {placeholder: 'Write a new bog post...'}) -  message.appendChild(h('div', [ -    identify, -    h('button', {onclick: function () { -      if (identify.value) { -        var content = { -          author: keys.publicKey, -          type: 'name', -          naming: src, -          name: identify.value, -          timestamp: Date.now() -        } - -        identify.value = '' -        publish(content, keys) -        localforage.setItem('id', keys, function (err, published) { -          if (published) { -            location.reload() +  var publisher = h('div', [ +    textarea, +    h('button', { +      onclick: function () { +        if (textarea.value) { +          var content = { +            type: 'post', +            text: textarea.value, +            timestamp: Date.now() +          } +          if (reply) { +            content.reply = reply.key            } -        }) +          publish(content, keys).then(post => { +            open(post).then(msg => { +              textarea.value = '' +              if (reply) { +                messageDiv.removeChild(messageDiv.firstChild) +                if (messageDiv.firstChild) { +                  messageDiv.insertBefore(h('div', {classList: 'submessage'}, [render(msg, keys)]), messageDiv.childNodes[1]) +                } else { +                  messageDiv.appendChild(h('div', {classList: 'submessage'}, [render(msg, keys)])) +                } +              } else { +                if (messageDiv.firstChild) { +                  messageDiv.insertBefore(render(msg, keys), messageDiv.childNodes[1]) +                } else { +                  messageDiv.appendChild(render(msg, keys)) +                } +              } +            }) +          })  +        }        } -    }}, ['Identify']) -  ])) - -  scroller.appendChild(message) +    }, ['Publish']) +  ]) -  requestFeed(src, 'ws://localhost:8080/', keys.publicKey) - -  localforage.getItem(src, function (err, log) { -    if (log) { -      for (var i=0; i < log.length; i++) { -        var post = log[i] -        scroller.appendChild(renderMessage(post)) -      } -    } -  }) +  message.appendChild(publisher) +  messageDiv.appendChild(message) +  return messageDiv  } -function threadPage (src, keys) { -  var scroller = document.getElementById('scroller') -   -  localforage.getItem('log', function (err, log) { -    for (var i = log.length - 1; i >= 0; --i) { -      if (log[i].key === src) { -        var post = log[i] -        scroller.appendChild(renderMessage(post)) -      } -    } -  }) -} - -function publicPage (keys) { -  compose(keys) - -  localforage.getItem('log', function (err, log) { -    if (log) { -      for (var i=0; i < log.length; i++) { -        var post = log[i] -        scroller.appendChild(renderMessage(post)) -      } -      var newLog = log.sort(function (a, b) { -        return b.content.timestamp - a.content.timestamp -      }) -      if (newLog) { -        localforage.setItem('log', log) -      } -    } -  }) -}  - -function route () { -  localforage.getItem('id', function (err, keys) { +function route (keys) { +  src = window.location.hash.substring(1) -    src = window.location.hash.substring(1) -    var scroller = h('div', {id: 'scroller'}) -    var screen = document.getElementById('screen') -    screen.appendChild(scroller) +  var scroller = h('div', {id: 'scroller'}) +  var screen = document.getElementById('screen') +  screen.appendChild(scroller) -    if (src === 'key') { -      keyPage(keys) -    } else if (src[0] === '@') { -      profilePage(src, keys) -    } else if (src[0] === '%') { -      threadPage(src, keys) -    } else { -      publicPage(keys) -    } -  }) +  if (src === 'key') { +    keyPage(keys) +  } else if (src[0] === '@') { +    profilePage(src, keys) +  } else if (src[0] === '%') { +    threadPage(src, keys) +  } else { +    publicPage(keys) +  }  } -localforage.getItem('id', function (err, keys) { -  if (keys) { +keys().then(key => {  -    var navbar = h('div', {classList: 'navbar'}, [ -      h('div', {classList: 'internal'}, [ -        h('li', [h('a', {href: '/#'}, ['Home'])]), -        h('li', [h('a', {href: '#' + keys.publicKey}, [getName(keys.publicKey)])]), -        h('li', [h('a', {href: '/#key'}, ['Key'])]) -      ]) +  var navbar = h('div', {classList: 'navbar'}, [ +    h('div', {classList: 'internal'}, [ +      h('li', [h('a', {href: '#'}, ['Home'])]), +      h('li', [h('a', {href: '#' + key.publicKey}, [getName(key.publicKey)])]), +      h('li', [h('a', {href: '#key'}, ['Key'])])      ]) +  ]) +  document.body.appendChild(navbar) -    document.body.appendChild(navbar) - -    route() -  } else { -    var genkey = nacl.sign.keyPair() -    if (genkey) { -      var keys = { -        publicKey: '@' + nacl.util.encodeBase64(genkey.publicKey), -        privateKey: nacl.util.encodeBase64(genkey.secretKey) -      } -       -      if (keys.publicKey.includes('/')) { -        console.log('TRYING AGAIN') -        setTimeout(function () { -          location.reload() -        }, 10) -      } else { -        welcomeScreen(keys) -      } -    } -  } +  route(key)  }) - -  window.onhashchange = function () { -  var oldscreen = document.getElementById('screen') -  var newscreen = h('div', {id: 'screen'}) -  oldscreen.parentNode.replaceChild(newscreen, oldscreen) -  route() +  keys().then(key => { +    var oldscreen = document.getElementById('screen') +    var newscreen = h('div', {id: 'screen'}) +    oldscreen.parentNode.replaceChild(newscreen, oldscreen) + +    route(key) +  })  } + @@ -0,0 +1,128 @@ +// bog.open -- opens a signature and returns content if you pass a signature and a public key +// EX:  open(msg).then(content => { console.log(content) }) + +async function open (msg) { + +  var pubkey = nacl.util.decodeBase64(msg.author.substring(1)) +  var sig = nacl.util.decodeBase64(msg.signature) +  var opened = await JSON.parse(nacl.util.encodeUTF8(nacl.sign.open(sig, pubkey))) + +  opened.key = msg.key + +  return opened +} + +// bog.keys -- gets your public/private keypair, generates one if there is none +// EX: keys().then(key => { console.log(key)}) + +async function keys (key) {  +  var keypair = await localforage.getItem('id') +  if (keypair != null) { +    return keypair +  } else { +    var genkey = nacl.sign.keyPair() +    var keypair = { +      publicKey: '@' + nacl.util.encodeBase64(genkey.publicKey), +      privateKey: nacl.util.encodeBase64(genkey.secretKey) +    } +    localforage.setItem('id', keypair) +    return keypair  +  } +} + +// bog.get -- iterates over log and returns a post. +// EX: get('%x5T7KZ5haR2F59ynUuCggwEdFXlLHEtFoBQIyKYppZYerq9oMoIqH76YzXQpw2DnYiM0ugEjePXv61g3E4l/Gw==').then(msg => { console.log(msg)}) + +async function get (key) { +  var log = await localforage.getItem('log') +  if (log != null) { +    for (var i = log.length - 1; i >= 0; --i) { +      if (log[i].key === key) { +        return log[i] +      } +    } +  } +} + +// bog.getName -- iterates over a feed and returns a person's name + +function getName (id) { +  var name = h('span') + +  name.textContent = id.substring(0, 10) + '...' + +  return name +} + +// bog.log (feed) -- returns a specific feed if a parameter is passed, if not returns the entire log +// EX: bog().then(log => { console.log(log)}) +// EX: bog('@ExE3QXmBhYQlGVA3WM2BD851turNzwhruWbIpMd7rbQ=').then(log => { console.log(log)}) + + +async function bog (feed) { +  if (feed) { +    var log = await localforage.getItem(feed) +    return log +  } else { +    var log = await localforage.getItem('log') +    return log +  } +} + +// bog.publish -- publishes a new bog post and updates the feeds +// EX: publish({type: 'post', timestamp: Date.now(), text: 'Hello World'}).then(msg => { console.log(msg)}) + +async function publish (post, keys) { +  post.author = keys.publicKey + +  var message = { author: keys.publicKey } + +  var feed = await localforage.getItem(keys.publicKey) +  if (feed) { +    var firstMsg = await open(feed[0]) + +    post.seq = ++firstMsg.seq + +    message.key = '%' + nacl.util.encodeBase64(nacl.hash(nacl.util.decodeUTF8(JSON.stringify(post)))), +    message.signature = nacl.util.encodeBase64(nacl.sign(nacl.util.decodeUTF8(JSON.stringify(post)), nacl.util.decodeBase64(keys.privateKey))) + +    localforage.getItem('log').then(log => { +      if (log) { +        log.unshift(message) +        localforage.setItem('log', log) +      } else { +        var feed = [message] +        localforage.setItem('log', feed) +      } +    }) + +    feed.unshift(message) +    localforage.setItem(keys.publicKey, feed) + +    return message + +  } else { + +    post.seq = 0 + +    message.key = '%' + nacl.util.encodeBase64(nacl.hash(nacl.util.decodeUTF8(JSON.stringify(post)))), +    message.signature = nacl.util.encodeBase64(nacl.sign(nacl.util.decodeUTF8(JSON.stringify(post)), nacl.util.decodeBase64(keys.privateKey))) + +    localforage.getItem('log').then(log => { +      if (log) { +        log.unshift(message) +        localforage.setItem('log', log) +      } else { +        var feed = [message] +        localforage.setItem('log', feed) +      } +    }) + +    var feed = [message] +    localforage.setItem(keys.publicKey, feed) + +    return message +  } +} + + diff --git a/css/style.css b/css/style.css index a59af50..7a517d1 100644 --- a/css/style.css +++ b/css/style.css @@ -1,7 +1,7 @@  body {    font-family: 'Source Sans Pro'; -  background: #222; -  color: #f5f5f5; +  background: #f5f5f5; +  color: #5a5a5a;    overflow-wrap: break-word;    word-wrap: break-word;    word-break: break-word; @@ -49,12 +49,23 @@ hr {  .right { float: right;}  .message { -  background: #333; +  border: 1px solid #ddd; +  background: white;    margin-top: .5em;    padding: .3em .5em;    border-radius: 5px;  } +.message, .message > *, .navbar, .navbar > * { +  animation: fadein .5s; +} + +@keyframes fadein { +    from { opacity: 0; } +    to   { opacity: 1; } +} + +  .submessage {    margin-left: 2em;  } @@ -73,7 +84,7 @@ img.small {  }  a { -  color: cyan; +  color: #0088cc;    text-decoration: none;  } @@ -82,7 +93,7 @@ a:hover {  }  pre { -  color: violet; +  color: #dd1144;    width: 100%;    display: block;  } @@ -105,13 +116,17 @@ code, pre {  textarea, input {    font-family: 'Source Sans Pro';    font-size: 1em; -  background: #222; +  background: #f5f5f5;    padding: .5em; -  color: #f5f5f5; +  color: #5a5a5a;    border: none;    border-radius: 5px;  } +textarea:active, textarea:focus { +  background: white; +} +  textarea {    width: 100%;  } @@ -170,28 +185,82 @@ textarea {  button {    display: inline-block; -  padding: .25em .5em; -  margin: .15em; -  font-size: 1em; -  line-height: 1.2em; -  color: #d5d5d5; +  *display: inline; +  padding: 4px 12px; +  margin-top: .5em; +  margin-bottom: 0; +  *margin-left: .3em; +  font-size: 14px; +  line-height: 20px; +  color: #333333;    text-align: center; -  text-shadow: 0 1px 1px rgba(0, 0, 0, 0.75); +  text-shadow: 0 1px 1px rgba(255, 255, 255, 0.75);    vertical-align: middle;    cursor: pointer; -  background-color: #222; -  border: 1px solid #222; -  border-radius: 4px; +  background-color: #f5f5f5; +  *background-color: #e6e6e6; +  background-image: -moz-linear-gradient(top, #ffffff, #e6e6e6); +  background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ffffff), to(#e6e6e6)); +  background-image: -webkit-linear-gradient(top, #ffffff, #e6e6e6); +  background-image: -o-linear-gradient(top, #ffffff, #e6e6e6); +  background-image: linear-gradient(to bottom, #ffffff, #e6e6e6); +  background-repeat: repeat-x; +  border: 1px solid #cccccc; +  *border: 0; +  border-color: #e6e6e6 #e6e6e6 #bfbfbf; +  border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); +  border-bottom-color: #b3b3b3; +  -webkit-border-radius: 4px; +     -moz-border-radius: 4px; +          border-radius: 4px; +  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe6e6e6', GradientType=0); +  filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); +  *zoom: 1; +  -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05); +     -moz-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05); +          box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);  }  button:hover,  button:focus, +button:active, +button[disabled] { +  color: #333333; +  background-color: #e6e6e6; +  *background-color: #d9d9d9; +} +  button:active { -  color: white; -  background-color: black; +  background-color: #cccccc \9; +} + +button:first-child { +  *margin-left: 0;  } -button.active { -  background-color: #111; +button:hover, +button:focus { +  color: #333333; +  text-decoration: none; +  background-position: 0 -15px; +  -webkit-transition: background-position 0.1s linear; +     -moz-transition: background-position 0.1s linear; +       -o-transition: background-position 0.1s linear; +          transition: background-position 0.1s linear;  } +button:focus { +  outline: thin dotted #333; +  outline: 5px auto -webkit-focus-ring-color; +  outline-offset: -2px; +} + +button:active { +  background-image: none; +  outline: 0; +  -webkit-box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05); +     -moz-box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05); +          box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05); +} + + @@ -2,18 +2,19 @@    <head>      <title>Bogbook</title>      <meta name='viewport' content='width=device-width initial-scale=1' /> +    <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>      <link rel='stylesheet' href='./css/source-sans-pro.min.css' />      <link rel='stylesheet' href='./css/style.css' /> +    <script src="./lib/nacl.min.js"></script> +    <script src="./lib/nacl-util.min.js"></script> +    <script src="./lib/localforage.min.js"></script> +    <script src="./lib/marked.min.js"></script> +    <script src="./lib/misc.js"></script> +    <script async src="bog.js"></script> +    <script async src="render.js"></script> +    <script async src="views.js"></script> +    <script async src="app.js"></script>    </head>    <body> -  <script src="./lib/nacl.min.js"></script> -  <script src="./lib/nacl-util.min.js"></script> -  <script src="./lib/localforage.min.js"></script> -  <script src="./lib/marked.min.js"></script> -  <script src="./lib/misc.js"></script> -  <script src="lib.js"></script> -  <script src="welcome.js"></script> -  <script src="render.js"></script> -  <script src="app.js"></script>    </body>  </html> @@ -1,230 +0,0 @@ -/*function requestFeed (src, server, requester) { - -  console.log(src) -  console.log(server) - -  var ws = new WebSocket(server + src) - -  localforage.getItem(src, function (err, log) { -    if (log) { -      console.log(log) - -      var post = log[0] - -      var pubkey = nacl.util.decodeBase64(src.substring(1)) -      var sig = nacl.util.decodeBase64(post.signature) -      post.content = JSON.parse(nacl.util.encodeUTF8(nacl.sign.open(sig, pubkey))) - -      var seq = post.content.sequence - -      ws.onopen = function () { -        var req = { -          feed: src, -          seq, -          requester -        } -         -        console.log(req) - -        ws.send(JSON.stringify(req)) - -      } -      ws.onmessage = function (message) { -        var res = JSON.parse(message.data) -        if (res.seq == null) { -          console.log('SENDING ENTIRE LOG') -          var send = { -            feed: src, -            log, -            requester -          } -          ws.send(JSON.stringify(send)) -        } -        if (seq > res.req) { -          console.log('SENDING') -          console.log(log) -        } -      } -    } else { -      ws.onopen = function () { -        var seq = null - -      } -      ws.onmessage = function (message) { -        console.log(message.data) -      }  -    } -  }) -}*/ - -// publish new messages to your log -function publish (toPublish, keys) { - -  localforage.getItem(keys.publicKey, function (err, log) { -    if (log) { -      var lastPost = log[0] - -      var pubkey = nacl.util.decodeBase64(keys.publicKey.substring(1)) -      var sig = nacl.util.decodeBase64(lastPost.signature)  - -      var opened = JSON.parse(nacl.util.encodeUTF8(nacl.sign.open(sig, pubkey))) - -      var seq = opened.sequence - -      toPublish.sequence = ++seq -      toPublish.previous = nacl.util.encodeBase64(nacl.hash(nacl.util.decodeUTF8(JSON.stringify(log[0])))) - -      var author = keys.publicKey -      var key = '%' + nacl.util.encodeBase64(nacl.hash(nacl.util.decodeUTF8(JSON.stringify(toPublish)))) -      var signature = nacl.util.encodeBase64(nacl.sign(nacl.util.decodeUTF8(JSON.stringify(toPublish)), nacl.util.decodeBase64(keys.privateKey))) - -      var toPost = { -         author, -         key, -         signature -      } - -      console.log(toPost) - -      // update the log -      updateLog(keys.publicKey, toPost) - -      /*var scroller = document.getElementById('scroller') -      if (scroller.firstChild) { -        scroller.insertBefore(renderMessage(toPost), scroller.childNodes[1]) -      } else { -        scroller.appendChild(renderMessage(toPost)) -      }*/ - -    } else { -      toPublish.sequence = 0 - -      var toPost = { -         author: keys.publicKey, -         key: '%' + nacl.util.encodeBase64(nacl.hash(nacl.util.decodeUTF8(JSON.stringify(toPublish)))), -         signature: nacl.util.encodeBase64(nacl.sign(nacl.util.decodeUTF8(JSON.stringify(toPublish)), nacl.util.decodeBase64(keys.privateKey))) -      } - -      updateLog(keys.publicKey, toPost) - -      /*var scroller = document.getElementById('scroller') -      if (scroller.firstChild) { -        scroller.insertBefore(renderMessage(toPost), scroller.childNodes[1]) -      } else { -        scroller.appendChild(renderMessage(toPost)) -      }*/ -    } -  }) -} - -function compose (keys) { -  var message = h('div', {classList: 'message'}) - -  var scroller = document.getElementById('scroller') - -  scroller.insertBefore(message, scroller.firstChild) - -  var textarea = h('textarea', {placeholder: 'Write a new bog post'}) - -  message.appendChild(textarea) - -  var composer = h('div', [ -    h('button', { -      onclick: function () { -        if (textarea.value) { -          var toPublish = { -            author: keys.publicKey, -            type: 'post', -            text: textarea.value, -            timestamp: Date.now() -          } -          textarea.value = '' -          publish(toPublish, keys) -        } -      } -    }, ['Publish']) -  ]) -  message.appendChild(composer) -} - - -// update your log in the browser - -function updateLog (feed, post) { -  localforage.getItem(feed, function (err, log) { -    if (log) { -      log.unshift(post) -      localforage.setItem(feed, log, function () { -        console.log('FEED UPDATED') -      }) -    } else { -      log = [] -      log.unshift(post) -      localforage.setItem(feed, log, function () { -        console.log('FEED UPDATED') -      }) -    } -  }) - -  localforage.getItem('log', function (err, log) { -    if (log) { -      log.unshift(post) -      localforage.setItem('log', log, function () { -        console.log('LOG UPDATED') -        location.reload() -      }) -    } else { -      log = [] -      log.unshift(post) -      localforage.setItem('log', log, function () { -        console.log('LOG UPDATED') -        location.reload() -      }) -    } -  }) -} - -function getName (id) { -  var name = h('span') -  name.textContent = id.substring(0, 10) + '...' - -  localforage.getItem(id, function (err, log) { -    if (log) { -      for (var i=0; i < log.length; i++) { -        var post = log[i] - -        var pubkey = nacl.util.decodeBase64(post.author.substring(1)) -        var sig = nacl.util.decodeBase64(post.signature) -        post.content = JSON.parse(nacl.util.encodeUTF8(nacl.sign.open(sig, pubkey))) - -        //if (post.content) { -          if (post.content.type == 'name') { -            name.textContent = '@' + post.content.name -          } -        //} -      } -    } -  }) -  return name -} - -function getHeader (post, mini) { -  var inner -  if (mini) { -    var inner = mini -  } - -  var head = h('span', [ -    h('a', {href: '#' + post.key}, [ -      h('p', {classList: 'right'}, [human(new Date(post.content.timestamp))]), -    ]), -    h('p', [ -      h('a', {href: '#' + post.content.author}, [ -        getName(post.content.author) -      ]), -      inner -    ]) -  ]) -  return head -} - diff --git a/package-lock.json b/package-lock.json index 05b71a4..89c4e57 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,75 +1,21 @@  {    "name": "bogbook", -  "version": "1.1.0", +  "version": "1.2.0",    "lockfileVersion": 1,    "requires": true,    "dependencies": { -    "async-limiter": { -      "version": "1.0.0", -      "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.0.tgz", -      "integrity": "sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg==" -    }, -    "ecstatic": { -      "version": "3.3.1", -      "resolved": "https://registry.npmjs.org/ecstatic/-/ecstatic-3.3.1.tgz", -      "integrity": "sha512-/rrctvxZ78HMI/tPIsqdvFKHHscxR3IJuKrZI2ZoUgkt2SiufyLFBmcco+aqQBIu6P1qBsUNG3drAAGLx80vTQ==", -      "requires": { -        "he": "^1.1.1", -        "mime": "^1.6.0", -        "minimist": "^1.1.0", -        "url-join": "^2.0.5" -      } -    }, -    "he": { -      "version": "1.2.0", -      "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", -      "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==" -    },      "is-wsl": {        "version": "1.1.0",        "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz",        "integrity": "sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0="      }, -    "mime": { -      "version": "1.6.0", -      "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", -      "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" -    }, -    "minimist": { -      "version": "1.2.0", -      "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", -      "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" -    }, -    "opn": { -      "version": "6.0.0", -      "resolved": "https://registry.npmjs.org/opn/-/opn-6.0.0.tgz", -      "integrity": "sha512-I9PKfIZC+e4RXZ/qr1RhgyCnGgYX0UEIlXgWnCOVACIvFgaC9rz6Won7xbdhoHrd8IIhV7YEpHjreNUNkqCGkQ==", +    "open": { +      "version": "6.2.0", +      "resolved": "https://registry.npmjs.org/open/-/open-6.2.0.tgz", +      "integrity": "sha512-Vxf6HJkwrqmvh9UAID3MnMYXntbTxKLOSfOnO7LJdzPf3NE3KQYFNV0/Lcz2VAndbRFil58XVCyh8tiX11fiYw==",        "requires": {          "is-wsl": "^1.1.0"        } -    }, -    "tweetnacl": { -      "version": "1.0.1", -      "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.1.tgz", -      "integrity": "sha512-kcoMoKTPYnoeS50tzoqjPY3Uv9axeuuFAZY9M/9zFnhoVvRfxz9K29IMPD7jGmt2c8SW7i3gT9WqDl2+nV7p4A==" -    }, -    "tweetnacl-util": { -      "version": "0.15.0", -      "resolved": "https://registry.npmjs.org/tweetnacl-util/-/tweetnacl-util-0.15.0.tgz", -      "integrity": "sha1-RXbBzuXi1j0gf+5S8boCgZSAvHU=" -    }, -    "url-join": { -      "version": "2.0.5", -      "resolved": "https://registry.npmjs.org/url-join/-/url-join-2.0.5.tgz", -      "integrity": "sha1-WvIvGMBSoACkjXuCxenC4v7tpyg=" -    }, -    "ws": { -      "version": "6.2.1", -      "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.1.tgz", -      "integrity": "sha512-GIyAXC2cB7LjvpgMt9EKS2ldqr0MTrORaleiOno6TweZ6r3TKtoFQWay/2PceJ3RuBasOHzXNn5Lrw1X0bEjqA==", -      "requires": { -        "async-limiter": "~1.0.0" -      }      }    }  } diff --git a/package.json b/package.json index f73137a..cca809b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@  {    "name": "bogbook", -  "version": "1.2.0", +  "version": "1.3.0",    "description": "secure blockchain logging (blogging, without the l) -- bogging",    "main": "server.js",    "scripts": { @@ -11,7 +11,7 @@    "license": "MIT",    "dependencies": {      "ecstatic": "^3.3.1", -    "opn": "^6.0.0", +    "open": "^6.2.0",      "tweetnacl": "^1.0.1",      "tweetnacl-util": "^0.15.0",      "ws": "^6.2.1" @@ -1,118 +1,53 @@ -function renderMessage (post) { -  var messageDiv = h('messageDiv', {id: post.key}) -  var message = h('div', {classList: 'message'}) - -  var pubkey = nacl.util.decodeBase64(post.author.substring(1)) -  var sig = nacl.util.decodeBase64(post.signature) -  post.content = JSON.parse(nacl.util.encodeUTF8(nacl.sign.open(sig, pubkey))) - - -  if (post.content.type == 'name') { -    var mini = h('span', [ -      ' identified as ', -      post.content.name +function getHeader (post, mini) { +  var head = h('span', [ +    h('a', {href: '#' + post.key}, [ +      h('p', {classList: 'right'}, [human(new Date(post.timestamp))]), +    ]), +    h('p', [ +      h('a', {href: '#' + post.author}, [ +        getName(post.author) +      ]), +      mini       ]) +  ]) +  return head +} -    message.appendChild(getHeader(post, mini)) - -    messageDiv.appendChild(message) -  } - - -  if (post.content.type == 'post') { +function render (msg, keys) { +  var messageDiv = h('messageDiv', {id: msg.key}) +  var message = h('div', {classList: 'message'}) -    /* -    NEED TO UNBOX THREADS -    localforage.getItem('log', function (err, log) { -      if (log) { -        for (var i = log.length - 1; i >= 0; --i) { -          -          if (log[i].content.reply == post.key) { -            var nextPost = log[i] -            var messageExists = (document.getElementById(nextPost.key) !== null); -            if (!messageExists) { -              messageDiv.appendChild(h('div', {classList: 'submessage'}, [ -                renderMessage(nextPost) -              ])) -            } +  bog().then(log => { +    log.forEach(function (nextPost) { +      open(nextPost).then(nextMessage => { +        var messageExists = (document.getElementById(nextMessage.key) !== null); +        if (nextMessage.reply == msg.key) { +          if (!messageExists) { +            messageDiv.appendChild(h('div', {classList: 'submessage'}, [render(nextMessage, keys)]))            }          } -      } -    })*/ -   -    var renderer = new marked.Renderer(); -    renderer.link = function(href, title, text) { -        if ((href[0] == '@') || (href[0] == '%')) { -          href = '#' + href -        } -        var link = marked.Renderer.prototype.link.call(this, href, title, text); -        return link -    } -     -    marked.setOptions({ -        renderer: renderer -    }); +      }) +    }) +  })  + +  if (msg.type == 'post') { +    message.appendChild(getHeader(msg)) -    message.appendChild(getHeader(post)) -     -    if (post.content.reply) { +    if (msg.reply) {        message.appendChild(h('span', [          're: ', -        h('a', {href: '#' + post.content.reply}, [post.content.reply.substring(0, 10) + '...']) +        h('a', {href: '#' + msg.reply}, [msg.reply.substring(0, 10) + '...'])        ]))      } -    message.appendChild(h('div', {innerHTML: marked(post.content.text)})) - -    message.appendChild(h('span', {id: post.key + 'src', classList: 'right'}, [ -      h('a', { -        onclick: function () { -          message.appendChild(h('pre', [JSON.stringify(post)])) -          var span = document.getElementById(post.key + 'src')  -          span.parentNode.removeChild(span) -        } -      }, ['[src]']) -    ])) - -    var gotName = getName(post.content.author) - -    localforage.getItem('id', function (err, keys) { - -      var publishButton = h('button', { -        onclick: function () { -          if (textarea.value) { -            var content = { -              author: keys.publicKey, -              type: 'post', -              text: textarea.value, -              reply: post.key, -              timestamp: Date.now() -            } -            publish(content, keys) -            message.removeChild(textarea) -            message.removeChild(publishButton) -          } -        } -      }, ['Publish']) - -      var textarea = h('textarea', {placeholder: 'Reply to this bog post'}, ['['+ gotName.textContent + '](' + post.content.author + ')']) - -      var replyButton = h('button', { -        classList: 'replyButton:' + post.key, -        onclick: function () { -          message.removeChild(replyButton) -          message.appendChild(textarea) -          message.appendChild(publishButton) -           -        } -      }, ['Reply']) - -      message.appendChild(replyButton) -    }) - -    messageDiv.appendChild(message) -  } +    message.appendChild(h('div', [msg.text])) +    message.appendChild(h('button', { +      onclick: function () { +        messageDiv.appendChild(composer(keys, msg))  +      } +    }, ['Reply'])) +  }  +  messageDiv.appendChild(message)    return messageDiv  } - @@ -2,62 +2,12 @@  var http = require('http')  var serve = require('ecstatic') -var opn = require('opn') +var open = require('open')  http.createServer(    serve({ root: __dirname})  ).listen(8089) -opn('http://localhost:8089') - -// websocket server (8080) - -var WebSocket = require('ws') -var fs = require('fs') -var nacl = require('tweetnacl') -    nacl.util = require('tweetnacl-util') - -var wserver = new WebSocket.Server({ port: 8080 }) - -wserver.on('connection', function (ws) { -  ws.on('message', function (message) { -    var data = JSON.parse(message) -    console.log(data) - -    // initial req/res contains a sequence number -    if (data.seq) { -      if (fs.existsSync(__dirname + '/bogs/' + data.feed)) { -        fs.readFile(__dirname + '/bogs/' + data.feed, 'UTF-8', function (err, data) { -          if (data) { -            var log = JSON.parse(data) -            console.log(log) -          } -        }) -      } else { -        var res = { -          feed: data.feed, -          seq: null -        } -        console.log(res) -        ws.send(JSON.stringify(res)) -      } - -    } - -    // if the client has a longer log, it'll send one for the server to save -    if (data.log) { -      console.log(data) -      if (fs.existsSync(__dirname + '/bogs/' + data.feed)) { -        var log = JSON.parse(fs.readFileSync(__dirname + '/bogs/' + data.feed)) -        console.log(log) - -      } else { -        fs.writeFile(__dirname + '/bogs/' + data.feed, JSON.stringify(data.log), function (err, success) { -          console.log('saved ' + data.feed + ' sent by ' + data.requester) -        }) -      } -    } - -  }) -}) +open('http://localhost:8089') +// ws server (8080) diff --git a/views.js b/views.js new file mode 100644 index 0000000..94fcb5d --- /dev/null +++ b/views.js @@ -0,0 +1,59 @@ +function threadPage (src, keys) { +  get(src).then(msg => { +    open(msg).then(post => { +      scroller.appendChild(render(post, keys)) +    }) +  }) +} + +function profilePage (src, keys) { +  bog(src).then(log => { +    log.forEach(function (msg) { +      open(msg).then(post => { +        scroller.appendChild(render(post, keys)) +      }) +    }) +  }) +} + +function publicPage (keys) { +  scroller.appendChild(composer(keys)) +  bog().then(log => { +    log.forEach(function (msg) { +      open(msg).then(post => { +        scroller.appendChild(render(post, keys)) +      }) +    }) +  }) +} + +function keyPage (keys) { +  var message = h('div', {classList: 'message'}) + +  message.appendChild(h('p', {innerHTML: marked('This is your ed25519 public/private keypair. It was generated using [TweetNaCl.js](https://tweetnacl.js.org/#/). Your public key is your identity when using [Bogbook](http://bogbook.com/), save your key in a safe place so that you can continue to use the same identity.')})) + +  message.appendChild(h('pre', {style: 'width: 80%'}, [h('code', [JSON.stringify(keys)])])) + +  message.appendChild(h('button', { +    onclick: function () { +     localforage.removeItem('id', function () { +       location.hash = '' +       location.reload() +     }) +    } +  }, ['Delete Key'])) + +  var textarea = h('textarea', {placeholder: 'Import your existing ed25519 keypair'}) +  message.appendChild(textarea) +  message.appendChild(h('button', { +    onclick: function () { +      if (textarea.value) { +        localforage.setItem('id', JSON.parse(textarea.value)) +        location.reload() +      } +    } +  }, ['Import Key'])) + +  scroller.appendChild(message) +} + diff --git a/welcome.js b/welcome.js deleted file mode 100644 index 342756a..0000000 --- a/welcome.js +++ /dev/null @@ -1,67 +0,0 @@ -function welcomeScreen (keys) { -  var screen = document.getElementById('screen') - -  var scroller = h('div', {id: 'scroller'}) -  screen.appendChild(scroller) - -  var message = h('div', {classList: 'message'}) - -  scroller.appendChild(message) - -  message.appendChild(h('h1', ['Welcome to Bogbook'])) -  message.appendChild(h('p', ['Bogbook is a distributed blogging network of signed append-only feeds. We call them "bogs".'])) -  message.appendChild(h('p', ['Please note: Bogbook is experimental software, not for use in producton environments. Expect bugs and breaking changes. Pull-requests are needed.'])) -  message.appendChild(h('p', {innerHTML: marked('View the code: [http://github.com/bogbook/bog](http://github.com/bogbook/bog). Questions? [ev@evbogue.com](mailto:ev@evbogue.com).')})) -  message.appendChild(h('hr')) -  message.appendChild(h('h3', ['Get started'])) -  message.appendChild(h('p', {innerHTML: marked('This is an ed25519 public/private signing keypair. It was generated using [TweetNaCl.js](https://tweetnacl.js.org/#/)')})) -  message.appendChild(h('pre', [JSON.stringify(keys)])) -  message.appendChild(h('p', ['Right now, this keypair exists only in memory. When you leave this page, the keypair will vanish forever. If you refresh this page you\'ll receive a new keypair.'])) -  message.appendChild(h('p', {innerHTML: marked('To save this keypair, identify with a handle below. Once you identify, your public/private keypair will be stored in your browser using [localForage.js](https://localforage.github.io/localForage). Save your keypair somewhere safe to preserve your identity.')})) -  message.appendChild(h('hr')) -  message.appendChild(h('h3', ['Identify'])) - -  var identify = h('input', {placeholder: 'Your Name'}) - -  message.appendChild(h('div', [ -    identify, -    h('button', {onclick: function () { -      if (identify.value) { -        var toPublish = { -          author: keys.publicKey, -          type: 'name', -          naming: keys.publicKey, -          name: identify.value, -          timestamp: Date.now() -        } - -        identify.value = '' -        publish(toPublish, keys) -        localforage.setItem('id', keys, function (err, published) { -          if (published) { -            location.hash = '' -            location.reload() -          } -        }) -      } -    }}, ['Identify']) -  ])) -  message.appendChild(h('p', ['When you click [Identify], you will post your first message to your append-only bog, your ed25519 keypair will be saved in your browser, and the page will reload. Don\'t forget to back up your key! and happy bogging.'])) -  message.appendChild(h('hr')) -  message.appendChild(h('h3', ['Already have a key?'])) -  message.appendChild(h('p', ['Import it here. Make sure to sync your existing feed from a Bogbook \'pub\' before posting a message.'])) - -  var textarea = h('textarea', {placeholder: 'Import your existing ed25519 keypair'}) -  message.appendChild(textarea) -  message.appendChild(h('button', { -    onclick: function () { -      if (textarea.value) { -        localforage.setItem('id', JSON.parse(textarea.value)) -        location.hash = '' -        location.reload() -      } -    } -  }, ['Import Key'])) - - -} | 
