mirror of
https://github.com/amark/gun.git
synced 2025-07-03 11:22:32 +00:00
commit
e9e595d209
25
README.md
25
README.md
@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
**GUN** is an _ecosystem_ of tools that let you <u>build tomorrow's dApps, today</u>.
|
**GUN** is an _ecosystem_ of tools that let you <u>build tomorrow's dApps, today</u>.
|
||||||
|
|
||||||
Decentralized alternatives to [Reddit](https://notabug.io/), [YouTube](https://d.tube/), [Wikipedia](https://news.ycombinator.com/item?id=17685682), etc. are already pushing terabytes of daily P2P traffic on GUN. We are a [friendly community](https://gitter.im/amark/gun) creating a free fun future for freedom:
|
Decentralized alternatives to [Reddit](https://notabug.io/t/whatever/comments/36588a16b9008da4e3f15663c2225e949eca4a15/gpu-bot-test), [YouTube](https://d.tube/), [Wikipedia](https://news.ycombinator.com/item?id=17685682), etc. are already pushing terabytes of daily P2P traffic on GUN. We are a [friendly community](https://gitter.im/amark/gun) creating a free fun future for freedom:
|
||||||
|
|
||||||
<table>
|
<table>
|
||||||
<tr>
|
<tr>
|
||||||
@ -30,7 +30,7 @@ For now, it is best to start with GUN and _just use it_ to learn the basics, sin
|
|||||||
|
|
||||||
## Quickstart
|
## Quickstart
|
||||||
|
|
||||||
- Try the [interactive tutorial](https://gun.eco/think.html) in the browser (**5min** ~ average developer).
|
- Try the [interactive tutorial](https://gun.eco/docs/Todo-Dapp) in the browser (**5min** ~ average developer).
|
||||||
- Or `npm install gun` and run the examples with `cd node_modules/gun && npm start` (**5min** ~ average developer).
|
- Or `npm install gun` and run the examples with `cd node_modules/gun && npm start` (**5min** ~ average developer).
|
||||||
|
|
||||||
> **Note:** If you don't have [node](http://nodejs.org/) or [npm](https://www.npmjs.com/), read [this](https://github.com/amark/gun/blob/master/examples/install.sh) first.
|
> **Note:** If you don't have [node](http://nodejs.org/) or [npm](https://www.npmjs.com/), read [this](https://github.com/amark/gun/blob/master/examples/install.sh) first.
|
||||||
@ -100,12 +100,15 @@ On that note, let's get some official shout outs covered first:
|
|||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
Thanks to:<br/>
|
Thanks to:<br/>
|
||||||
|
<a href="https://github.com/robertheessels">Robert Heessels</a>,
|
||||||
<a href="http://qxip.net/">Lorenzo Mangani</a>,
|
<a href="http://qxip.net/">Lorenzo Mangani</a>,
|
||||||
|
<a href="https://nlnet.nl/">NLnet Foundation</a>,
|
||||||
<a href="http://github.com/samliu">Sam Liu</a>,
|
<a href="http://github.com/samliu">Sam Liu</a>,
|
||||||
<a href="http://github.com/ddombrow">Daniel Dombrowsky</a>,
|
<a href="http://github.com/ddombrow">Daniel Dombrowsky</a>,
|
||||||
<a href="http://github.com/vincentwoo">Vincent Woo</a>,
|
<a href="http://github.com/vincentwoo">Vincent Woo</a>,
|
||||||
<a href="http://github.com/coolaj86">AJ ONeal</a>,
|
<a href="http://github.com/coolaj86">AJ ONeal</a>,
|
||||||
<a href="http://github.com/ottman">Bill Ottman</a>,
|
<a href="http://github.com/ottman">Bill Ottman</a>,
|
||||||
|
<a href="http://github.com/mikewlange">Mike Lange</a>,
|
||||||
<a href="http://github.com/ctrlplusb">Sean Matheson</a>,
|
<a href="http://github.com/ctrlplusb">Sean Matheson</a>,
|
||||||
<a href="http://github.com/alanmimms">Alan Mimms</a>,
|
<a href="http://github.com/alanmimms">Alan Mimms</a>,
|
||||||
<a href="https://github.com/dfreire">Dário Freire</a>,
|
<a href="https://github.com/dfreire">Dário Freire</a>,
|
||||||
@ -129,14 +132,14 @@ The goal was to build a P2P database that could survive living inside **any** br
|
|||||||
|
|
||||||
<img src="https://gun.eco/see/compare.png" title="comparison table">
|
<img src="https://gun.eco/see/compare.png" title="comparison table">
|
||||||
|
|
||||||
Technically, **GUN is a graph synchronization protocol** with a *lightweight embedded engine*, capable of doing *[20M+ API ops/sec](https://gun.eco/docs/100000-ops-sec-in-IE6-on-2GB-Atom-CPU)* in **just ~9KB gzipped size**.
|
Technically, **GUN is a graph synchronization protocol** with a *lightweight embedded engine*, capable of doing *[20M+ API ops/sec](https://gun.eco/docs/Performance)* in **just ~9KB gzipped size**.
|
||||||
|
|
||||||
## Documentation
|
## Documentation
|
||||||
|
|
||||||
<table>
|
<table>
|
||||||
<tr>
|
<tr>
|
||||||
<td style="border: 0;"><h3><a href="https://github.com/amark/gun/wiki/API">API reference</a></h3></td>
|
<td style="border: 0;"><h3><a href="https://gun.eco/docs/API">API reference</a></h3></td>
|
||||||
<td style="border: 0;"><h3><a href="https://gun.eco/think.html">Tutorials</a></h3></td>
|
<td style="border: 0;"><h3><a href="https://gun.eco/docs/Todo-Dapp">Tutorials</a></h3></td>
|
||||||
<td style="border: 0;"><h3><a href="https://github.com/amark/gun/tree/master/examples">Examples</a></h3></td>
|
<td style="border: 0;"><h3><a href="https://github.com/amark/gun/tree/master/examples">Examples</a></h3></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
@ -150,20 +153,20 @@ Technically, **GUN is a graph synchronization protocol** with a *lightweight emb
|
|||||||
<td style="border: 0;"><h3><a href="https://github.com/Stefdv/gun-ui-lcd#syncing">Webcomponents</a></h3></td>
|
<td style="border: 0;"><h3><a href="https://github.com/Stefdv/gun-ui-lcd#syncing">Webcomponents</a></h3></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td style="border: 0;"><h3><a href="https://github.com/amark/gun/wiki/CAP-Theorem">CAP Theorem Tradeoffs</a></h3></td>
|
<td style="border: 0;"><h3><a href="https://gun.eco/docs/CAP-Theorem">CAP Theorem Tradeoffs</a></h3></td>
|
||||||
<td style="border: 0;"><h3><a href="https://gun.eco/distributed/matters.html">How Data Sync Works</a></h3></td>
|
<td style="border: 0;"><h3><a href="https://gun.eco/distributed/matters.html">How Data Sync Works</a></h3></td>
|
||||||
<td style="border: 0;"><h3><a href="https://github.com/amark/gun/wiki/porting-gun">How GUN is Built</a></h3></td>
|
<td style="border: 0;"><h3><a href="https://gun.eco/docs/Porting-GUN">How GUN is Built</a></h3></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td style="border: 0;"><h3><a href="https://github.com/amark/gun/wiki/auth">Crypto Auth</a></h3></td>
|
<td style="border: 0;"><h3><a href="https://gun.eco/docs/Auth">Crypto Auth</a></h3></td>
|
||||||
<td style="border: 0;"><h3><a href="https://github.com/amark/gun/wiki/Modules">Modules</a></h3></td>
|
<td style="border: 0;"><h3><a href="https://gun.eco/docs/Awesome-GUN">Modules</a></h3></td>
|
||||||
<td style="border: 0;"><h3><a href="https://github.com/amark/gun/wiki/roadmap">Roadmap</a></h3></td>
|
<td style="border: 0;"><h3><a href="https://gun.eco/docs/Roadmap">Roadmap</a></h3></td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
This would not be possible without **community contributors**, big shout out to:
|
This would not be possible without **community contributors**, big shout out to:
|
||||||
|
|
||||||
**[anywhichway](https://github.com/anywhichway) ([Block Storage](https://github.com/anywhichway/gun-block))**; **[beebase](https://github.com/beebase) ([Quasar](https://github.com/beebase/gun-vuex-quasar))**; **[BrockAtkinson](https://github.com/BrockAtkinson) ([brunch config](https://github.com/BrockAtkinson/brunch-gun))**; **[Brysgo](https://github.com/brysgo) ([GraphQL](https://github.com/brysgo/graphql-gun))**; **[d3x0r](https://github.com/d3x0r) ([SQLite](https://github.com/d3x0r/gun-db))**; **[forrestjt](https://github.com/forrestjt) ([file.js](https://github.com/amark/gun/blob/master/lib/file.js))**; **[hillct](https://github.com/hillct) (Docker)**; **[JosePedroDias](https://github.com/josepedrodias) ([graph visualizer](http://acor.sl.pt:9966))**; **[JuniperChicago](https://github.com/JuniperChicago) ([cycle.js bindings](https://github.com/JuniperChicago/cycle-gun))**; **[jveres](https://github.com/jveres) ([todoMVC](https://github.com/jveres/todomvc))**; **[kristianmandrup](https://github.com/kristianmandrup) ([edge](https://github.com/kristianmandrup/gun-edge))**; **[Lightnet](https://github.com/Lightnet)** ([User Kitchen Sink Playground](https://gdb-auth-vue-node.glitch.me/)); **[lmangani](https://github.com/lmangani) ([Cytoscape Visualizer](https://github.com/lmangani/gun-scape), [Cassandra](https://github.com/lmangani/gun-cassandra), [Fastify](https://github.com/lmangani/fastify-gundb), [LetsEncrypt](https://github.com/lmangani/polyGun-letsencrypt))**; **[mhelander](https://github.com/mhelander) ([SEA](https://github.com/amark/gun/blob/master/sea.js))**; [omarzion](https://github.com/omarzion) ([Sticky Note App](https://github.com/omarzion/stickies)); [PsychoLlama](https://github.com/PsychoLlama) ([LevelDB](https://github.com/PsychoLlama/gun-level)); **[RangerMauve](https://github.com/RangerMauve) ([schema](https://github.com/gundb/gun-schema))**; **[robertheessels](https://github.com/swifty) ([gun-p2p-auth](https://github.com/swifty/gun-p2p-auth))**; [sbeleidy](https://github.com/sbeleidy); **[Sean Matheson](https://github.com/ctrlplusb) ([Observable/RxJS/Most.js bindings](https://github.com/ctrlplusb/gun-most))**; **[Stefdv](https://github.com/stefdv) (Polymer/web components)**; **[sjones6](https://github.com/sjones6) ([Flint](https://github.com/sjones6/gun-flint))**; **[zrrrzzt](https://github.com/zrrrzzt) ([JWT Auth](https://gist.github.com/zrrrzzt/6f88dc3cedee4ee18588236756d2cfce))**; **[88dev](https://github.com/88dev) ([Database Viewer](https://github.com/88dev/gun-show))**;
|
**[anywhichway](https://github.com/anywhichway) ([Block Storage](https://github.com/anywhichway/gun-block))**; **[beebase](https://github.com/beebase) ([Quasar](https://github.com/beebase/gun-vuex-quasar))**; **[BrockAtkinson](https://github.com/BrockAtkinson) ([brunch config](https://github.com/BrockAtkinson/brunch-gun))**; **[Brysgo](https://github.com/brysgo) ([GraphQL](https://github.com/brysgo/graphql-gun))**; **[d3x0r](https://github.com/d3x0r) ([SQLite](https://github.com/d3x0r/gun-db))**; **[forrestjt](https://github.com/forrestjt) ([file.js](https://github.com/amark/gun/blob/master/lib/file.js))**; **[hillct](https://github.com/hillct) (Docker)**; **[JosePedroDias](https://github.com/josepedrodias) ([graph visualizer](http://acor.sl.pt:9966))**; **[JuniperChicago](https://github.com/JuniperChicago) ([cycle.js bindings](https://github.com/JuniperChicago/cycle-gun))**; **[jveres](https://github.com/jveres) ([todoMVC](https://github.com/jveres/todomvc))**; **[kristianmandrup](https://github.com/kristianmandrup) ([edge](https://github.com/kristianmandrup/gun-edge))**; **[Lightnet](https://github.com/Lightnet)** ([Awesome Vue User Examples](https://glitch.com/edit/#!/jsvuegunui?path=README.md:1:0) & [User Kitchen Sink Playground](https://gdb-auth-vue-node.glitch.me/)); **[lmangani](https://github.com/lmangani) ([Cytoscape Visualizer](https://github.com/lmangani/gun-scape), [Cassandra](https://github.com/lmangani/gun-cassandra), [Fastify](https://github.com/lmangani/fastify-gundb), [LetsEncrypt](https://github.com/lmangani/polyGun-letsencrypt))**; **[mhelander](https://github.com/mhelander) ([SEA](https://github.com/amark/gun/blob/master/sea.js))**; [omarzion](https://github.com/omarzion) ([Sticky Note App](https://github.com/omarzion/stickies)); [PsychoLlama](https://github.com/PsychoLlama) ([LevelDB](https://github.com/PsychoLlama/gun-level)); **[RangerMauve](https://github.com/RangerMauve) ([schema](https://github.com/gundb/gun-schema))**; **[robertheessels](https://github.com/swifty) ([gun-p2p-auth](https://github.com/swifty/gun-p2p-auth))**; [sbeleidy](https://github.com/sbeleidy); **[Sean Matheson](https://github.com/ctrlplusb) ([Observable/RxJS/Most.js bindings](https://github.com/ctrlplusb/gun-most))**; **[Stefdv](https://github.com/stefdv) (Polymer/web components)**; **[sjones6](https://github.com/sjones6) ([Flint](https://github.com/sjones6/gun-flint))**; **[zrrrzzt](https://github.com/zrrrzzt) ([JWT Auth](https://gist.github.com/zrrrzzt/6f88dc3cedee4ee18588236756d2cfce))**; **[88dev](https://github.com/88dev) ([Database Viewer](https://github.com/88dev/gun-show))**;
|
||||||
|
|
||||||
I am missing many others, apologies, will be adding them soon!
|
I am missing many others, apologies, will be adding them soon!
|
||||||
|
|
||||||
|
@ -73,7 +73,6 @@
|
|||||||
<script src="https://cdn.jsdelivr.net/npm/gun/examples/jquery.js"></script>
|
<script src="https://cdn.jsdelivr.net/npm/gun/examples/jquery.js"></script>
|
||||||
<script src="https://cdn.jsdelivr.net/npm/gun/gun.js"></script>
|
<script src="https://cdn.jsdelivr.net/npm/gun/gun.js"></script>
|
||||||
<script src="https://cdn.jsdelivr.net/npm/gun/sea.js"></script>
|
<script src="https://cdn.jsdelivr.net/npm/gun/sea.js"></script>
|
||||||
<script src="https://cdn.jsdelivr.net/npm/gun/lib/open.js"></script>
|
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
//var gun = Gun();
|
//var gun = Gun();
|
||||||
@ -104,7 +103,6 @@ gun.on('auth', function(){
|
|||||||
$('#profile input').on('keyup', function(e){
|
$('#profile input').on('keyup', function(e){
|
||||||
if(!user.is){ return }
|
if(!user.is){ return }
|
||||||
var id = LI.busy = $(this).attr('id');
|
var id = LI.busy = $(this).attr('id');
|
||||||
console.log("Huh?", id, user);
|
|
||||||
user.get('profile').get(id).secret($(this).val());
|
user.get('profile').get(id).secret($(this).val());
|
||||||
}).on('blur', function(){ LI.busy = false })
|
}).on('blur', function(){ LI.busy = false })
|
||||||
|
|
@ -11,16 +11,16 @@
|
|||||||
|
|
||||||
<form id="said">
|
<form id="said">
|
||||||
<input id="say">
|
<input id="say">
|
||||||
<input id="speak" type="button" value="speak">
|
<input id="speak" type="submit" value="speak">
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<script src="../../examples/jquery.js"></script>
|
<script src="../jquery.js"></script>
|
||||||
<script src="../../gun.js"></script>
|
<script src="../../../gun/gun.js"></script>
|
||||||
<script src="../../sea.js"></script>
|
<script src="../../../gun/sea.js"></script>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
var gun = Gun();
|
var gun = Gun(); //Gun(['http://localhost:8765/gun', 'https://guntest.herokuapp.com/gun']);
|
||||||
var user = gun.user();
|
var user = gun.user().recall({sessionStorage: true});
|
||||||
|
|
||||||
$('#up').on('click', function(e){
|
$('#up').on('click', function(e){
|
||||||
user.create($('#alias').val(), $('#pass').val());
|
user.create($('#alias').val(), $('#pass').val());
|
||||||
@ -33,7 +33,7 @@ $('#sign').on('submit', function(e){
|
|||||||
|
|
||||||
gun.on('auth', function(){
|
gun.on('auth', function(){
|
||||||
$('#sign').hide();
|
$('#sign').hide();
|
||||||
user.get('said').map().val(UI);
|
user.get('said').map().on(UI);
|
||||||
});
|
});
|
||||||
|
|
||||||
$('#said').on('submit', function(e){
|
$('#said').on('submit', function(e){
|
@ -1,20 +1,21 @@
|
|||||||
var fs = require('fs');
|
;(function(){
|
||||||
var config = {
|
var cluster = require('cluster');
|
||||||
port: process.env.OPENSHIFT_NODEJS_PORT || process.env.VCAP_APP_PORT || process.env.PORT || process.argv[2] || 8765
|
if(cluster.isMaster){
|
||||||
};
|
return cluster.fork() && cluster.on('exit', function(){ cluster.fork() });
|
||||||
var Gun = require('../'); // require('gun')
|
}
|
||||||
|
|
||||||
if(process.env.HTTPS_KEY){
|
var fs = require('fs');
|
||||||
|
var config = { port: process.env.OPENSHIFT_NODEJS_PORT || process.env.VCAP_APP_PORT || process.env.PORT || process.argv[2] || 8765 };
|
||||||
|
var Gun = require('../'); // require('gun')
|
||||||
|
|
||||||
|
if(process.env.HTTPS_KEY){
|
||||||
config.key = fs.readFileSync(process.env.HTTPS_KEY);
|
config.key = fs.readFileSync(process.env.HTTPS_KEY);
|
||||||
config.cert = fs.readFileSync(process.env.HTTPS_CERT);
|
config.cert = fs.readFileSync(process.env.HTTPS_CERT);
|
||||||
config.server = require('https').createServer(config, Gun.serve(__dirname));
|
config.server = require('https').createServer(config, Gun.serve(__dirname));
|
||||||
} else {
|
} else {
|
||||||
config.server = require('http').createServer(Gun.serve(__dirname));
|
config.server = require('http').createServer(Gun.serve(__dirname));
|
||||||
}
|
}
|
||||||
|
|
||||||
var gun = Gun({
|
var gun = Gun({web: config.server.listen(config.port) });
|
||||||
web: config.server
|
console.log('Relay peer started on port ' + config.port + ' with /gun');
|
||||||
});
|
}());
|
||||||
|
|
||||||
config.server.listen(config.port);
|
|
||||||
console.log('Server started on port ' + config.port + ' with /gun');
|
|
@ -668,25 +668,6 @@
|
|||||||
<p class="mid black">Hello world!</p>
|
<p class="mid black">Hello world!</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<style>
|
|
||||||
.joy {
|
|
||||||
width: 100px;
|
|
||||||
height: 100px;
|
|
||||||
position: absolute;
|
|
||||||
background: url(./pop.png) no-repeat;
|
|
||||||
background-position: -2800px 0;
|
|
||||||
pointer-events: none;
|
|
||||||
animation: joy 1s steps(28);
|
|
||||||
}
|
|
||||||
@keyframes joy {
|
|
||||||
0% {
|
|
||||||
background-position: 0 0;
|
|
||||||
}
|
|
||||||
100% {
|
|
||||||
background-position: -2800px 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
<script>
|
<script>
|
||||||
|
|
||||||
$.as.route.page('person', () => {
|
$.as.route.page('person', () => {
|
||||||
|
@ -330,3 +330,22 @@ ul, li {
|
|||||||
50% {opacity: 0.5;}
|
50% {opacity: 0.5;}
|
||||||
100% {opacity: 1;}
|
100% {opacity: 1;}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.joy {
|
||||||
|
width: 100px;
|
||||||
|
height: 100px;
|
||||||
|
position: absolute;
|
||||||
|
background: url(https://cdn.jsdelivr.net/npm/gun/examples/pop.png) no-repeat;
|
||||||
|
background-position: -2800px 0;
|
||||||
|
pointer-events: none;
|
||||||
|
animation: joy 1s steps(28);
|
||||||
|
}
|
||||||
|
@keyframes joy {
|
||||||
|
0% {
|
||||||
|
background-position: 0 0;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
background-position: -2800px 0;
|
||||||
|
}
|
||||||
|
}
|
45
gun.js
45
gun.js
@ -6,8 +6,8 @@
|
|||||||
if(typeof global !== "undefined"){ root = global }
|
if(typeof global !== "undefined"){ root = global }
|
||||||
root = root || {};
|
root = root || {};
|
||||||
var console = root.console || {log: function(){}};
|
var console = root.console || {log: function(){}};
|
||||||
function USE(arg){
|
function USE(arg, req){
|
||||||
return arg.slice? USE[R(arg)] : function(mod, path){
|
return req? require(arg) : arg.slice? USE[R(arg)] : function(mod, path){
|
||||||
arg(mod = {exports: {}});
|
arg(mod = {exports: {}});
|
||||||
USE[R(path)] = mod.exports;
|
USE[R(path)] = mod.exports;
|
||||||
}
|
}
|
||||||
@ -638,7 +638,7 @@
|
|||||||
dup.to = setTimeout(function(){
|
dup.to = setTimeout(function(){
|
||||||
var now = time_is();
|
var now = time_is();
|
||||||
Type.obj.map(dup.s, function(it, id){
|
Type.obj.map(dup.s, function(it, id){
|
||||||
if(opt.age > (now - it.was)){ return }
|
if(it && opt.age > (now - it.was)){ return }
|
||||||
Type.obj.del(dup.s, id);
|
Type.obj.del(dup.s, id);
|
||||||
});
|
});
|
||||||
dup.to = null;
|
dup.to = null;
|
||||||
@ -1661,7 +1661,8 @@
|
|||||||
data = tmp.put;
|
data = tmp.put;
|
||||||
}
|
}
|
||||||
if((tmp = eve.wait) && (tmp = tmp[at.id])){ clearTimeout(tmp) }
|
if((tmp = eve.wait) && (tmp = tmp[at.id])){ clearTimeout(tmp) }
|
||||||
if(!to && (u === data || at.soul || at.link || (link && !(0 < link.ack)))){
|
if((!to && (u === data || at.soul || at.link || (link && !(0 < link.ack))))
|
||||||
|
|| (u === data && (tmp = (obj_map(at.root.opt.peers, function(v,k,t){t(k)})||[]).length) && (link||at).ack <= tmp)){
|
||||||
tmp = (eve.wait = {})[at.id] = setTimeout(function(){
|
tmp = (eve.wait = {})[at.id] = setTimeout(function(){
|
||||||
val.call({as:opt}, msg, eve, tmp || 1);
|
val.call({as:opt}, msg, eve, tmp || 1);
|
||||||
}, opt.wait || 99);
|
}, opt.wait || 99);
|
||||||
@ -1901,7 +1902,7 @@
|
|||||||
if(data){ disk = data }
|
if(data){ disk = data }
|
||||||
try{store.setItem(opt.prefix, JSON.stringify(disk));
|
try{store.setItem(opt.prefix, JSON.stringify(disk));
|
||||||
}catch(e){
|
}catch(e){
|
||||||
Gun.log(err = e || "localStorage failure");
|
Gun.log(err = (e || "localStorage failure") + " Consider using GUN's IndexedDB plugin for RAD for more storage space, temporary example at https://github.com/amark/gun/blob/master/test/tmp/indexedDB.html .");
|
||||||
root.on('localStorage:error', {err: err, file: opt.prefix, flush: disk, retry: flush});
|
root.on('localStorage:error', {err: err, file: opt.prefix, flush: disk, retry: flush});
|
||||||
}
|
}
|
||||||
if(!err && !Gun.obj.empty(opt.peers)){ return } // only ack if there are no peers.
|
if(!err && !Gun.obj.empty(opt.peers)){ return } // only ack if there are no peers.
|
||||||
@ -1921,7 +1922,10 @@
|
|||||||
|
|
||||||
function Mesh(ctx){
|
function Mesh(ctx){
|
||||||
var mesh = function(){};
|
var mesh = function(){};
|
||||||
var opt = ctx.opt;
|
var opt = ctx.opt || {};
|
||||||
|
opt.log = opt.log || console.log;
|
||||||
|
opt.gap = opt.gap || opt.wait || 1;
|
||||||
|
opt.pack = opt.pack || (opt.memory? (opt.memory * 1000 * 1000) : 1399000000) * 0.3; // max_old_space_size defaults to 1400 MB.
|
||||||
|
|
||||||
mesh.out = function(msg){ var tmp;
|
mesh.out = function(msg){ var tmp;
|
||||||
if(this.to){ this.to.next(msg) }
|
if(this.to){ this.to.next(msg) }
|
||||||
@ -1947,8 +1951,9 @@
|
|||||||
mesh.hear = function(raw, peer){
|
mesh.hear = function(raw, peer){
|
||||||
if(!raw){ return }
|
if(!raw){ return }
|
||||||
var dup = ctx.dup, id, hash, msg, tmp = raw[0];
|
var dup = ctx.dup, id, hash, msg, tmp = raw[0];
|
||||||
|
if(opt.pack <= raw.length){ return mesh.say({dam: '!', err: "Message too big!"}, peer) }
|
||||||
try{msg = JSON.parse(raw);
|
try{msg = JSON.parse(raw);
|
||||||
}catch(e){console.log('DAM JSON parse error', e)}
|
}catch(e){opt.log('DAM JSON parse error', e)}
|
||||||
if('{' === tmp){
|
if('{' === tmp){
|
||||||
if(!msg){ return }
|
if(!msg){ return }
|
||||||
if(dup.check(id = msg['#'])){ return }
|
if(dup.check(id = msg['#'])){ return }
|
||||||
@ -2012,20 +2017,25 @@
|
|||||||
}
|
}
|
||||||
if((tmp = msh.to) && (tmp[peer.url] || tmp[peer.id]) && !o){ return } // TODO: still needs to be tested
|
if((tmp = msh.to) && (tmp[peer.url] || tmp[peer.id]) && !o){ return } // TODO: still needs to be tested
|
||||||
if(peer.batch){
|
if(peer.batch){
|
||||||
|
peer.tail = (peer.tail || 0) + raw.length;
|
||||||
|
if(peer.tail <= opt.pack){
|
||||||
peer.batch.push(raw);
|
peer.batch.push(raw);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
flush(peer);
|
||||||
|
}
|
||||||
peer.batch = [];
|
peer.batch = [];
|
||||||
setTimeout(function(){
|
setTimeout(function(){flush(peer)}, opt.gap);
|
||||||
var tmp = peer.batch;
|
|
||||||
if(!tmp){ return }
|
|
||||||
peer.batch = null;
|
|
||||||
if(!tmp.length){ return }
|
|
||||||
send(JSON.stringify(tmp), peer);
|
|
||||||
}, opt.gap || opt.wait || 1);
|
|
||||||
send(raw, peer);
|
send(raw, peer);
|
||||||
}
|
}
|
||||||
|
function flush(peer){
|
||||||
|
var tmp = peer.batch;
|
||||||
|
if(!tmp){ return }
|
||||||
|
peer.batch = peer.tail = null;
|
||||||
|
if(!tmp.length){ return }
|
||||||
|
try{send(JSON.stringify(tmp), peer);
|
||||||
|
}catch(e){opt.log('DAM JSON stringify error', e)}
|
||||||
|
}
|
||||||
function send(raw, peer){
|
function send(raw, peer){
|
||||||
var wire = peer.wire;
|
var wire = peer.wire;
|
||||||
try{
|
try{
|
||||||
@ -2111,6 +2121,7 @@
|
|||||||
ctx.on('bye', peer);
|
ctx.on('bye', peer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mesh.hear['!'] = function(msg, peer){ opt.log('Error:', msg.err) }
|
||||||
mesh.hear['?'] = function(msg, peer){
|
mesh.hear['?'] = function(msg, peer){
|
||||||
if(!msg.pid){ return mesh.say({dam: '?', pid: opt.pid, '@': msg['#']}, peer) }
|
if(!msg.pid){ return mesh.say({dam: '?', pid: opt.pid, '@': msg['#']}, peer) }
|
||||||
peer.id = peer.id || msg.pid;
|
peer.id = peer.id || msg.pid;
|
||||||
@ -2162,7 +2173,7 @@
|
|||||||
|
|
||||||
var wire = opt.wire;
|
var wire = opt.wire;
|
||||||
opt.wire = open;
|
opt.wire = open;
|
||||||
function open(peer){
|
function open(peer){ try{
|
||||||
if(!peer || !peer.url){ return wire && wire(peer) }
|
if(!peer || !peer.url){ return wire && wire(peer) }
|
||||||
var url = peer.url.replace('http', 'ws');
|
var url = peer.url.replace('http', 'ws');
|
||||||
var wire = peer.wire = new opt.WebSocket(url);
|
var wire = peer.wire = new opt.WebSocket(url);
|
||||||
@ -2185,7 +2196,7 @@
|
|||||||
opt.mesh.hear(msg.data || msg, peer);
|
opt.mesh.hear(msg.data || msg, peer);
|
||||||
};
|
};
|
||||||
return wire;
|
return wire;
|
||||||
}
|
}catch(e){}}
|
||||||
|
|
||||||
function reconnect(peer){
|
function reconnect(peer){
|
||||||
clearTimeout(peer.defer);
|
clearTimeout(peer.defer);
|
||||||
|
4
gun.min.js
vendored
4
gun.min.js
vendored
File diff suppressed because one or more lines are too long
@ -8,7 +8,7 @@
|
|||||||
var util = process.memoryUsage;
|
var util = process.memoryUsage;
|
||||||
if(!util){ return }
|
if(!util){ return }
|
||||||
|
|
||||||
ev.max = parseFloat(root.opt.memory || process.env.WEB_MEMORY || 512) * 0.8;
|
ev.max = parseFloat(root.opt.memory || process.env.WEB_MEMORY || 1399) * 0.8; // max_old_space_size defaults to 1400 MB. Note: old space !== memory space though.
|
||||||
|
|
||||||
setInterval(check, 1000);
|
setInterval(check, 1000);
|
||||||
function check(){
|
function check(){
|
||||||
|
121
lib/les.js
Normal file
121
lib/les.js
Normal file
@ -0,0 +1,121 @@
|
|||||||
|
;
|
||||||
|
(function() {
|
||||||
|
|
||||||
|
// _ _____ ____ _
|
||||||
|
// | | | ____/ ___| (_)___
|
||||||
|
// | | | _| \___ \ | / __|
|
||||||
|
// | |___| |___ ___) | | \__ \
|
||||||
|
// |_____|_____|____(_)/ |___/
|
||||||
|
// ----------------------------
|
||||||
|
// LES.js (Last rEcently uSed)
|
||||||
|
// ----------------------------
|
||||||
|
// A Small, lightweight, queue-based
|
||||||
|
// Garbage Collector for Gun
|
||||||
|
// Originally By: Collin Conrad (@masterex1000)
|
||||||
|
|
||||||
|
//NOTE: set to false is running from file in YOUR code
|
||||||
|
var USELOCALGUN = true;
|
||||||
|
|
||||||
|
//NOTE: adds some debug messages
|
||||||
|
var DEBUG = false;
|
||||||
|
|
||||||
|
|
||||||
|
var Gun = (typeof window !== "undefined") ? window.Gun : (USELOCALGUN ? require('../gun') : require("gun"));
|
||||||
|
var ev = {};
|
||||||
|
var empty = {};
|
||||||
|
|
||||||
|
Gun.on('opt', function(root) {
|
||||||
|
this.to.next(root);
|
||||||
|
if (root.once)
|
||||||
|
return;
|
||||||
|
if (typeof process == 'undefined')
|
||||||
|
return
|
||||||
|
var mem = process.memoryUsage;
|
||||||
|
|
||||||
|
if (!mem) //exit because we are in the browser
|
||||||
|
return;
|
||||||
|
|
||||||
|
//Figure out the most amount of memory we can use. TODO: make configurable?
|
||||||
|
ev.max = parseFloat(root.opt.memory || process.env.WEB_MEMORY || 512) * 0.8;
|
||||||
|
|
||||||
|
var nodes = {}; //checks if the node already exists
|
||||||
|
var nodesArray = []; //used to easily sort everything and store info about the nodes
|
||||||
|
var memoryUpdate = 0; // last time we printed the current memory stats
|
||||||
|
|
||||||
|
var check = function() {
|
||||||
|
ev.used = mem().rss / 1024 / 1024; //Contains the amt. of used ram in MB
|
||||||
|
setTimeout(function() { // So we can handle requests etc. before we start collecting
|
||||||
|
GC(ev.used / ev.max); // Calculate the memory ratio, and execute the garbage collector
|
||||||
|
}, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
setInterval(check, 1000); // set the garbage collector to run every second, TODO: make configurable
|
||||||
|
|
||||||
|
//Executed every time a node gets modifyed
|
||||||
|
root.on("put", function(e) {
|
||||||
|
var ctime = Date.now();
|
||||||
|
var souls = Object.keys(e.put || empty);
|
||||||
|
for (var i = 0; i < souls.length; i++) {
|
||||||
|
enqueueNode(souls[i], ctime);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
//Adds a soul the garbage collectors "freeing" queue
|
||||||
|
function enqueueNode(soul, ctime) {
|
||||||
|
if (nodes[soul] == true) { //The node already exists in the queue
|
||||||
|
var index = nodesArray.findIndex(function(e) {
|
||||||
|
return e[0] === soul;
|
||||||
|
});
|
||||||
|
if (index == -1) {
|
||||||
|
console.err("Something happened and the node '" + soul + "' won't get garbage collection unless the value is updated agian");
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
nodesArray.splice(index, 1); // remove the existing ref.
|
||||||
|
nodesArray.push([soul, ctime]); // push the new instance
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
nodesArray.push([soul, ctime]);
|
||||||
|
nodes[soul] = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//The main garbage collecting routine
|
||||||
|
function GC(memRatio) {
|
||||||
|
var curTime = Date.now(); // get the current time
|
||||||
|
|
||||||
|
if (curTime - memoryUpdate >= 5000) {
|
||||||
|
console.log("|GC| %s | Current Memory Ratio: %d | Current Ram Usage %sMB | Nodes in Memory %s", new Date().toLocaleString(), round(memRatio, 2), round(ev.used, 2), Object.keys(root.graph || empty).length);
|
||||||
|
memoryUpdate = curTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
var freed = 0;
|
||||||
|
|
||||||
|
while (nodesArray.length > 0) {
|
||||||
|
var soul = nodesArray[0][0];
|
||||||
|
var nts = nodesArray[0][1];
|
||||||
|
if (DEBUG)
|
||||||
|
console.log("Soul: " + soul + " | Remove Importance: " + calcRemoveImportance(nts, curTime, memRatio) +
|
||||||
|
" | Memory Ratio: " + memRatio + " | Time Existed: " + (curTime - nts) / 1000);
|
||||||
|
if (calcRemoveImportance(nodesArray[0][1], curTime, memRatio) >= 100) {
|
||||||
|
root.gun.get(nodesArray[0][0]).off(); //Remove the node
|
||||||
|
delete nodes[nodesArray[0][0]]; // remove the lookup value
|
||||||
|
nodesArray.splice(0, 1);
|
||||||
|
freed++;
|
||||||
|
} else
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (freed > 0)
|
||||||
|
console.log("|GC| Removed %s nodes in %s seconds-----------------------------------------------------------------", freed, (Date.now() - curTime) * 0.001);
|
||||||
|
}
|
||||||
|
|
||||||
|
//Generates a number that, after it hits a threshold, the node gets removed
|
||||||
|
function calcRemoveImportance(timestamp, ctime, memoryUsageRatio) {
|
||||||
|
var time = (ctime - timestamp) * 0.001;
|
||||||
|
return time * 10 * (memoryUsageRatio * memoryUsageRatio)
|
||||||
|
}
|
||||||
|
|
||||||
|
function round(value, decimals) { //a basic rounding function
|
||||||
|
return Number(Math.round(value + 'e' + decimals) + 'e-' + decimals);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}());
|
@ -3,7 +3,9 @@
|
|||||||
function Radisk(opt){
|
function Radisk(opt){
|
||||||
|
|
||||||
opt = opt || {};
|
opt = opt || {};
|
||||||
|
opt.log = opt.log || console.log;
|
||||||
opt.file = String(opt.file || 'radata');
|
opt.file = String(opt.file || 'radata');
|
||||||
|
opt.pack = opt.pack || (opt.memory? (opt.memory * 1000 * 1000) : 1399000000) * 0.3; // max_old_space_size defaults to 1400 MB.
|
||||||
opt.until = opt.until || opt.wait || 9;
|
opt.until = opt.until || opt.wait || 9;
|
||||||
opt.batch = opt.batch || 10 * 1000;
|
opt.batch = opt.batch || 10 * 1000;
|
||||||
opt.chunk = opt.chunk || (1024 * 1024 * 10); // 10MB
|
opt.chunk = opt.chunk || (1024 * 1024 * 10); // 10MB
|
||||||
@ -14,16 +16,16 @@
|
|||||||
var map = Gun.obj.map;
|
var map = Gun.obj.map;
|
||||||
|
|
||||||
if(!opt.store){
|
if(!opt.store){
|
||||||
return Gun.log("ERROR: Radisk needs `opt.store` interface with `{get: fn, put: fn (, list: fn)}`!");
|
return opt.log("ERROR: Radisk needs `opt.store` interface with `{get: fn, put: fn (, list: fn)}`!");
|
||||||
}
|
}
|
||||||
if(!opt.store.put){
|
if(!opt.store.put){
|
||||||
return Gun.log("ERROR: Radisk needs `store.put` interface with `(file, data, cb)`!");
|
return opt.log("ERROR: Radisk needs `store.put` interface with `(file, data, cb)`!");
|
||||||
}
|
}
|
||||||
if(!opt.store.get){
|
if(!opt.store.get){
|
||||||
return Gun.log("ERROR: Radisk needs `store.get` interface with `(file, cb)`!");
|
return opt.log("ERROR: Radisk needs `store.get` interface with `(file, cb)`!");
|
||||||
}
|
}
|
||||||
if(!opt.store.list){
|
if(!opt.store.list){
|
||||||
Gun.log("WARNING: `store.list` interface might be needed!");
|
//opt.log("WARNING: `store.list` interface might be needed!");
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -73,7 +75,7 @@
|
|||||||
r.batch.ed = 0;
|
r.batch.ed = 0;
|
||||||
r.save(batch, function(err, ok){
|
r.save(batch, function(err, ok){
|
||||||
if(++i > 1){ return }
|
if(++i > 1){ return }
|
||||||
if(err){ Gun.log('err', err) }
|
if(err){ opt.log('err', err) }
|
||||||
map(batch.acks, function(cb){ cb(err, ok) });
|
map(batch.acks, function(cb){ cb(err, ok) });
|
||||||
thrash.at = null;
|
thrash.at = null;
|
||||||
thrash.ing = false;
|
thrash.ing = false;
|
||||||
@ -141,6 +143,7 @@
|
|||||||
f.file = file;
|
f.file = file;
|
||||||
f.each = function(val, key, k, pre){
|
f.each = function(val, key, k, pre){
|
||||||
if(u !== val){ f.count++ }
|
if(u !== val){ f.count++ }
|
||||||
|
if(opt.pack <= (val||'').length){ return cb("Record too big!"), true }
|
||||||
var enc = Radisk.encode(pre.length) +'#'+ Radisk.encode(k) + (u === val? '' : '='+ Radisk.encode(val)) +'\n';
|
var enc = Radisk.encode(pre.length) +'#'+ Radisk.encode(k) + (u === val? '' : '='+ Radisk.encode(val)) +'\n';
|
||||||
if((opt.chunk < f.text.length + enc.length) && (1 < f.count) && !force){
|
if((opt.chunk < f.text.length + enc.length) && (1 < f.count) && !force){
|
||||||
f.text = '';
|
f.text = '';
|
||||||
@ -206,7 +209,7 @@
|
|||||||
g.file = file;
|
g.file = file;
|
||||||
}
|
}
|
||||||
g.it = function(err, disk){
|
g.it = function(err, disk){
|
||||||
if(g.err = err){ Gun.log('err', err) }
|
if(g.err = err){ opt.log('err', err) }
|
||||||
if(disk){ RAD = g.disk = disk }
|
if(disk){ RAD = g.disk = disk }
|
||||||
disk = Q[g.file]; delete Q[g.file];
|
disk = Q[g.file]; delete Q[g.file];
|
||||||
map(disk, g.ack);
|
map(disk, g.ack);
|
||||||
@ -243,6 +246,16 @@
|
|||||||
//return cb(err, u);//map(q, p.ack);
|
//return cb(err, u);//map(q, p.ack);
|
||||||
return map(q, p.ack);
|
return map(q, p.ack);
|
||||||
}
|
}
|
||||||
|
if(typeof data !== 'string'){
|
||||||
|
try{
|
||||||
|
if(opt.pack <= data.length){
|
||||||
|
p.err = "Chunk too big!";
|
||||||
|
} else {
|
||||||
|
data = data.toString();
|
||||||
|
}
|
||||||
|
}catch(e){ p.err = e }
|
||||||
|
if(p.err){ return map(q, p.ack) }
|
||||||
|
}
|
||||||
var tmp = p.split(data), pre = [], i, k, v;
|
var tmp = p.split(data), pre = [], i, k, v;
|
||||||
while(tmp){
|
while(tmp){
|
||||||
k = v = u;
|
k = v = u;
|
||||||
@ -309,7 +322,7 @@
|
|||||||
}
|
}
|
||||||
r.list.init = function(err, disk){
|
r.list.init = function(err, disk){
|
||||||
if(err){
|
if(err){
|
||||||
Gun.log('list', err);
|
opt.log('list', err);
|
||||||
setTimeout(function(){ r.parse(f, r.list.init) }, 1000);
|
setTimeout(function(){ r.parse(f, r.list.init) }, 1000);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -22,7 +22,6 @@ function Store(opt){
|
|||||||
}
|
}
|
||||||
Gun.log("ERROR:", err)
|
Gun.log("ERROR:", err)
|
||||||
}
|
}
|
||||||
if(data){ data = data.toString() }
|
|
||||||
cb(err, data);
|
cb(err, data);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
@ -62,7 +62,7 @@ function Store(opt){
|
|||||||
if(!ack){ cb(null); return; }
|
if(!ack){ cb(null); return; }
|
||||||
cb(err, data);
|
cb(err, data);
|
||||||
};
|
};
|
||||||
if(data = (ack||{}).Body){ data = data.toString() }
|
data = (ack||{}).Body; //if(data = (ack||{}).Body){ data = data.toString() }
|
||||||
Gun.obj.map(cbs, cbe);
|
Gun.obj.map(cbs, cbe);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
@ -4,9 +4,10 @@ var path = require('path');
|
|||||||
function CDN(dir){
|
function CDN(dir){
|
||||||
return function(req, res){
|
return function(req, res){
|
||||||
if(serve(req, res)){ return } // filters GUN requests!
|
if(serve(req, res)){ return } // filters GUN requests!
|
||||||
fs.createReadStream(path.join(dir, req.url)).on('error',function(){ // static files!
|
fs.createReadStream(path.join(dir, req.url)).on('error',function(tmp){ // static files!
|
||||||
|
try{ tmp = fs.readFileSync(path.join(dir, 'index.html')) }catch(e){}
|
||||||
res.writeHead(200, {'Content-Type': 'text/html'});
|
res.writeHead(200, {'Content-Type': 'text/html'});
|
||||||
res.end(fs.readFileSync(path.join(dir, 'index.html'))); // or default to index
|
res.end(tmp+''); // or default to index
|
||||||
}).pipe(res); // stream
|
}).pipe(res); // stream
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -41,7 +41,7 @@ Gun.on('create', function(root){
|
|||||||
var id = msg['#'], soul = msg.get['#'], key = msg.get['.']||'', tmp = soul+'.'+key, node;
|
var id = msg['#'], soul = msg.get['#'], key = msg.get['.']||'', tmp = soul+'.'+key, node;
|
||||||
rad(tmp, function(err, val){
|
rad(tmp, function(err, val){
|
||||||
if(val){
|
if(val){
|
||||||
Radix.map(val, each);
|
if(val && typeof val !== 'string'){ Radix.map(val, each) }
|
||||||
if(!node){ each(val, key) }
|
if(!node){ each(val, key) }
|
||||||
}
|
}
|
||||||
root.on('in', {'@': id, put: Gun.graph.node(node), err: err? err : u, rad: Radix});
|
root.on('in', {'@': id, put: Gun.graph.node(node), err: err? err : u, rad: Radix});
|
||||||
|
@ -65,6 +65,7 @@ Gun.on('opt', function(root){
|
|||||||
|
|
||||||
opt.mesh = opt.mesh || Gun.Mesh(root);
|
opt.mesh = opt.mesh || Gun.Mesh(root);
|
||||||
ws.path = ws.path || '/gun';
|
ws.path = ws.path || '/gun';
|
||||||
|
ws.maxPayload = ws.maxPayload; // || opt.pack || (opt.memory? (opt.memory * 1000 * 1000) : 1399000000) * 0.3;
|
||||||
ws.web = new opt.WebSocket.Server(ws);
|
ws.web = new opt.WebSocket.Server(ws);
|
||||||
ws.web.on('connection', function(wire){ var peer;
|
ws.web.on('connection', function(wire){ var peer;
|
||||||
wire.upgradeReq = wire.upgradeReq || {};
|
wire.upgradeReq = wire.upgradeReq || {};
|
||||||
|
47
package-lock.json
generated
47
package-lock.json
generated
@ -1,28 +1,29 @@
|
|||||||
{
|
{
|
||||||
"name": "gun",
|
"name": "gun",
|
||||||
"version": "0.9.99992",
|
"version": "0.9.99995",
|
||||||
"lockfileVersion": 1,
|
"lockfileVersion": 1,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@trust/keyto": {
|
"@trust/keyto": {
|
||||||
"version": "0.3.2",
|
"version": "0.3.4",
|
||||||
"resolved": "https://registry.npmjs.org/@trust/keyto/-/keyto-0.3.2.tgz",
|
"resolved": "https://registry.npmjs.org/@trust/keyto/-/keyto-0.3.4.tgz",
|
||||||
"integrity": "sha512-ywlelg2ePNpX4IlN+A3qXySzKBAZmI2ZxMdDL3amJLCTYhYhemYcv6Aa+PTETojUfB+k4z4X4970q/jjSzyLvw==",
|
"integrity": "sha512-OAqKvuSEPIu2zCnIHzBthvGnV8nKmpv7cBlRMngLzJZzZI9CanyuSfnEI1xC4sH4TwqA0XJR7Mb0oX4bwymXIw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"asn1.js": "^4.9.1",
|
"asn1.js": "^4.9.1",
|
||||||
"base64url": "^2.0.0",
|
"base64url": "^3.0.0",
|
||||||
"elliptic": "^6.4.0"
|
"elliptic": "^6.4.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@trust/webcrypto": {
|
"@trust/webcrypto": {
|
||||||
"version": "0.7.1",
|
"version": "0.9.2",
|
||||||
"resolved": "https://registry.npmjs.org/@trust/webcrypto/-/webcrypto-0.7.1.tgz",
|
"resolved": "https://registry.npmjs.org/@trust/webcrypto/-/webcrypto-0.9.2.tgz",
|
||||||
"integrity": "sha512-aix+LOG/3Ku3MzClfVxVH88QbSdIL1HcBQ+gjXL/VnX05uyORf28CaQZOvsoEcCzGnWIVBUNwE2gxLBapWANWw==",
|
"integrity": "sha512-5iMAVcGYKhqLJGjefB1nzuQSqUJTru0nG4CytpBT/GGp1Piz/MVnj2jORdYf4JBYzggCIa8WZUr2rchP2Ngn/w==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"@trust/keyto": "^0.3.1",
|
"@trust/keyto": "^0.3.4",
|
||||||
"base64url": "^2.0.0",
|
"base64url": "^3.0.0",
|
||||||
|
"elliptic": "^6.4.0",
|
||||||
"node-rsa": "^0.4.0",
|
"node-rsa": "^0.4.0",
|
||||||
"text-encoding": "^0.6.1"
|
"text-encoding": "^0.6.1"
|
||||||
}
|
}
|
||||||
@ -126,9 +127,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"base64url": {
|
"base64url": {
|
||||||
"version": "2.0.0",
|
"version": "3.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/base64url/-/base64url-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/base64url/-/base64url-3.0.0.tgz",
|
||||||
"integrity": "sha1-6sFuA+oUOO/5Qj1puqNiYu0fcLs=",
|
"integrity": "sha512-LIVmqIrIWuiqTvn4RzcrwCOuHo2DD6tKmKBPXXlr4p4n4l6BZBkwFTIa3zu1XkX5MbZgro4a6BvPi+n2Mns5Gg==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"better-assert": {
|
"better-assert": {
|
||||||
@ -309,9 +310,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"elliptic": {
|
"elliptic": {
|
||||||
"version": "6.4.0",
|
"version": "6.4.1",
|
||||||
"resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.4.1.tgz",
|
||||||
"integrity": "sha1-ysmvh2LIWDYYcAPI3+GT5eLq5d8=",
|
"integrity": "sha512-BsXLz5sqX8OHcsh7CqBMztyXARmGQ3LWPtGjJi6DiJHq5C/qvi9P3OqgswKSDftbu8+IoI/QDTAm2fFnQ9SZSQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"bn.js": "^4.4.0",
|
"bn.js": "^4.4.0",
|
||||||
@ -606,13 +607,13 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"hash.js": {
|
"hash.js": {
|
||||||
"version": "1.1.3",
|
"version": "1.1.5",
|
||||||
"resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.5.tgz",
|
||||||
"integrity": "sha512-/UETyP0W22QILqS+6HowevwhEFJ3MBJnwTf75Qob9Wz9t0DPuisL8kW8YZMK62dHAKE1c1p+gY1TtOLY+USEHA==",
|
"integrity": "sha512-eWI5HG9Np+eHV1KQhisXWwM+4EPPYe5dFX1UZZH7k/E3JzDEazVH+VGlZi6R94ZqImq+A3D1mCEtrFIfg/E7sA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"inherits": "^2.0.3",
|
"inherits": "^2.0.3",
|
||||||
"minimalistic-assert": "^1.0.0"
|
"minimalistic-assert": "^1.0.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"he": {
|
"he": {
|
||||||
@ -853,9 +854,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node-webcrypto-ossl": {
|
"node-webcrypto-ossl": {
|
||||||
"version": "1.0.37",
|
"version": "1.0.38",
|
||||||
"resolved": "https://registry.npmjs.org/node-webcrypto-ossl/-/node-webcrypto-ossl-1.0.37.tgz",
|
"resolved": "https://registry.npmjs.org/node-webcrypto-ossl/-/node-webcrypto-ossl-1.0.38.tgz",
|
||||||
"integrity": "sha512-AQSux10u8NoUhRPqb2bapqM8EKMawKGYIBbGNWsBUTeq0uXYtuIheujcaJo5XY1Yy6HffC/fP7AHHtSA4KP2ig==",
|
"integrity": "sha512-UiQcDiBDNzaZbP0WVgz4QvVTVI4uR4jrFAtOtFsKbDDNOMFWc9+3mVeiF1hVvdLlv3ILC0ODgs8Wp/hp7SMoLA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"mkdirp": "^0.5.1",
|
"mkdirp": "^0.5.1",
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "gun",
|
"name": "gun",
|
||||||
"version": "0.9.99993",
|
"version": "0.9.99995",
|
||||||
"description": "A realtime, decentralized, offline-first, graph data synchronization engine.",
|
"description": "A realtime, decentralized, offline-first, graph data synchronization engine.",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"browser": "gun.min.js",
|
"browser": "gun.min.js",
|
||||||
@ -51,13 +51,13 @@
|
|||||||
"ws": "~>5.2.0"
|
"ws": "~>5.2.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@trust/webcrypto": "^0.7.1",
|
"@trust/webcrypto": "^0.9.2",
|
||||||
"node-webcrypto-ossl": "^1.0.37",
|
|
||||||
"aws-sdk": ">=2.153.0",
|
"aws-sdk": ">=2.153.0",
|
||||||
|
"concat-map": "^0.0.1",
|
||||||
"express": ">=4.15.2",
|
"express": ">=4.15.2",
|
||||||
"ip": "^1.1.5",
|
"ip": "^1.1.5",
|
||||||
"concat-map": "^0.0.1",
|
|
||||||
"mocha": ">=3.2.0",
|
"mocha": ">=3.2.0",
|
||||||
|
"node-webcrypto-ossl": "^1.0.38",
|
||||||
"panic-manager": "^1.2.0",
|
"panic-manager": "^1.2.0",
|
||||||
"panic-server": "^1.1.1",
|
"panic-server": "^1.1.1",
|
||||||
"text-encoding": "^0.6.4",
|
"text-encoding": "^0.6.4",
|
||||||
|
190
sea.js
190
sea.js
@ -6,8 +6,8 @@
|
|||||||
if(typeof global !== "undefined"){ root = global }
|
if(typeof global !== "undefined"){ root = global }
|
||||||
root = root || {};
|
root = root || {};
|
||||||
var console = root.console || {log: function(){}};
|
var console = root.console || {log: function(){}};
|
||||||
function USE(arg){
|
function USE(arg, req){
|
||||||
return arg.slice? USE[R(arg)] : function(mod, path){
|
return req? require(arg) : arg.slice? USE[R(arg)] : function(mod, path){
|
||||||
arg(mod = {exports: {}});
|
arg(mod = {exports: {}});
|
||||||
USE[R(path)] = mod.exports;
|
USE[R(path)] = mod.exports;
|
||||||
}
|
}
|
||||||
@ -20,12 +20,24 @@
|
|||||||
|
|
||||||
;USE(function(module){
|
;USE(function(module){
|
||||||
// Security, Encryption, and Authorization: SEA.js
|
// Security, Encryption, and Authorization: SEA.js
|
||||||
// MANDATORY READING: http://gun.js.org/explainers/data/security.html
|
// MANDATORY READING: https://gun.eco/explainers/data/security.html
|
||||||
|
// IT IS IMPLEMENTED IN A POLYFILL/SHIM APPROACH.
|
||||||
// THIS IS AN EARLY ALPHA!
|
// THIS IS AN EARLY ALPHA!
|
||||||
|
|
||||||
function SEA(){}
|
if(typeof window !== "undefined"){ module.window = window }
|
||||||
if(typeof window !== "undefined"){ (SEA.window = window).SEA = SEA }
|
|
||||||
|
|
||||||
|
var tmp = module.window || module;
|
||||||
|
var SEA = tmp.SEA || function(){};
|
||||||
|
|
||||||
|
if(SEA.window = module.window){ try{
|
||||||
|
SEA.window.SEA = SEA;
|
||||||
|
tmp = document.createEvent('CustomEvent');
|
||||||
|
tmp.initCustomEvent('extension', false, false, {type: "SEA"});
|
||||||
|
(window.dispatchEvent || window.fireEvent)(tmp);
|
||||||
|
window.postMessage({type: "SEA"}, '*');
|
||||||
|
} catch(e){} }
|
||||||
|
|
||||||
|
try{ if(typeof common !== "undefined"){ common.exports = SEA } }catch(e){}
|
||||||
module.exports = SEA;
|
module.exports = SEA;
|
||||||
})(USE, './root');
|
})(USE, './root');
|
||||||
|
|
||||||
@ -148,7 +160,7 @@
|
|||||||
const Buffer = USE('./buffer')
|
const Buffer = USE('./buffer')
|
||||||
const api = {Buffer: Buffer}
|
const api = {Buffer: Buffer}
|
||||||
|
|
||||||
if (typeof __webpack_require__ === 'function' || typeof window !== 'undefined') {
|
if (typeof window !== 'undefined') {
|
||||||
var crypto = window.crypto || window.msCrypto;
|
var crypto = window.crypto || window.msCrypto;
|
||||||
var subtle = crypto.subtle || crypto.webkitSubtle;
|
var subtle = crypto.subtle || crypto.webkitSubtle;
|
||||||
const TextEncoder = window.TextEncoder
|
const TextEncoder = window.TextEncoder
|
||||||
@ -162,9 +174,9 @@
|
|||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
try{
|
try{
|
||||||
var crypto = require('crypto');
|
var crypto = USE('crypto', 1);
|
||||||
const { subtle } = require('@trust/webcrypto') // All but ECDH
|
const { subtle } = USE('@trust/webcrypto', 1) // All but ECDH
|
||||||
const { TextEncoder, TextDecoder } = require('text-encoding')
|
const { TextEncoder, TextDecoder } = USE('text-encoding', 1)
|
||||||
Object.assign(api, {
|
Object.assign(api, {
|
||||||
crypto,
|
crypto,
|
||||||
subtle,
|
subtle,
|
||||||
@ -173,7 +185,7 @@
|
|||||||
random: (len) => Buffer.from(crypto.randomBytes(len))
|
random: (len) => Buffer.from(crypto.randomBytes(len))
|
||||||
});
|
});
|
||||||
//try{
|
//try{
|
||||||
const WebCrypto = require('node-webcrypto-ossl')
|
const WebCrypto = USE('node-webcrypto-ossl', 1)
|
||||||
api.ossl = new WebCrypto({directory: 'ossl'}).subtle // ECDH
|
api.ossl = new WebCrypto({directory: 'ossl'}).subtle // ECDH
|
||||||
//}catch(e){
|
//}catch(e){
|
||||||
//console.log("node-webcrypto-ossl is optionally needed for ECDH, please install if needed.");
|
//console.log("node-webcrypto-ossl is optionally needed for ECDH, please install if needed.");
|
||||||
@ -189,6 +201,7 @@
|
|||||||
})(USE, './shim');
|
})(USE, './shim');
|
||||||
|
|
||||||
;USE(function(module){
|
;USE(function(module){
|
||||||
|
const SEA = USE('./root');
|
||||||
const Buffer = USE('./buffer')
|
const Buffer = USE('./buffer')
|
||||||
const settings = {}
|
const settings = {}
|
||||||
// Encryption parameters
|
// Encryption parameters
|
||||||
@ -225,6 +238,7 @@
|
|||||||
jwk: keysToEcdsaJwk,
|
jwk: keysToEcdsaJwk,
|
||||||
recall: authsettings
|
recall: authsettings
|
||||||
})
|
})
|
||||||
|
SEA.opt = settings;
|
||||||
module.exports = settings
|
module.exports = settings
|
||||||
})(USE, './settings');
|
})(USE, './settings');
|
||||||
|
|
||||||
@ -267,49 +281,40 @@
|
|||||||
var SEA = USE('./root');
|
var SEA = USE('./root');
|
||||||
var shim = USE('./shim');
|
var shim = USE('./shim');
|
||||||
var S = USE('./settings');
|
var S = USE('./settings');
|
||||||
|
var sha = USE('./sha256');
|
||||||
var u;
|
var u;
|
||||||
|
|
||||||
SEA.work = async (data, pair, cb) => { try { // used to be named `proof`
|
SEA.work = SEA.work || (async (data, pair, cb, opt) => { try { // used to be named `proof`
|
||||||
var salt = pair.epub || pair; // epub not recommended, salt should be random!
|
var salt = (pair||{}).epub || pair; // epub not recommended, salt should be random!
|
||||||
|
var opt = opt || {};
|
||||||
if(salt instanceof Function){
|
if(salt instanceof Function){
|
||||||
cb = salt;
|
cb = salt;
|
||||||
salt = u;
|
salt = u;
|
||||||
}
|
}
|
||||||
salt = salt || shim.random(9);
|
salt = salt || shim.random(9);
|
||||||
if (SEA.window) {
|
if('SHA-256' === opt.name){
|
||||||
// For browser subtle works fine
|
var rsha = shim.Buffer.from(await sha(data), 'binary').toString('utf8')
|
||||||
const key = await shim.subtle.importKey(
|
if(cb){ try{ cb(rsha) }catch(e){console.log(e)} }
|
||||||
'raw', new shim.TextEncoder().encode(data), { name: 'PBKDF2' }, false, ['deriveBits']
|
return rsha;
|
||||||
|
}
|
||||||
|
const key = await (shim.ossl || shim.subtle).importKey(
|
||||||
|
'raw', new shim.TextEncoder().encode(data), { name: opt.name || 'PBKDF2' }, false, ['deriveBits']
|
||||||
)
|
)
|
||||||
const result = await shim.subtle.deriveBits({
|
const result = await (shim.ossl || shim.subtle).deriveBits({
|
||||||
name: 'PBKDF2',
|
name: opt.name || 'PBKDF2',
|
||||||
iterations: S.pbkdf2.iter,
|
iterations: opt.iterations || S.pbkdf2.iter,
|
||||||
salt: new shim.TextEncoder().encode(salt),
|
salt: new shim.TextEncoder().encode(opt.salt || salt),
|
||||||
hash: S.pbkdf2.hash,
|
hash: opt.hash || S.pbkdf2.hash,
|
||||||
}, key, S.pbkdf2.ks * 8)
|
}, key, opt.length || (S.pbkdf2.ks * 8))
|
||||||
data = shim.random(data.length) // Erase data in case of passphrase
|
data = shim.random(data.length) // Erase data in case of passphrase
|
||||||
const r = shim.Buffer.from(result, 'binary').toString('utf8')
|
const r = shim.Buffer.from(result, 'binary').toString('utf8')
|
||||||
if(cb){ try{ cb(r) }catch(e){console.log(e)} }
|
if(cb){ try{ cb(r) }catch(e){console.log(e)} }
|
||||||
return r;
|
return r;
|
||||||
}
|
|
||||||
// For NodeJS crypto.pkdf2 rocks
|
|
||||||
const crypto = shim.crypto;
|
|
||||||
const hash = crypto.pbkdf2Sync(
|
|
||||||
data,
|
|
||||||
new shim.TextEncoder().encode(salt),
|
|
||||||
S.pbkdf2.iter,
|
|
||||||
S.pbkdf2.ks,
|
|
||||||
S.pbkdf2.hash.replace('-', '').toLowerCase()
|
|
||||||
)
|
|
||||||
data = shim.random(data.length) // Erase passphrase for app
|
|
||||||
const r = hash && hash.toString('utf8')
|
|
||||||
if(cb){ try{ cb(r) }catch(e){console.log(e)} }
|
|
||||||
return r;
|
|
||||||
} catch(e) {
|
} catch(e) {
|
||||||
SEA.err = e;
|
SEA.err = e;
|
||||||
if(cb){ cb() }
|
if(cb){ cb() }
|
||||||
return;
|
return;
|
||||||
}}
|
}});
|
||||||
|
|
||||||
module.exports = SEA.work;
|
module.exports = SEA.work;
|
||||||
})(USE, './work');
|
})(USE, './work');
|
||||||
@ -321,7 +326,7 @@
|
|||||||
var Buff = (typeof Buffer !== 'undefined')? Buffer : shim.Buffer;
|
var Buff = (typeof Buffer !== 'undefined')? Buffer : shim.Buffer;
|
||||||
|
|
||||||
//SEA.pair = async (data, proof, cb) => { try {
|
//SEA.pair = async (data, proof, cb) => { try {
|
||||||
SEA.pair = async (cb) => { try {
|
SEA.pair = SEA.pair || (async (cb) => { try {
|
||||||
|
|
||||||
const ecdhSubtle = shim.ossl || shim.subtle
|
const ecdhSubtle = shim.ossl || shim.subtle
|
||||||
// First: ECDSA keys for signing/verifying...
|
// First: ECDSA keys for signing/verifying...
|
||||||
@ -372,7 +377,7 @@
|
|||||||
SEA.err = e;
|
SEA.err = e;
|
||||||
if(cb){ cb() }
|
if(cb){ cb() }
|
||||||
return;
|
return;
|
||||||
}}
|
}});
|
||||||
|
|
||||||
module.exports = SEA.pair;
|
module.exports = SEA.pair;
|
||||||
})(USE, './pair');
|
})(USE, './pair');
|
||||||
@ -383,7 +388,7 @@
|
|||||||
var S = USE('./settings');
|
var S = USE('./settings');
|
||||||
var sha256hash = USE('./sha256');
|
var sha256hash = USE('./sha256');
|
||||||
|
|
||||||
SEA.sign = async (data, pair, cb) => { try {
|
SEA.sign = SEA.sign || (async (data, pair, cb) => { try {
|
||||||
if(data && data.slice
|
if(data && data.slice
|
||||||
&& 'SEA{' === data.slice(0,4)
|
&& 'SEA{' === data.slice(0,4)
|
||||||
&& '"m":' === data.slice(4,8)){
|
&& '"m":' === data.slice(4,8)){
|
||||||
@ -409,7 +414,7 @@
|
|||||||
SEA.err = e;
|
SEA.err = e;
|
||||||
if(cb){ cb() }
|
if(cb){ cb() }
|
||||||
return;
|
return;
|
||||||
}}
|
}});
|
||||||
|
|
||||||
module.exports = SEA.sign;
|
module.exports = SEA.sign;
|
||||||
})(USE, './sign');
|
})(USE, './sign');
|
||||||
@ -422,7 +427,7 @@
|
|||||||
var parse = USE('./parse');
|
var parse = USE('./parse');
|
||||||
var u;
|
var u;
|
||||||
|
|
||||||
SEA.verify = async (data, pair, cb) => { try {
|
SEA.verify = SEA.verify || (async (data, pair, cb) => { try {
|
||||||
const json = parse(data)
|
const json = parse(data)
|
||||||
if(false === pair){ // don't verify!
|
if(false === pair){ // don't verify!
|
||||||
const raw = (json !== data)?
|
const raw = (json !== data)?
|
||||||
@ -447,7 +452,7 @@
|
|||||||
SEA.err = e;
|
SEA.err = e;
|
||||||
if(cb){ cb() }
|
if(cb){ cb() }
|
||||||
return;
|
return;
|
||||||
}}
|
}});
|
||||||
|
|
||||||
module.exports = SEA.verify;
|
module.exports = SEA.verify;
|
||||||
})(USE, './verify');
|
})(USE, './verify');
|
||||||
@ -472,13 +477,13 @@
|
|||||||
var S = USE('./settings');
|
var S = USE('./settings');
|
||||||
var aeskey = USE('./aeskey');
|
var aeskey = USE('./aeskey');
|
||||||
|
|
||||||
SEA.encrypt = async (data, pair, cb, opt) => { try {
|
SEA.encrypt = SEA.encrypt || (async (data, pair, cb, opt) => { try {
|
||||||
var opt = opt || {};
|
var opt = opt || {};
|
||||||
const key = pair.epriv || pair;
|
const key = pair.epriv || pair;
|
||||||
const msg = JSON.stringify(data)
|
const msg = JSON.stringify(data)
|
||||||
const rand = {s: shim.random(8), iv: shim.random(16)};
|
const rand = {s: shim.random(8), iv: shim.random(16)};
|
||||||
const ct = await aeskey(key, rand.s, opt)
|
const ct = await aeskey(key, rand.s, opt)
|
||||||
.then((aes) => shim.subtle.encrypt({ // Keeping the AES key scope as private as possible...
|
.then((aes) => (/*shim.ossl ||*/ shim.subtle).encrypt({ // Keeping the AES key scope as private as possible...
|
||||||
name: opt.name || 'AES-GCM', iv: new Uint8Array(rand.iv)
|
name: opt.name || 'AES-GCM', iv: new Uint8Array(rand.iv)
|
||||||
}, aes, new shim.TextEncoder().encode(msg)))
|
}, aes, new shim.TextEncoder().encode(msg)))
|
||||||
const r = 'SEA'+JSON.stringify({
|
const r = 'SEA'+JSON.stringify({
|
||||||
@ -493,7 +498,7 @@
|
|||||||
SEA.err = e;
|
SEA.err = e;
|
||||||
if(cb){ cb() }
|
if(cb){ cb() }
|
||||||
return;
|
return;
|
||||||
}}
|
}});
|
||||||
|
|
||||||
module.exports = SEA.encrypt;
|
module.exports = SEA.encrypt;
|
||||||
})(USE, './encrypt');
|
})(USE, './encrypt');
|
||||||
@ -505,23 +510,22 @@
|
|||||||
var aeskey = USE('./aeskey');
|
var aeskey = USE('./aeskey');
|
||||||
var parse = USE('./parse');
|
var parse = USE('./parse');
|
||||||
|
|
||||||
SEA.decrypt = async (data, pair, cb, opt) => { try {
|
SEA.decrypt = SEA.decrypt || (async (data, pair, cb, opt) => { try {
|
||||||
var opt = opt || {};
|
var opt = opt || {};
|
||||||
const key = pair.epriv || pair;
|
const key = pair.epriv || pair;
|
||||||
const json = parse(data)
|
const json = parse(data)
|
||||||
const ct = await aeskey(key, shim.Buffer.from(json.s, 'utf8'), opt)
|
const ct = await aeskey(key, shim.Buffer.from(json.s, 'utf8'), opt)
|
||||||
.then((aes) => shim.subtle.decrypt({ // Keeping aesKey scope as private as possible...
|
.then((aes) => (/*shim.ossl ||*/ shim.subtle).decrypt({ // Keeping aesKey scope as private as possible...
|
||||||
name: opt.name || 'AES-GCM', iv: new Uint8Array(shim.Buffer.from(json.iv, 'utf8'))
|
name: opt.name || 'AES-GCM', iv: new Uint8Array(shim.Buffer.from(json.iv, 'utf8'))
|
||||||
}, aes, new Uint8Array(shim.Buffer.from(json.ct, 'utf8'))))
|
}, aes, new Uint8Array(shim.Buffer.from(json.ct, 'utf8'))))
|
||||||
const r = parse(new shim.TextDecoder('utf8').decode(ct))
|
const r = parse(new shim.TextDecoder('utf8').decode(ct))
|
||||||
|
|
||||||
if(cb){ try{ cb(r) }catch(e){console.log(e)} }
|
if(cb){ try{ cb(r) }catch(e){console.log(e)} }
|
||||||
return r;
|
return r;
|
||||||
} catch(e) {
|
} catch(e) {
|
||||||
SEA.err = e;
|
SEA.err = e;
|
||||||
if(cb){ cb() }
|
if(cb){ cb() }
|
||||||
return;
|
return;
|
||||||
}}
|
}});
|
||||||
|
|
||||||
module.exports = SEA.decrypt;
|
module.exports = SEA.decrypt;
|
||||||
})(USE, './decrypt');
|
})(USE, './decrypt');
|
||||||
@ -531,7 +535,7 @@
|
|||||||
var shim = USE('./shim');
|
var shim = USE('./shim');
|
||||||
var S = USE('./settings');
|
var S = USE('./settings');
|
||||||
// Derive shared secret from other's pub and my epub/epriv
|
// Derive shared secret from other's pub and my epub/epriv
|
||||||
SEA.secret = async (key, pair, cb) => { try {
|
SEA.secret = SEA.secret || (async (key, pair, cb) => { try {
|
||||||
const pub = key.epub || key
|
const pub = key.epub || key
|
||||||
const epub = pair.epub
|
const epub = pair.epub
|
||||||
const epriv = pair.epriv
|
const epriv = pair.epriv
|
||||||
@ -555,7 +559,7 @@
|
|||||||
SEA.err = e;
|
SEA.err = e;
|
||||||
if(cb){ cb() }
|
if(cb){ cb() }
|
||||||
return;
|
return;
|
||||||
}}
|
}});
|
||||||
|
|
||||||
const keysToEcdhJwk = (pub, d) => { // d === priv
|
const keysToEcdhJwk = (pub, d) => { // d === priv
|
||||||
//const [ x, y ] = Buffer.from(pub, 'base64').toString('utf8').split(':') // old
|
//const [ x, y ] = Buffer.from(pub, 'base64').toString('utf8').split(':') // old
|
||||||
@ -605,7 +609,7 @@
|
|||||||
SEA.encrypt = USE('./encrypt');
|
SEA.encrypt = USE('./encrypt');
|
||||||
SEA.decrypt = USE('./decrypt');
|
SEA.decrypt = USE('./decrypt');
|
||||||
|
|
||||||
SEA.random = getRandomBytes;
|
SEA.random = SEA.random || getRandomBytes;
|
||||||
|
|
||||||
// This is easy way to use IndexedDB, all methods are Promises
|
// This is easy way to use IndexedDB, all methods are Promises
|
||||||
// Note: Not all SEA interfaces have to support this.
|
// Note: Not all SEA interfaces have to support this.
|
||||||
@ -613,7 +617,7 @@
|
|||||||
|
|
||||||
// This is Buffer used in SEA and usable from Gun/SEA application also.
|
// This is Buffer used in SEA and usable from Gun/SEA application also.
|
||||||
// For documentation see https://nodejs.org/api/buffer.html
|
// For documentation see https://nodejs.org/api/buffer.html
|
||||||
SEA.Buffer = Buffer;
|
SEA.Buffer = SEA.Buffer || Buffer;
|
||||||
|
|
||||||
// These SEA functions support now ony Promises or
|
// These SEA functions support now ony Promises or
|
||||||
// async/await (compatible) code, use those like Promises.
|
// async/await (compatible) code, use those like Promises.
|
||||||
@ -621,7 +625,7 @@
|
|||||||
// Creates a wrapper library around Web Crypto API
|
// Creates a wrapper library around Web Crypto API
|
||||||
// for various AES, ECDSA, PBKDF2 functions we called above.
|
// for various AES, ECDSA, PBKDF2 functions we called above.
|
||||||
// Calculate public key KeyID aka PGPv4 (result: 8 bytes as hex string)
|
// Calculate public key KeyID aka PGPv4 (result: 8 bytes as hex string)
|
||||||
SEA.keyid = async (pub) => {
|
SEA.keyid = SEA.keyid || (async (pub) => {
|
||||||
try {
|
try {
|
||||||
// base64('base64(x):base64(y)') => Buffer(xy)
|
// base64('base64(x):base64(y)') => Buffer(xy)
|
||||||
const pb = Buffer.concat(
|
const pb = Buffer.concat(
|
||||||
@ -639,7 +643,7 @@
|
|||||||
console.log(e)
|
console.log(e)
|
||||||
throw e
|
throw e
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
// all done!
|
// all done!
|
||||||
// Obviously it is missing MANY necessary features. This is only an alpha release.
|
// Obviously it is missing MANY necessary features. This is only an alpha release.
|
||||||
// Please experiment with it, audit what I've done so far, and complain about what needs to be added.
|
// Please experiment with it, audit what I've done so far, and complain about what needs to be added.
|
||||||
@ -649,7 +653,7 @@
|
|||||||
// But all other behavior needs to be equally easy, like opinionated ways of
|
// But all other behavior needs to be equally easy, like opinionated ways of
|
||||||
// Adding friends (trusted public keys), sending private messages, etc.
|
// Adding friends (trusted public keys), sending private messages, etc.
|
||||||
// Cheers! Tell me what you think.
|
// Cheers! Tell me what you think.
|
||||||
var Gun = (SEA.window||{}).Gun || require('./gun');
|
var Gun = (SEA.window||{}).Gun || USE('./gun', 1);
|
||||||
Gun.SEA = SEA;
|
Gun.SEA = SEA;
|
||||||
SEA.Gun = Gun;
|
SEA.Gun = Gun;
|
||||||
|
|
||||||
@ -662,9 +666,9 @@
|
|||||||
// This is internal func queries public key(s) for alias.
|
// This is internal func queries public key(s) for alias.
|
||||||
const queryGunAliases = (alias, gunRoot) => new Promise((resolve, reject) => {
|
const queryGunAliases = (alias, gunRoot) => new Promise((resolve, reject) => {
|
||||||
// load all public keys associated with the username alias we want to log in with.
|
// load all public keys associated with the username alias we want to log in with.
|
||||||
gunRoot.get('~@'+alias).get((rat, rev) => {
|
gunRoot.get('~@'+alias).once((data, key) => {
|
||||||
rev.off();
|
//rev.off();
|
||||||
if (!rat.put) {
|
if (!data) {
|
||||||
// if no user, don't do anything.
|
// if no user, don't do anything.
|
||||||
const err = 'No user!'
|
const err = 'No user!'
|
||||||
Gun.log(err)
|
Gun.log(err)
|
||||||
@ -674,19 +678,18 @@
|
|||||||
const aliases = []
|
const aliases = []
|
||||||
let c = 0
|
let c = 0
|
||||||
// TODO: how about having real chainable map without callback ?
|
// TODO: how about having real chainable map without callback ?
|
||||||
Gun.obj.map(rat.put, (at, pub) => {
|
Gun.obj.map(data, (at, pub) => {
|
||||||
if (!pub.slice || '~' !== pub.slice(0, 1)) {
|
if (!pub.slice || '~' !== pub.slice(0, 1)) {
|
||||||
// TODO: ... this would then be .filter((at, pub))
|
// TODO: ... this would then be .filter((at, pub))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
++c
|
++c
|
||||||
// grab the account associated with this public key.
|
// grab the account associated with this public key.
|
||||||
gunRoot.get(pub).get((at, ev) => {
|
gunRoot.get(pub).once(data => {
|
||||||
pub = pub.slice(1)
|
pub = pub.slice(1)
|
||||||
ev.off()
|
|
||||||
--c
|
--c
|
||||||
if (at.put){
|
if (data){
|
||||||
aliases.push({ pub, at })
|
aliases.push({ pub, put: data })
|
||||||
}
|
}
|
||||||
if (!c && (c = -1)) {
|
if (!c && (c = -1)) {
|
||||||
resolve(aliases)
|
resolve(aliases)
|
||||||
@ -710,7 +713,7 @@
|
|||||||
const authenticate = async (alias, pass, gunRoot) => {
|
const authenticate = async (alias, pass, gunRoot) => {
|
||||||
// load all public keys associated with the username alias we want to log in with.
|
// load all public keys associated with the username alias we want to log in with.
|
||||||
const aliases = (await queryGunAliases(alias, gunRoot))
|
const aliases = (await queryGunAliases(alias, gunRoot))
|
||||||
.filter(({ pub, at: { put } = {} } = {}) => !!pub && !!put)
|
.filter(a => !!a.pub && !!a.put)
|
||||||
// Got any?
|
// Got any?
|
||||||
if (!aliases.length) {
|
if (!aliases.length) {
|
||||||
throw { err: 'Public key does not exist!' }
|
throw { err: 'Public key does not exist!' }
|
||||||
@ -718,14 +721,14 @@
|
|||||||
let err
|
let err
|
||||||
// then attempt to log into each one until we find ours!
|
// then attempt to log into each one until we find ours!
|
||||||
// (if two users have the same username AND the same password... that would be bad)
|
// (if two users have the same username AND the same password... that would be bad)
|
||||||
const users = await Promise.all(aliases.map(async ({ at: at, pub: pub }, i) => {
|
const users = await Promise.all(aliases.map(async (a, i) => {
|
||||||
// attempt to PBKDF2 extend the password with the salt. (Verifying the signature gives us the plain text salt.)
|
// attempt to PBKDF2 extend the password with the salt. (Verifying the signature gives us the plain text salt.)
|
||||||
const auth = parseProps(at.put.auth)
|
const auth = parseProps(a.put.auth)
|
||||||
// NOTE: aliasquery uses `gun.get` which internally SEA.read verifies the data for us, so we do not need to re-verify it here.
|
// NOTE: aliasquery uses `gun.get` which internally SEA.read verifies the data for us, so we do not need to re-verify it here.
|
||||||
// SEA.verify(at.put.auth, pub).then(function(auth){
|
// SEA.verify(at.put.auth, pub).then(function(auth){
|
||||||
try {
|
try {
|
||||||
const proof = await SEA.work(pass, auth.s)
|
const proof = await SEA.work(pass, auth.s)
|
||||||
const props = { pub: pub, proof: proof, at: at }
|
//const props = { pub: pub, proof: proof, at: at }
|
||||||
// the proof of work is evidence that we've spent some time/effort trying to log in, this slows brute force.
|
// the proof of work is evidence that we've spent some time/effort trying to log in, this slows brute force.
|
||||||
/*
|
/*
|
||||||
MARK TO @mhelander : pub vs epub!???
|
MARK TO @mhelander : pub vs epub!???
|
||||||
@ -733,24 +736,24 @@
|
|||||||
const salt = auth.salt
|
const salt = auth.salt
|
||||||
const sea = await SEA.decrypt(auth.ek, proof)
|
const sea = await SEA.decrypt(auth.ek, proof)
|
||||||
if (!sea) {
|
if (!sea) {
|
||||||
err = 'Failed to decrypt secret! ' + i +'/'+aliases.length;
|
err = 'Failed to decrypt secret! ' + (i+1) +'/'+aliases.length;
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// now we have AES decrypted the private key, from when we encrypted it with the proof at registration.
|
// now we have AES decrypted the private key, from when we encrypted it with the proof at registration.
|
||||||
// if we were successful, then that meanswe're logged in!
|
// if we were successful, then that meanswe're logged in!
|
||||||
const priv = sea.priv
|
const priv = sea.priv
|
||||||
const epriv = sea.epriv
|
const epriv = sea.epriv
|
||||||
const epub = at.put.epub
|
const epub = a.put.epub
|
||||||
// TODO: 'salt' needed?
|
// TODO: 'salt' needed?
|
||||||
err = null
|
err = null
|
||||||
if(typeof window !== 'undefined'){
|
if(SEA.window){
|
||||||
var tmp = window.sessionStorage;
|
var tmp = SEA.window.sessionStorage;
|
||||||
if(tmp && gunRoot._.opt.remember){
|
if(tmp && gunRoot._.opt.remember){
|
||||||
window.sessionStorage.alias = alias;
|
SEA.window.sessionStorage.alias = alias;
|
||||||
window.sessionStorage.tmp = pass;
|
SEA.window.sessionStorage.tmp = pass;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Object.assign(props, { priv: priv, salt: salt, epub: epub, epriv: epriv })
|
return {priv: priv, pub: a.put.pub, salt: salt, epub: epub, epriv: epriv };
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
err = 'Failed to decrypt secret!'
|
err = 'Failed to decrypt secret!'
|
||||||
throw { err }
|
throw { err }
|
||||||
@ -862,10 +865,25 @@
|
|||||||
const finalizeLogin = async (alias, key, gunRoot, opts) => {
|
const finalizeLogin = async (alias, key, gunRoot, opts) => {
|
||||||
const user = gunRoot._.user
|
const user = gunRoot._.user
|
||||||
// add our credentials in-memory only to our root gun instance
|
// add our credentials in-memory only to our root gun instance
|
||||||
//var tmp = user._.tag;
|
var tmp = user._.tag;
|
||||||
var opt = user._.opt;
|
var opt = user._.opt;
|
||||||
user._ = key.at.$._;
|
user._ = gunRoot.get('~'+key.pub)._;
|
||||||
user._.opt = opt;
|
user._.opt = opt;
|
||||||
|
var tags = user._.tag;
|
||||||
|
/*Object.values && Object.values(tmp).forEach(function(tag){
|
||||||
|
// TODO: This is ugly & buggy code, it needs to be refactored & tested into a event "merge" utility.
|
||||||
|
var t = tags[tag.tag];
|
||||||
|
console.log("hm??", tag, t);
|
||||||
|
if(!t){
|
||||||
|
tags[tag.tag] = tag;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(tag.last){
|
||||||
|
tag.last.to = t.to;
|
||||||
|
t.last = tag.last = t.last || tag.last;
|
||||||
|
}
|
||||||
|
t.to = tag.to;
|
||||||
|
})*/
|
||||||
//user._.tag = tmp || user._.tag;
|
//user._.tag = tmp || user._.tag;
|
||||||
// so that way we can use the credentials to encrypt/decrypt data
|
// so that way we can use the credentials to encrypt/decrypt data
|
||||||
// that is input/output through gun (see below)
|
// that is input/output through gun (see below)
|
||||||
@ -880,7 +898,8 @@
|
|||||||
//await authPersist(user._, key.proof, opts) // temporarily disabled
|
//await authPersist(user._, key.proof, opts) // temporarily disabled
|
||||||
// emit an auth event, useful for page redirects and stuff.
|
// emit an auth event, useful for page redirects and stuff.
|
||||||
try {
|
try {
|
||||||
gunRoot._.on('auth', user._)
|
gunRoot._.on('auth', user._) // TODO: Deprecate this, emit on user instead! Update docs when you do.
|
||||||
|
//user._.on('auth', user._) // Arrgh, this doesn't work without event "merge" code, but "merge" code causes stack overflow and crashes after logging in & trying to write data.
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log('Your \'auth\' callback crashed with:', e)
|
console.log('Your \'auth\' callback crashed with:', e)
|
||||||
}
|
}
|
||||||
@ -1300,7 +1319,8 @@
|
|||||||
}
|
}
|
||||||
// If authentication is to be remembered over reloads or browser closing,
|
// If authentication is to be remembered over reloads or browser closing,
|
||||||
// set validity time in minutes.
|
// set validity time in minutes.
|
||||||
User.prototype.recall = async function(setvalidity, options){
|
User.prototype.recall = function(setvalidity, options){
|
||||||
|
var gun = this;
|
||||||
const gunRoot = this.back(-1)
|
const gunRoot = this.back(-1)
|
||||||
|
|
||||||
let validity
|
let validity
|
||||||
@ -1317,7 +1337,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return this;
|
return gun;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!Gun.val.is(setvalidity)) {
|
if (!Gun.val.is(setvalidity)) {
|
||||||
@ -1340,13 +1360,15 @@
|
|||||||
authsettings.hook = (Gun.obj.has(opts, 'hook') && typeof opts.hook === 'function')
|
authsettings.hook = (Gun.obj.has(opts, 'hook') && typeof opts.hook === 'function')
|
||||||
? opts.hook : _initial_authsettings.hook
|
? opts.hook : _initial_authsettings.hook
|
||||||
// All is good. Should we do something more with actual recalled data?
|
// All is good. Should we do something more with actual recalled data?
|
||||||
return await authRecall(gunRoot)
|
(async function(){ await authRecall(gunRoot) }());
|
||||||
|
return gun;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
const err = 'No session!'
|
const err = 'No session!'
|
||||||
Gun.log(err)
|
Gun.log(err)
|
||||||
// NOTE! It's fine to resolve recall with reason why not successful
|
// NOTE! It's fine to resolve recall with reason why not successful
|
||||||
// instead of rejecting...
|
// instead of rejecting...
|
||||||
return { err: (e && e.err) || err }
|
//return { err: (e && e.err) || err }
|
||||||
|
return gun;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
User.prototype.alive = async function(){
|
User.prototype.alive = async function(){
|
||||||
@ -1563,7 +1585,7 @@
|
|||||||
if(tmp = relpub(soul)){
|
if(tmp = relpub(soul)){
|
||||||
check['any'+soul+key] = 1;
|
check['any'+soul+key] = 1;
|
||||||
SEA.verify(val, pub = tmp, function(data){ var rel;
|
SEA.verify(val, pub = tmp, function(data){ var rel;
|
||||||
if(!data){ return each.end({err: "Mismatched owner on '" + key + "'."}) }
|
if(u === data){ return each.end({err: "Mismatched owner on '" + key + "'."}) } // thanks @rogowski !
|
||||||
if((rel = Gun.val.link.is(data)) && pub === relpub(rel)){
|
if((rel = Gun.val.link.is(data)) && pub === relpub(rel)){
|
||||||
(at.sea.own[rel] = at.sea.own[rel] || {})[pub] = true;
|
(at.sea.own[rel] = at.sea.own[rel] || {})[pub] = true;
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
const authenticate = async (alias, pass, gunRoot) => {
|
const authenticate = async (alias, pass, gunRoot) => {
|
||||||
// load all public keys associated with the username alias we want to log in with.
|
// load all public keys associated with the username alias we want to log in with.
|
||||||
const aliases = (await queryGunAliases(alias, gunRoot))
|
const aliases = (await queryGunAliases(alias, gunRoot))
|
||||||
.filter(({ pub, at: { put } = {} } = {}) => !!pub && !!put)
|
.filter(a => !!a.pub && !!a.put)
|
||||||
// Got any?
|
// Got any?
|
||||||
if (!aliases.length) {
|
if (!aliases.length) {
|
||||||
throw { err: 'Public key does not exist!' }
|
throw { err: 'Public key does not exist!' }
|
||||||
@ -15,14 +15,14 @@
|
|||||||
let err
|
let err
|
||||||
// then attempt to log into each one until we find ours!
|
// then attempt to log into each one until we find ours!
|
||||||
// (if two users have the same username AND the same password... that would be bad)
|
// (if two users have the same username AND the same password... that would be bad)
|
||||||
const users = await Promise.all(aliases.map(async ({ at: at, pub: pub }, i) => {
|
const users = await Promise.all(aliases.map(async (a, i) => {
|
||||||
// attempt to PBKDF2 extend the password with the salt. (Verifying the signature gives us the plain text salt.)
|
// attempt to PBKDF2 extend the password with the salt. (Verifying the signature gives us the plain text salt.)
|
||||||
const auth = parseProps(at.put.auth)
|
const auth = parseProps(a.put.auth)
|
||||||
// NOTE: aliasquery uses `gun.get` which internally SEA.read verifies the data for us, so we do not need to re-verify it here.
|
// NOTE: aliasquery uses `gun.get` which internally SEA.read verifies the data for us, so we do not need to re-verify it here.
|
||||||
// SEA.verify(at.put.auth, pub).then(function(auth){
|
// SEA.verify(at.put.auth, pub).then(function(auth){
|
||||||
try {
|
try {
|
||||||
const proof = await SEA.work(pass, auth.s)
|
const proof = await SEA.work(pass, auth.s)
|
||||||
const props = { pub: pub, proof: proof, at: at }
|
//const props = { pub: pub, proof: proof, at: at }
|
||||||
// the proof of work is evidence that we've spent some time/effort trying to log in, this slows brute force.
|
// the proof of work is evidence that we've spent some time/effort trying to log in, this slows brute force.
|
||||||
/*
|
/*
|
||||||
MARK TO @mhelander : pub vs epub!???
|
MARK TO @mhelander : pub vs epub!???
|
||||||
@ -30,24 +30,24 @@
|
|||||||
const salt = auth.salt
|
const salt = auth.salt
|
||||||
const sea = await SEA.decrypt(auth.ek, proof)
|
const sea = await SEA.decrypt(auth.ek, proof)
|
||||||
if (!sea) {
|
if (!sea) {
|
||||||
err = 'Failed to decrypt secret! ' + i +'/'+aliases.length;
|
err = 'Failed to decrypt secret! ' + (i+1) +'/'+aliases.length;
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// now we have AES decrypted the private key, from when we encrypted it with the proof at registration.
|
// now we have AES decrypted the private key, from when we encrypted it with the proof at registration.
|
||||||
// if we were successful, then that meanswe're logged in!
|
// if we were successful, then that meanswe're logged in!
|
||||||
const priv = sea.priv
|
const priv = sea.priv
|
||||||
const epriv = sea.epriv
|
const epriv = sea.epriv
|
||||||
const epub = at.put.epub
|
const epub = a.put.epub
|
||||||
// TODO: 'salt' needed?
|
// TODO: 'salt' needed?
|
||||||
err = null
|
err = null
|
||||||
if(typeof window !== 'undefined'){
|
if(SEA.window){
|
||||||
var tmp = window.sessionStorage;
|
var tmp = SEA.window.sessionStorage;
|
||||||
if(tmp && gunRoot._.opt.remember){
|
if(tmp && gunRoot._.opt.remember){
|
||||||
window.sessionStorage.alias = alias;
|
SEA.window.sessionStorage.alias = alias;
|
||||||
window.sessionStorage.tmp = pass;
|
SEA.window.sessionStorage.tmp = pass;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Object.assign(props, { priv: priv, salt: salt, epub: epub, epriv: epriv })
|
return {priv: priv, pub: a.put.pub, salt: salt, epub: epub, epriv: epriv };
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
err = 'Failed to decrypt secret!'
|
err = 'Failed to decrypt secret!'
|
||||||
throw { err }
|
throw { err }
|
||||||
|
@ -202,7 +202,8 @@
|
|||||||
}
|
}
|
||||||
// If authentication is to be remembered over reloads or browser closing,
|
// If authentication is to be remembered over reloads or browser closing,
|
||||||
// set validity time in minutes.
|
// set validity time in minutes.
|
||||||
User.prototype.recall = async function(setvalidity, options){
|
User.prototype.recall = function(setvalidity, options){
|
||||||
|
var gun = this;
|
||||||
const gunRoot = this.back(-1)
|
const gunRoot = this.back(-1)
|
||||||
|
|
||||||
let validity
|
let validity
|
||||||
@ -219,7 +220,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return this;
|
return gun;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!Gun.val.is(setvalidity)) {
|
if (!Gun.val.is(setvalidity)) {
|
||||||
@ -242,13 +243,15 @@
|
|||||||
authsettings.hook = (Gun.obj.has(opts, 'hook') && typeof opts.hook === 'function')
|
authsettings.hook = (Gun.obj.has(opts, 'hook') && typeof opts.hook === 'function')
|
||||||
? opts.hook : _initial_authsettings.hook
|
? opts.hook : _initial_authsettings.hook
|
||||||
// All is good. Should we do something more with actual recalled data?
|
// All is good. Should we do something more with actual recalled data?
|
||||||
return await authRecall(gunRoot)
|
(async function(){ await authRecall(gunRoot) }());
|
||||||
|
return gun;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
const err = 'No session!'
|
const err = 'No session!'
|
||||||
Gun.log(err)
|
Gun.log(err)
|
||||||
// NOTE! It's fine to resolve recall with reason why not successful
|
// NOTE! It's fine to resolve recall with reason why not successful
|
||||||
// instead of rejecting...
|
// instead of rejecting...
|
||||||
return { err: (e && e.err) || err }
|
//return { err: (e && e.err) || err }
|
||||||
|
return gun;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
User.prototype.alive = async function(){
|
User.prototype.alive = async function(){
|
||||||
|
@ -5,23 +5,22 @@
|
|||||||
var aeskey = require('./aeskey');
|
var aeskey = require('./aeskey');
|
||||||
var parse = require('./parse');
|
var parse = require('./parse');
|
||||||
|
|
||||||
SEA.decrypt = async (data, pair, cb, opt) => { try {
|
SEA.decrypt = SEA.decrypt || (async (data, pair, cb, opt) => { try {
|
||||||
var opt = opt || {};
|
var opt = opt || {};
|
||||||
const key = pair.epriv || pair;
|
const key = pair.epriv || pair;
|
||||||
const json = parse(data)
|
const json = parse(data)
|
||||||
const ct = await aeskey(key, shim.Buffer.from(json.s, 'utf8'), opt)
|
const ct = await aeskey(key, shim.Buffer.from(json.s, 'utf8'), opt)
|
||||||
.then((aes) => shim.subtle.decrypt({ // Keeping aesKey scope as private as possible...
|
.then((aes) => (/*shim.ossl ||*/ shim.subtle).decrypt({ // Keeping aesKey scope as private as possible...
|
||||||
name: opt.name || 'AES-GCM', iv: new Uint8Array(shim.Buffer.from(json.iv, 'utf8'))
|
name: opt.name || 'AES-GCM', iv: new Uint8Array(shim.Buffer.from(json.iv, 'utf8'))
|
||||||
}, aes, new Uint8Array(shim.Buffer.from(json.ct, 'utf8'))))
|
}, aes, new Uint8Array(shim.Buffer.from(json.ct, 'utf8'))))
|
||||||
const r = parse(new shim.TextDecoder('utf8').decode(ct))
|
const r = parse(new shim.TextDecoder('utf8').decode(ct))
|
||||||
|
|
||||||
if(cb){ try{ cb(r) }catch(e){console.log(e)} }
|
if(cb){ try{ cb(r) }catch(e){console.log(e)} }
|
||||||
return r;
|
return r;
|
||||||
} catch(e) {
|
} catch(e) {
|
||||||
SEA.err = e;
|
SEA.err = e;
|
||||||
if(cb){ cb() }
|
if(cb){ cb() }
|
||||||
return;
|
return;
|
||||||
}}
|
}});
|
||||||
|
|
||||||
module.exports = SEA.decrypt;
|
module.exports = SEA.decrypt;
|
||||||
|
|
@ -4,13 +4,13 @@
|
|||||||
var S = require('./settings');
|
var S = require('./settings');
|
||||||
var aeskey = require('./aeskey');
|
var aeskey = require('./aeskey');
|
||||||
|
|
||||||
SEA.encrypt = async (data, pair, cb, opt) => { try {
|
SEA.encrypt = SEA.encrypt || (async (data, pair, cb, opt) => { try {
|
||||||
var opt = opt || {};
|
var opt = opt || {};
|
||||||
const key = pair.epriv || pair;
|
const key = pair.epriv || pair;
|
||||||
const msg = JSON.stringify(data)
|
const msg = JSON.stringify(data)
|
||||||
const rand = {s: shim.random(8), iv: shim.random(16)};
|
const rand = {s: shim.random(8), iv: shim.random(16)};
|
||||||
const ct = await aeskey(key, rand.s, opt)
|
const ct = await aeskey(key, rand.s, opt)
|
||||||
.then((aes) => shim.subtle.encrypt({ // Keeping the AES key scope as private as possible...
|
.then((aes) => (/*shim.ossl ||*/ shim.subtle).encrypt({ // Keeping the AES key scope as private as possible...
|
||||||
name: opt.name || 'AES-GCM', iv: new Uint8Array(rand.iv)
|
name: opt.name || 'AES-GCM', iv: new Uint8Array(rand.iv)
|
||||||
}, aes, new shim.TextEncoder().encode(msg)))
|
}, aes, new shim.TextEncoder().encode(msg)))
|
||||||
const r = 'SEA'+JSON.stringify({
|
const r = 'SEA'+JSON.stringify({
|
||||||
@ -25,7 +25,7 @@
|
|||||||
SEA.err = e;
|
SEA.err = e;
|
||||||
if(cb){ cb() }
|
if(cb){ cb() }
|
||||||
return;
|
return;
|
||||||
}}
|
}});
|
||||||
|
|
||||||
module.exports = SEA.encrypt;
|
module.exports = SEA.encrypt;
|
||||||
|
|
@ -62,6 +62,7 @@
|
|||||||
// if there is a request to read data from us, then...
|
// if there is a request to read data from us, then...
|
||||||
var soul = msg.get['#'];
|
var soul = msg.get['#'];
|
||||||
if(soul){ // for now, only allow direct IDs to be read.
|
if(soul){ // for now, only allow direct IDs to be read.
|
||||||
|
if(soul !== 'string'){ return to.next(msg) } // do not handle lexical cursors.
|
||||||
if('alias' === soul){ // Allow reading the list of usernames/aliases in the system?
|
if('alias' === soul){ // Allow reading the list of usernames/aliases in the system?
|
||||||
return to.next(msg); // yes.
|
return to.next(msg); // yes.
|
||||||
} else
|
} else
|
||||||
@ -149,7 +150,7 @@
|
|||||||
if(tmp = relpub(soul)){
|
if(tmp = relpub(soul)){
|
||||||
check['any'+soul+key] = 1;
|
check['any'+soul+key] = 1;
|
||||||
SEA.verify(val, pub = tmp, function(data){ var rel;
|
SEA.verify(val, pub = tmp, function(data){ var rel;
|
||||||
if(u === data){ return each.end({err: "Mismatched owner on '" + key + "'."}) }
|
if(u === data){ return each.end({err: "Mismatched owner on '" + key + "'."}) } // thanks @rogowski !
|
||||||
if((rel = Gun.val.link.is(data)) && pub === relpub(rel)){
|
if((rel = Gun.val.link.is(data)) && pub === relpub(rel)){
|
||||||
(at.sea.own[rel] = at.sea.own[rel] || {})[pub] = true;
|
(at.sea.own[rel] = at.sea.own[rel] || {})[pub] = true;
|
||||||
}
|
}
|
||||||
|
22
sea/login.js
22
sea/login.js
@ -4,10 +4,25 @@
|
|||||||
const finalizeLogin = async (alias, key, gunRoot, opts) => {
|
const finalizeLogin = async (alias, key, gunRoot, opts) => {
|
||||||
const user = gunRoot._.user
|
const user = gunRoot._.user
|
||||||
// add our credentials in-memory only to our root gun instance
|
// add our credentials in-memory only to our root gun instance
|
||||||
//var tmp = user._.tag;
|
var tmp = user._.tag;
|
||||||
var opt = user._.opt;
|
var opt = user._.opt;
|
||||||
user._ = key.at.$._;
|
user._ = gunRoot.get('~'+key.pub)._;
|
||||||
user._.opt = opt;
|
user._.opt = opt;
|
||||||
|
var tags = user._.tag;
|
||||||
|
/*Object.values && Object.values(tmp).forEach(function(tag){
|
||||||
|
// TODO: This is ugly & buggy code, it needs to be refactored & tested into a event "merge" utility.
|
||||||
|
var t = tags[tag.tag];
|
||||||
|
console.log("hm??", tag, t);
|
||||||
|
if(!t){
|
||||||
|
tags[tag.tag] = tag;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(tag.last){
|
||||||
|
tag.last.to = t.to;
|
||||||
|
t.last = tag.last = t.last || tag.last;
|
||||||
|
}
|
||||||
|
t.to = tag.to;
|
||||||
|
})*/
|
||||||
//user._.tag = tmp || user._.tag;
|
//user._.tag = tmp || user._.tag;
|
||||||
// so that way we can use the credentials to encrypt/decrypt data
|
// so that way we can use the credentials to encrypt/decrypt data
|
||||||
// that is input/output through gun (see below)
|
// that is input/output through gun (see below)
|
||||||
@ -22,7 +37,8 @@
|
|||||||
//await authPersist(user._, key.proof, opts) // temporarily disabled
|
//await authPersist(user._, key.proof, opts) // temporarily disabled
|
||||||
// emit an auth event, useful for page redirects and stuff.
|
// emit an auth event, useful for page redirects and stuff.
|
||||||
try {
|
try {
|
||||||
gunRoot._.on('auth', user._)
|
gunRoot._.on('auth', user._) // TODO: Deprecate this, emit on user instead! Update docs when you do.
|
||||||
|
//user._.on('auth', user._) // Arrgh, this doesn't work without event "merge" code, but "merge" code causes stack overflow and crashes after logging in & trying to write data.
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log('Your \'auth\' callback crashed with:', e)
|
console.log('Your \'auth\' callback crashed with:', e)
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
var Buff = (typeof Buffer !== 'undefined')? Buffer : shim.Buffer;
|
var Buff = (typeof Buffer !== 'undefined')? Buffer : shim.Buffer;
|
||||||
|
|
||||||
//SEA.pair = async (data, proof, cb) => { try {
|
//SEA.pair = async (data, proof, cb) => { try {
|
||||||
SEA.pair = async (cb) => { try {
|
SEA.pair = SEA.pair || (async (cb) => { try {
|
||||||
|
|
||||||
const ecdhSubtle = shim.ossl || shim.subtle
|
const ecdhSubtle = shim.ossl || shim.subtle
|
||||||
// First: ECDSA keys for signing/verifying...
|
// First: ECDSA keys for signing/verifying...
|
||||||
@ -56,7 +56,7 @@
|
|||||||
SEA.err = e;
|
SEA.err = e;
|
||||||
if(cb){ cb() }
|
if(cb){ cb() }
|
||||||
return;
|
return;
|
||||||
}}
|
}});
|
||||||
|
|
||||||
module.exports = SEA.pair;
|
module.exports = SEA.pair;
|
||||||
|
|
15
sea/query.js
15
sea/query.js
@ -4,9 +4,9 @@
|
|||||||
// This is internal func queries public key(s) for alias.
|
// This is internal func queries public key(s) for alias.
|
||||||
const queryGunAliases = (alias, gunRoot) => new Promise((resolve, reject) => {
|
const queryGunAliases = (alias, gunRoot) => new Promise((resolve, reject) => {
|
||||||
// load all public keys associated with the username alias we want to log in with.
|
// load all public keys associated with the username alias we want to log in with.
|
||||||
gunRoot.get('~@'+alias).get((rat, rev) => {
|
gunRoot.get('~@'+alias).once((data, key) => {
|
||||||
rev.off();
|
//rev.off();
|
||||||
if (!rat.put) {
|
if (!data) {
|
||||||
// if no user, don't do anything.
|
// if no user, don't do anything.
|
||||||
const err = 'No user!'
|
const err = 'No user!'
|
||||||
Gun.log(err)
|
Gun.log(err)
|
||||||
@ -16,19 +16,18 @@
|
|||||||
const aliases = []
|
const aliases = []
|
||||||
let c = 0
|
let c = 0
|
||||||
// TODO: how about having real chainable map without callback ?
|
// TODO: how about having real chainable map without callback ?
|
||||||
Gun.obj.map(rat.put, (at, pub) => {
|
Gun.obj.map(data, (at, pub) => {
|
||||||
if (!pub.slice || '~' !== pub.slice(0, 1)) {
|
if (!pub.slice || '~' !== pub.slice(0, 1)) {
|
||||||
// TODO: ... this would then be .filter((at, pub))
|
// TODO: ... this would then be .filter((at, pub))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
++c
|
++c
|
||||||
// grab the account associated with this public key.
|
// grab the account associated with this public key.
|
||||||
gunRoot.get(pub).get((at, ev) => {
|
gunRoot.get(pub).once(data => {
|
||||||
pub = pub.slice(1)
|
pub = pub.slice(1)
|
||||||
ev.off()
|
|
||||||
--c
|
--c
|
||||||
if (at.put){
|
if (data){
|
||||||
aliases.push({ pub, at })
|
aliases.push({ pub, put: data })
|
||||||
}
|
}
|
||||||
if (!c && (c = -1)) {
|
if (!c && (c = -1)) {
|
||||||
resolve(aliases)
|
resolve(aliases)
|
||||||
|
18
sea/root.js
18
sea/root.js
@ -1,10 +1,22 @@
|
|||||||
|
|
||||||
// Security, Encryption, and Authorization: SEA.js
|
// Security, Encryption, and Authorization: SEA.js
|
||||||
// MANDATORY READING: http://gun.js.org/explainers/data/security.html
|
// MANDATORY READING: https://gun.eco/explainers/data/security.html
|
||||||
|
// IT IS IMPLEMENTED IN A POLYFILL/SHIM APPROACH.
|
||||||
// THIS IS AN EARLY ALPHA!
|
// THIS IS AN EARLY ALPHA!
|
||||||
|
|
||||||
function SEA(){}
|
if(typeof window !== "undefined"){ module.window = window }
|
||||||
if(typeof window !== "undefined"){ (SEA.window = window).SEA = SEA }
|
|
||||||
|
|
||||||
|
var tmp = module.window || module;
|
||||||
|
var SEA = tmp.SEA || function(){};
|
||||||
|
|
||||||
|
if(SEA.window = module.window){ try{
|
||||||
|
SEA.window.SEA = SEA;
|
||||||
|
tmp = document.createEvent('CustomEvent');
|
||||||
|
tmp.initCustomEvent('extension', false, false, {type: "SEA"});
|
||||||
|
(window.dispatchEvent || window.fireEvent)(tmp);
|
||||||
|
window.postMessage({type: "SEA"}, '*');
|
||||||
|
} catch(e){} }
|
||||||
|
|
||||||
|
try{ if(typeof common !== "undefined"){ common.exports = SEA } }catch(e){}
|
||||||
module.exports = SEA;
|
module.exports = SEA;
|
||||||
|
|
10
sea/sea.js
10
sea/sea.js
@ -29,7 +29,7 @@
|
|||||||
SEA.encrypt = require('./encrypt');
|
SEA.encrypt = require('./encrypt');
|
||||||
SEA.decrypt = require('./decrypt');
|
SEA.decrypt = require('./decrypt');
|
||||||
|
|
||||||
SEA.random = getRandomBytes;
|
SEA.random = SEA.random || getRandomBytes;
|
||||||
|
|
||||||
// This is easy way to use IndexedDB, all methods are Promises
|
// This is easy way to use IndexedDB, all methods are Promises
|
||||||
// Note: Not all SEA interfaces have to support this.
|
// Note: Not all SEA interfaces have to support this.
|
||||||
@ -37,7 +37,7 @@
|
|||||||
|
|
||||||
// This is Buffer used in SEA and usable from Gun/SEA application also.
|
// This is Buffer used in SEA and usable from Gun/SEA application also.
|
||||||
// For documentation see https://nodejs.org/api/buffer.html
|
// For documentation see https://nodejs.org/api/buffer.html
|
||||||
SEA.Buffer = Buffer;
|
SEA.Buffer = SEA.Buffer || Buffer;
|
||||||
|
|
||||||
// These SEA functions support now ony Promises or
|
// These SEA functions support now ony Promises or
|
||||||
// async/await (compatible) code, use those like Promises.
|
// async/await (compatible) code, use those like Promises.
|
||||||
@ -45,7 +45,7 @@
|
|||||||
// Creates a wrapper library around Web Crypto API
|
// Creates a wrapper library around Web Crypto API
|
||||||
// for various AES, ECDSA, PBKDF2 functions we called above.
|
// for various AES, ECDSA, PBKDF2 functions we called above.
|
||||||
// Calculate public key KeyID aka PGPv4 (result: 8 bytes as hex string)
|
// Calculate public key KeyID aka PGPv4 (result: 8 bytes as hex string)
|
||||||
SEA.keyid = async (pub) => {
|
SEA.keyid = SEA.keyid || (async (pub) => {
|
||||||
try {
|
try {
|
||||||
// base64('base64(x):base64(y)') => Buffer(xy)
|
// base64('base64(x):base64(y)') => Buffer(xy)
|
||||||
const pb = Buffer.concat(
|
const pb = Buffer.concat(
|
||||||
@ -63,7 +63,7 @@
|
|||||||
console.log(e)
|
console.log(e)
|
||||||
throw e
|
throw e
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
// all done!
|
// all done!
|
||||||
// Obviously it is missing MANY necessary features. This is only an alpha release.
|
// Obviously it is missing MANY necessary features. This is only an alpha release.
|
||||||
// Please experiment with it, audit what I've done so far, and complain about what needs to be added.
|
// Please experiment with it, audit what I've done so far, and complain about what needs to be added.
|
||||||
@ -73,7 +73,7 @@
|
|||||||
// But all other behavior needs to be equally easy, like opinionated ways of
|
// But all other behavior needs to be equally easy, like opinionated ways of
|
||||||
// Adding friends (trusted public keys), sending private messages, etc.
|
// Adding friends (trusted public keys), sending private messages, etc.
|
||||||
// Cheers! Tell me what you think.
|
// Cheers! Tell me what you think.
|
||||||
var Gun = (SEA.window||{}).Gun || require('./gun');
|
var Gun = (SEA.window||{}).Gun || require('./gun', 1);
|
||||||
Gun.SEA = SEA;
|
Gun.SEA = SEA;
|
||||||
SEA.Gun = Gun;
|
SEA.Gun = Gun;
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
var shim = require('./shim');
|
var shim = require('./shim');
|
||||||
var S = require('./settings');
|
var S = require('./settings');
|
||||||
// Derive shared secret from other's pub and my epub/epriv
|
// Derive shared secret from other's pub and my epub/epriv
|
||||||
SEA.secret = async (key, pair, cb) => { try {
|
SEA.secret = SEA.secret || (async (key, pair, cb) => { try {
|
||||||
const pub = key.epub || key
|
const pub = key.epub || key
|
||||||
const epub = pair.epub
|
const epub = pair.epub
|
||||||
const epriv = pair.epriv
|
const epriv = pair.epriv
|
||||||
@ -27,7 +27,7 @@
|
|||||||
SEA.err = e;
|
SEA.err = e;
|
||||||
if(cb){ cb() }
|
if(cb){ cb() }
|
||||||
return;
|
return;
|
||||||
}}
|
}});
|
||||||
|
|
||||||
const keysToEcdhJwk = (pub, d) => { // d === priv
|
const keysToEcdhJwk = (pub, d) => { // d === priv
|
||||||
//const [ x, y ] = Buffer.from(pub, 'base64').toString('utf8').split(':') // old
|
//const [ x, y ] = Buffer.from(pub, 'base64').toString('utf8').split(':') // old
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
|
|
||||||
|
const SEA = require('./root');
|
||||||
const Buffer = require('./buffer')
|
const Buffer = require('./buffer')
|
||||||
const settings = {}
|
const settings = {}
|
||||||
// Encryption parameters
|
// Encryption parameters
|
||||||
@ -35,5 +36,6 @@
|
|||||||
jwk: keysToEcdsaJwk,
|
jwk: keysToEcdsaJwk,
|
||||||
recall: authsettings
|
recall: authsettings
|
||||||
})
|
})
|
||||||
|
SEA.opt = settings;
|
||||||
module.exports = settings
|
module.exports = settings
|
||||||
|
|
10
sea/shim.js
10
sea/shim.js
@ -2,7 +2,7 @@
|
|||||||
const Buffer = require('./buffer')
|
const Buffer = require('./buffer')
|
||||||
const api = {Buffer: Buffer}
|
const api = {Buffer: Buffer}
|
||||||
|
|
||||||
if (typeof __webpack_require__ === 'function' || typeof window !== 'undefined') {
|
if (typeof window !== 'undefined') {
|
||||||
var crypto = window.crypto || window.msCrypto;
|
var crypto = window.crypto || window.msCrypto;
|
||||||
var subtle = crypto.subtle || crypto.webkitSubtle;
|
var subtle = crypto.subtle || crypto.webkitSubtle;
|
||||||
const TextEncoder = window.TextEncoder
|
const TextEncoder = window.TextEncoder
|
||||||
@ -16,9 +16,9 @@
|
|||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
try{
|
try{
|
||||||
var crypto = require('crypto');
|
var crypto = require('crypto', 1);
|
||||||
const { subtle } = require('@trust/webcrypto') // All but ECDH
|
const { subtle } = require('@trust/webcrypto', 1) // All but ECDH
|
||||||
const { TextEncoder, TextDecoder } = require('text-encoding')
|
const { TextEncoder, TextDecoder } = require('text-encoding', 1)
|
||||||
Object.assign(api, {
|
Object.assign(api, {
|
||||||
crypto,
|
crypto,
|
||||||
subtle,
|
subtle,
|
||||||
@ -27,7 +27,7 @@
|
|||||||
random: (len) => Buffer.from(crypto.randomBytes(len))
|
random: (len) => Buffer.from(crypto.randomBytes(len))
|
||||||
});
|
});
|
||||||
//try{
|
//try{
|
||||||
const WebCrypto = require('node-webcrypto-ossl')
|
const WebCrypto = require('node-webcrypto-ossl', 1)
|
||||||
api.ossl = new WebCrypto({directory: 'ossl'}).subtle // ECDH
|
api.ossl = new WebCrypto({directory: 'ossl'}).subtle // ECDH
|
||||||
//}catch(e){
|
//}catch(e){
|
||||||
//console.log("node-webcrypto-ossl is optionally needed for ECDH, please install if needed.");
|
//console.log("node-webcrypto-ossl is optionally needed for ECDH, please install if needed.");
|
||||||
|
10
sea/sign.js
10
sea/sign.js
@ -4,8 +4,8 @@
|
|||||||
var S = require('./settings');
|
var S = require('./settings');
|
||||||
var sha256hash = require('./sha256');
|
var sha256hash = require('./sha256');
|
||||||
|
|
||||||
SEA.sign = async (data, pair, cb) => { try {
|
SEA.sign = SEA.sign || (async (data, pair, cb) => { try {
|
||||||
if(data.slice
|
if(data && data.slice
|
||||||
&& 'SEA{' === data.slice(0,4)
|
&& 'SEA{' === data.slice(0,4)
|
||||||
&& '"m":' === data.slice(4,8)){
|
&& '"m":' === data.slice(4,8)){
|
||||||
// TODO: This would prevent pair2 signing pair1's signature.
|
// TODO: This would prevent pair2 signing pair1's signature.
|
||||||
@ -19,8 +19,8 @@
|
|||||||
const jwk = S.jwk(pub, priv)
|
const jwk = S.jwk(pub, priv)
|
||||||
const msg = JSON.stringify(data)
|
const msg = JSON.stringify(data)
|
||||||
const hash = await sha256hash(msg)
|
const hash = await sha256hash(msg)
|
||||||
const sig = await shim.subtle.importKey('jwk', jwk, S.ecdsa.pair, false, ['sign'])
|
const sig = await (shim.ossl || shim.subtle).importKey('jwk', jwk, S.ecdsa.pair, false, ['sign'])
|
||||||
.then((key) => shim.subtle.sign(S.ecdsa.sign, key, new Uint8Array(hash))) // privateKey scope doesn't leak out from here!
|
.then((key) => (shim.ossl || shim.subtle).sign(S.ecdsa.sign, key, new Uint8Array(hash))) // privateKey scope doesn't leak out from here!
|
||||||
const r = 'SEA'+JSON.stringify({m: msg, s: shim.Buffer.from(sig, 'binary').toString('utf8')});
|
const r = 'SEA'+JSON.stringify({m: msg, s: shim.Buffer.from(sig, 'binary').toString('utf8')});
|
||||||
|
|
||||||
if(cb){ try{ cb(r) }catch(e){console.log(e)} }
|
if(cb){ try{ cb(r) }catch(e){console.log(e)} }
|
||||||
@ -30,7 +30,7 @@
|
|||||||
SEA.err = e;
|
SEA.err = e;
|
||||||
if(cb){ cb() }
|
if(cb){ cb() }
|
||||||
return;
|
return;
|
||||||
}}
|
}});
|
||||||
|
|
||||||
module.exports = SEA.sign;
|
module.exports = SEA.sign;
|
||||||
|
|
@ -6,7 +6,7 @@
|
|||||||
var parse = require('./parse');
|
var parse = require('./parse');
|
||||||
var u;
|
var u;
|
||||||
|
|
||||||
SEA.verify = async (data, pair, cb) => { try {
|
SEA.verify = SEA.verify || (async (data, pair, cb) => { try {
|
||||||
const json = parse(data)
|
const json = parse(data)
|
||||||
if(false === pair){ // don't verify!
|
if(false === pair){ // don't verify!
|
||||||
const raw = (json !== data)?
|
const raw = (json !== data)?
|
||||||
@ -31,7 +31,7 @@
|
|||||||
SEA.err = e;
|
SEA.err = e;
|
||||||
if(cb){ cb() }
|
if(cb){ cb() }
|
||||||
return;
|
return;
|
||||||
}}
|
}});
|
||||||
|
|
||||||
module.exports = SEA.verify;
|
module.exports = SEA.verify;
|
||||||
|
|
45
sea/work.js
45
sea/work.js
@ -2,49 +2,40 @@
|
|||||||
var SEA = require('./root');
|
var SEA = require('./root');
|
||||||
var shim = require('./shim');
|
var shim = require('./shim');
|
||||||
var S = require('./settings');
|
var S = require('./settings');
|
||||||
|
var sha = require('./sha256');
|
||||||
var u;
|
var u;
|
||||||
|
|
||||||
SEA.work = async (data, pair, cb) => { try { // used to be named `proof`
|
SEA.work = SEA.work || (async (data, pair, cb, opt) => { try { // used to be named `proof`
|
||||||
var salt = pair.epub || pair; // epub not recommended, salt should be random!
|
var salt = (pair||{}).epub || pair; // epub not recommended, salt should be random!
|
||||||
|
var opt = opt || {};
|
||||||
if(salt instanceof Function){
|
if(salt instanceof Function){
|
||||||
cb = salt;
|
cb = salt;
|
||||||
salt = u;
|
salt = u;
|
||||||
}
|
}
|
||||||
salt = salt || shim.random(9);
|
salt = salt || shim.random(9);
|
||||||
if (SEA.window) {
|
if('SHA-256' === opt.name){
|
||||||
// For browser subtle works fine
|
var rsha = shim.Buffer.from(await sha(data), 'binary').toString('utf8')
|
||||||
const key = await shim.subtle.importKey(
|
if(cb){ try{ cb(rsha) }catch(e){console.log(e)} }
|
||||||
'raw', new shim.TextEncoder().encode(data), { name: 'PBKDF2' }, false, ['deriveBits']
|
return rsha;
|
||||||
|
}
|
||||||
|
const key = await (shim.ossl || shim.subtle).importKey(
|
||||||
|
'raw', new shim.TextEncoder().encode(data), { name: opt.name || 'PBKDF2' }, false, ['deriveBits']
|
||||||
)
|
)
|
||||||
const result = await shim.subtle.deriveBits({
|
const result = await (shim.ossl || shim.subtle).deriveBits({
|
||||||
name: 'PBKDF2',
|
name: opt.name || 'PBKDF2',
|
||||||
iterations: S.pbkdf2.iter,
|
iterations: opt.iterations || S.pbkdf2.iter,
|
||||||
salt: new shim.TextEncoder().encode(salt),
|
salt: new shim.TextEncoder().encode(opt.salt || salt),
|
||||||
hash: S.pbkdf2.hash,
|
hash: opt.hash || S.pbkdf2.hash,
|
||||||
}, key, S.pbkdf2.ks * 8)
|
}, key, opt.length || (S.pbkdf2.ks * 8))
|
||||||
data = shim.random(data.length) // Erase data in case of passphrase
|
data = shim.random(data.length) // Erase data in case of passphrase
|
||||||
const r = shim.Buffer.from(result, 'binary').toString('utf8')
|
const r = shim.Buffer.from(result, 'binary').toString('utf8')
|
||||||
if(cb){ try{ cb(r) }catch(e){console.log(e)} }
|
if(cb){ try{ cb(r) }catch(e){console.log(e)} }
|
||||||
return r;
|
return r;
|
||||||
}
|
|
||||||
// For NodeJS crypto.pkdf2 rocks
|
|
||||||
const crypto = shim.crypto;
|
|
||||||
const hash = crypto.pbkdf2Sync(
|
|
||||||
data,
|
|
||||||
new shim.TextEncoder().encode(salt),
|
|
||||||
S.pbkdf2.iter,
|
|
||||||
S.pbkdf2.ks,
|
|
||||||
S.pbkdf2.hash.replace('-', '').toLowerCase()
|
|
||||||
)
|
|
||||||
data = shim.random(data.length) // Erase passphrase for app
|
|
||||||
const r = hash && hash.toString('utf8')
|
|
||||||
if(cb){ try{ cb(r) }catch(e){console.log(e)} }
|
|
||||||
return r;
|
|
||||||
} catch(e) {
|
} catch(e) {
|
||||||
SEA.err = e;
|
SEA.err = e;
|
||||||
if(cb){ cb() }
|
if(cb){ cb() }
|
||||||
return;
|
return;
|
||||||
}}
|
}});
|
||||||
|
|
||||||
module.exports = SEA.work;
|
module.exports = SEA.work;
|
||||||
|
|
@ -129,7 +129,7 @@ Gun.on('create', function(root){
|
|||||||
if(data){ disk = data }
|
if(data){ disk = data }
|
||||||
try{store.setItem(opt.prefix, JSON.stringify(disk));
|
try{store.setItem(opt.prefix, JSON.stringify(disk));
|
||||||
}catch(e){
|
}catch(e){
|
||||||
Gun.log(err = e || "localStorage failure");
|
Gun.log(err = (e || "localStorage failure") + " Consider using GUN's IndexedDB plugin for RAD for more storage space, temporary example at https://github.com/amark/gun/blob/master/test/tmp/indexedDB.html .");
|
||||||
root.on('localStorage:error', {err: err, file: opt.prefix, flush: disk, retry: flush});
|
root.on('localStorage:error', {err: err, file: opt.prefix, flush: disk, retry: flush});
|
||||||
}
|
}
|
||||||
if(!err && !Gun.obj.empty(opt.peers)){ return } // only ack if there are no peers.
|
if(!err && !Gun.obj.empty(opt.peers)){ return } // only ack if there are no peers.
|
||||||
|
@ -3,7 +3,10 @@ var Type = require('../type');
|
|||||||
|
|
||||||
function Mesh(ctx){
|
function Mesh(ctx){
|
||||||
var mesh = function(){};
|
var mesh = function(){};
|
||||||
var opt = ctx.opt;
|
var opt = ctx.opt || {};
|
||||||
|
opt.log = opt.log || console.log;
|
||||||
|
opt.gap = opt.gap || opt.wait || 1;
|
||||||
|
opt.pack = opt.pack || (opt.memory? (opt.memory * 1000 * 1000) : 1399000000) * 0.3; // max_old_space_size defaults to 1400 MB.
|
||||||
|
|
||||||
mesh.out = function(msg){ var tmp;
|
mesh.out = function(msg){ var tmp;
|
||||||
if(this.to){ this.to.next(msg) }
|
if(this.to){ this.to.next(msg) }
|
||||||
@ -29,8 +32,9 @@ function Mesh(ctx){
|
|||||||
mesh.hear = function(raw, peer){
|
mesh.hear = function(raw, peer){
|
||||||
if(!raw){ return }
|
if(!raw){ return }
|
||||||
var dup = ctx.dup, id, hash, msg, tmp = raw[0];
|
var dup = ctx.dup, id, hash, msg, tmp = raw[0];
|
||||||
|
if(opt.pack <= raw.length){ return mesh.say({dam: '!', err: "Message too big!"}, peer) }
|
||||||
try{msg = JSON.parse(raw);
|
try{msg = JSON.parse(raw);
|
||||||
}catch(e){console.log('DAM JSON parse error', e)}
|
}catch(e){opt.log('DAM JSON parse error', e)}
|
||||||
if('{' === tmp){
|
if('{' === tmp){
|
||||||
if(!msg){ return }
|
if(!msg){ return }
|
||||||
if(dup.check(id = msg['#'])){ return }
|
if(dup.check(id = msg['#'])){ return }
|
||||||
@ -94,20 +98,25 @@ function Mesh(ctx){
|
|||||||
}
|
}
|
||||||
if((tmp = msh.to) && (tmp[peer.url] || tmp[peer.id]) && !o){ return } // TODO: still needs to be tested
|
if((tmp = msh.to) && (tmp[peer.url] || tmp[peer.id]) && !o){ return } // TODO: still needs to be tested
|
||||||
if(peer.batch){
|
if(peer.batch){
|
||||||
|
peer.tail = (peer.tail || 0) + raw.length;
|
||||||
|
if(peer.tail <= opt.pack){
|
||||||
peer.batch.push(raw);
|
peer.batch.push(raw);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
flush(peer);
|
||||||
|
}
|
||||||
peer.batch = [];
|
peer.batch = [];
|
||||||
setTimeout(function(){
|
setTimeout(function(){flush(peer)}, opt.gap);
|
||||||
var tmp = peer.batch;
|
|
||||||
if(!tmp){ return }
|
|
||||||
peer.batch = null;
|
|
||||||
if(!tmp.length){ return }
|
|
||||||
send(JSON.stringify(tmp), peer);
|
|
||||||
}, opt.gap || opt.wait || 1);
|
|
||||||
send(raw, peer);
|
send(raw, peer);
|
||||||
}
|
}
|
||||||
|
function flush(peer){
|
||||||
|
var tmp = peer.batch;
|
||||||
|
if(!tmp){ return }
|
||||||
|
peer.batch = peer.tail = null;
|
||||||
|
if(!tmp.length){ return }
|
||||||
|
try{send(JSON.stringify(tmp), peer);
|
||||||
|
}catch(e){opt.log('DAM JSON stringify error', e)}
|
||||||
|
}
|
||||||
function send(raw, peer){
|
function send(raw, peer){
|
||||||
var wire = peer.wire;
|
var wire = peer.wire;
|
||||||
try{
|
try{
|
||||||
@ -193,6 +202,7 @@ function Mesh(ctx){
|
|||||||
ctx.on('bye', peer);
|
ctx.on('bye', peer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mesh.hear['!'] = function(msg, peer){ opt.log('Error:', msg.err) }
|
||||||
mesh.hear['?'] = function(msg, peer){
|
mesh.hear['?'] = function(msg, peer){
|
||||||
if(!msg.pid){ return mesh.say({dam: '?', pid: opt.pid, '@': msg['#']}, peer) }
|
if(!msg.pid){ return mesh.say({dam: '?', pid: opt.pid, '@': msg['#']}, peer) }
|
||||||
peer.id = peer.id || msg.pid;
|
peer.id = peer.id || msg.pid;
|
||||||
|
@ -21,7 +21,7 @@ Gun.on('opt', function(root){
|
|||||||
|
|
||||||
var wire = opt.wire;
|
var wire = opt.wire;
|
||||||
opt.wire = open;
|
opt.wire = open;
|
||||||
function open(peer){
|
function open(peer){ try{
|
||||||
if(!peer || !peer.url){ return wire && wire(peer) }
|
if(!peer || !peer.url){ return wire && wire(peer) }
|
||||||
var url = peer.url.replace('http', 'ws');
|
var url = peer.url.replace('http', 'ws');
|
||||||
var wire = peer.wire = new opt.WebSocket(url);
|
var wire = peer.wire = new opt.WebSocket(url);
|
||||||
@ -44,7 +44,7 @@ Gun.on('opt', function(root){
|
|||||||
opt.mesh.hear(msg.data || msg, peer);
|
opt.mesh.hear(msg.data || msg, peer);
|
||||||
};
|
};
|
||||||
return wire;
|
return wire;
|
||||||
}
|
}catch(e){}}
|
||||||
|
|
||||||
function reconnect(peer){
|
function reconnect(peer){
|
||||||
clearTimeout(peer.defer);
|
clearTimeout(peer.defer);
|
||||||
|
@ -16,7 +16,7 @@ function Dup(opt){
|
|||||||
dup.to = setTimeout(function(){
|
dup.to = setTimeout(function(){
|
||||||
var now = time_is();
|
var now = time_is();
|
||||||
Type.obj.map(dup.s, function(it, id){
|
Type.obj.map(dup.s, function(it, id){
|
||||||
if(opt.age > (now - it.was)){ return }
|
if(it && opt.age > (now - it.was)){ return }
|
||||||
Type.obj.del(dup.s, id);
|
Type.obj.del(dup.s, id);
|
||||||
});
|
});
|
||||||
dup.to = null;
|
dup.to = null;
|
||||||
|
@ -84,7 +84,8 @@ function val(msg, eve, to){
|
|||||||
data = tmp.put;
|
data = tmp.put;
|
||||||
}
|
}
|
||||||
if((tmp = eve.wait) && (tmp = tmp[at.id])){ clearTimeout(tmp) }
|
if((tmp = eve.wait) && (tmp = tmp[at.id])){ clearTimeout(tmp) }
|
||||||
if(!to && (u === data || at.soul || at.link || (link && !(0 < link.ack)))){
|
if((!to && (u === data || at.soul || at.link || (link && !(0 < link.ack))))
|
||||||
|
|| (u === data && (tmp = (obj_map(at.root.opt.peers, function(v,k,t){t(k)})||[]).length) && (link||at).ack <= tmp)){
|
||||||
tmp = (eve.wait = {})[at.id] = setTimeout(function(){
|
tmp = (eve.wait = {})[at.id] = setTimeout(function(){
|
||||||
val.call({as:opt}, msg, eve, tmp || 1);
|
val.call({as:opt}, msg, eve, tmp || 1);
|
||||||
}, opt.wait || 99);
|
}, opt.wait || 99);
|
||||||
|
@ -1,16 +1,17 @@
|
|||||||
|
|
||||||
var root;
|
var root;
|
||||||
if(typeof window !== "undefined"){ root = window }
|
if(typeof window !== "undefined"){ root = window }
|
||||||
if(typeof global !== "undefined"){ root = global }
|
if(typeof global !== "undefined"){ root = global }
|
||||||
root = root || {};
|
root = root || {};
|
||||||
var console = root.console || {log: function(){}};
|
var console = root.console || {log: function(){}};
|
||||||
function USE(arg){
|
function USE(arg, req){
|
||||||
return arg.slice? USE[R(arg)] : function(mod, path){
|
return req? require(arg) : arg.slice? USE[R(arg)] : function(mod, path){
|
||||||
arg(mod = {exports: {}});
|
arg(mod = {exports: {}});
|
||||||
USE[R(path)] = mod.exports;
|
USE[R(path)] = mod.exports;
|
||||||
}
|
}
|
||||||
function R(p){
|
function R(p){
|
||||||
return p.split('/').slice(-1).toString().replace('.js','');
|
return p.split('/').slice(-1).toString().replace('.js','');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(typeof module !== "undefined"){ var common = module }
|
if(typeof module !== "undefined"){ var common = module }
|
||||||
|
|
@ -153,11 +153,11 @@ Gun.dup = require('./dup');
|
|||||||
Gun.on.get = function(msg, gun){
|
Gun.on.get = function(msg, gun){
|
||||||
var root = gun._, get = msg.get, soul = get[_soul], node = root.graph[soul], has = get[_has], tmp;
|
var root = gun._, get = msg.get, soul = get[_soul], node = root.graph[soul], has = get[_has], tmp;
|
||||||
var next = root.next || (root.next = {}), at = next[soul];
|
var next = root.next || (root.next = {}), at = next[soul];
|
||||||
if(get['*']){ // TEMPORARY HACK FOR MARTTI, TESTING
|
if(obj_has(soul, '*')){ // TEMPORARY HACK FOR MARTTI, TESTING
|
||||||
var graph = {};
|
var graph = {};
|
||||||
Gun.obj.map(root.graph, function(node, soul){
|
Gun.obj.map(root.graph, function(node, s){
|
||||||
if(Gun.text.match(soul, get)){
|
if(Gun.text.match(s, soul)){
|
||||||
graph[soul] = Gun.obj.copy(node);
|
graph[s] = Gun.obj.copy(node);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
if(!Gun.obj.empty(graph)){
|
if(!Gun.obj.empty(graph)){
|
||||||
|
@ -1,17 +1,25 @@
|
|||||||
var Gun = require('../../');
|
var Gun = require('../../');
|
||||||
|
|
||||||
var data = require('fs').readFileSync('/Users/mark/Downloads/raddataformat.txt');
|
/*var data = '';
|
||||||
|
var a = [], b = Gun.text.random(1000 * 1000 * 10), c;
|
||||||
|
for(var i = 0; i <= 7; i++){
|
||||||
|
data += b;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
data = 1;
|
||||||
|
|
||||||
data = data.toString();
|
var gun = Gun('http://localhost:8765/gun');
|
||||||
data += data + data;
|
//var gun = Gun();
|
||||||
|
|
||||||
console.log(data.length);
|
|
||||||
|
|
||||||
var gun = Gun('http://localhost:8080/gun');
|
|
||||||
|
|
||||||
setTimeout(function(){
|
setTimeout(function(){
|
||||||
|
|
||||||
|
/*console.log("READ!");
|
||||||
|
gun.get('bigsync').get('raw').on(function(a,b){
|
||||||
|
console.log('yay!', b, (a && a.slice && a.slice(0,20)) || a, a.length);
|
||||||
|
});
|
||||||
|
return;*/
|
||||||
console.log("SEND!");
|
console.log("SEND!");
|
||||||
gun.get('bigsync').get('raw').put(data);
|
gun.get('bigsync').get('raw').put(data, function(ack){console.log(ack)});
|
||||||
|
|
||||||
/*var req = require('http').request({
|
/*var req = require('http').request({
|
||||||
host: 'localhost'
|
host: 'localhost'
|
||||||
|
@ -1,18 +1,11 @@
|
|||||||
<h1>RindexedDB</h1>
|
<h1>RindexedDB</h1>
|
||||||
|
|
||||||
<script src="../../examples/jquery.js"></script>
|
|
||||||
<script src="../../gun.js"></script>
|
<script src="../../gun.js"></script>
|
||||||
<script src="../../lib/radix.js"></script>
|
<script src="../../lib/radix.js"></script>
|
||||||
<script src="../../lib/radisk.js"></script>
|
<script src="../../lib/radisk.js"></script>
|
||||||
<script src="../../lib/store.js"></script>
|
<script src="../../lib/store.js"></script>
|
||||||
<script src="../../lib/rindexed.js"></script>
|
<script src="../../lib/rindexed.js"></script>
|
||||||
|
|
||||||
<button onclick="var i = 0; window.TO = setInterval(function(){ gun.get(Gun.text.random(3)).put({a: ++i}) }, 0);">start</button>
|
|
||||||
<button onclick="clearTimeout(window.TO);">end</button>
|
|
||||||
<br/><br/>
|
|
||||||
<button onclick="gun.get('a').put({b: Gun.text.random(900)});setTimeout(function(){gun.get('x').put({y: Gun.text.random(900)});},1000);">write</button>
|
|
||||||
<button id='read' onclick="console.debug.i=1;gun.get('a').once(d => console.log(1, d));gun.get('x').once(d => console.log(2, d));">read</button>
|
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
localStorage.clear();
|
localStorage.clear();
|
||||||
|
|
||||||
|
@ -1,101 +0,0 @@
|
|||||||
<h1>notabug</h1>
|
|
||||||
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/gun/examples/jquery.js"></script>
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/gun@0.9.9992/gun.js"></script>
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/gun@0.9.9992/sea.js"></script>
|
|
||||||
|
|
||||||
<span id='info'></span>
|
|
||||||
|
|
||||||
<h2>homepage</h2>
|
|
||||||
<ul>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<div class="model" style="display: none;">
|
|
||||||
<li id="submission">
|
|
||||||
<a href=""></a>
|
|
||||||
<i></i>
|
|
||||||
</li>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
// utilities jacked from NAB
|
|
||||||
var getDayStr = function (timestamp) {
|
|
||||||
var d = new Date(timestamp || new Date().getTime());
|
|
||||||
var year = d.getUTCFullYear();
|
|
||||||
var month = d.getUTCMonth() + 1;
|
|
||||||
var dayNum = d.getUTCDate();
|
|
||||||
return year + "/" + month + "/" + dayNum;
|
|
||||||
}
|
|
||||||
|
|
||||||
function lastDays(days){ --days;
|
|
||||||
var oneDay = (1000*60*60*24);
|
|
||||||
var start = (new Date()).getTime() - oneDay * parseInt(days, 10);
|
|
||||||
dayStrings = [];
|
|
||||||
for (let i = 0; i <= (days + 1); i++) {
|
|
||||||
dayStrings.push(getDayStr(start + (i * oneDay)));
|
|
||||||
}
|
|
||||||
return dayStrings.reverse();
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
Gun.on('create', function(root){
|
|
||||||
var route = {}, rpc = 'homepage', link = Gun.val.link.is;
|
|
||||||
root.on('get', function(msg){
|
|
||||||
var eve = this, get = msg.get, soul = get['#'], ack, tmp;
|
|
||||||
if(soul !== rpc){
|
|
||||||
return eve.to.next(msg);
|
|
||||||
}
|
|
||||||
console.log('get', msg, route);
|
|
||||||
if(tmp = route[rpc]){
|
|
||||||
console.log("do something!", tmp);
|
|
||||||
ack = true;
|
|
||||||
//return;
|
|
||||||
}
|
|
||||||
var graph = root.graph, index = route[rpc] = {};
|
|
||||||
var days = lastDays(2), pre = 'nab/topics/all/days/';
|
|
||||||
days.forEach(function(day){
|
|
||||||
root.$.get(pre + day).map().get('data').once(function(){});
|
|
||||||
if(!(tmp = graph[pre + day])){ return }
|
|
||||||
index[pre + day] = tmp;
|
|
||||||
});
|
|
||||||
Gun.graph.is(index, null, function(val, key){
|
|
||||||
if(!(key = link(val))){ return }
|
|
||||||
console.log("???", key, val);
|
|
||||||
root.$.get(key).once(function(){});
|
|
||||||
if(!(val = graph[key])){ return }
|
|
||||||
index[key] = val;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<script>;(function(){
|
|
||||||
//localStorage.clear();
|
|
||||||
// benchmark logic
|
|
||||||
var check = {};
|
|
||||||
//var gun = Gun('https://notabug.io/gun');
|
|
||||||
var gun = Gun();
|
|
||||||
window.gun = gun;
|
|
||||||
|
|
||||||
// 'nab/topics/all/days/2018/6/26';
|
|
||||||
var pre = 'nab/topics/all/days/';
|
|
||||||
|
|
||||||
var days = lastDays(2);
|
|
||||||
var start = (+new Date);
|
|
||||||
|
|
||||||
gun.get('homepage').on(function(){});
|
|
||||||
|
|
||||||
days.forEach(function(day){
|
|
||||||
check[pre + day] = true;
|
|
||||||
gun.get(pre + day).map().get('data').once(function(data, key){
|
|
||||||
if(!data || !data.title){ return }
|
|
||||||
$(info).text("loaded in... " + (((+new Date)-start)/1000) +'s.');
|
|
||||||
//console.log('...', key, data);
|
|
||||||
var $li = $(submission).clone(true).attr('id','').appendTo('ul');
|
|
||||||
$li.find('a').attr('src', data.url).text(data.title);
|
|
||||||
$li.find('i').text(data.topic);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
}());</script>
|
|
Loading…
x
Reference in New Issue
Block a user