mirror of
https://github.com/amark/gun.git
synced 2025-07-03 03:12: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>.
|
||||
|
||||
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>
|
||||
<tr>
|
||||
@ -30,7 +30,7 @@ For now, it is best to start with GUN and _just use it_ to learn the basics, sin
|
||||
|
||||
## 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).
|
||||
|
||||
> **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">
|
||||
Thanks to:<br/>
|
||||
<a href="https://github.com/robertheessels">Robert Heessels</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/ddombrow">Daniel Dombrowsky</a>,
|
||||
<a href="http://github.com/vincentwoo">Vincent Woo</a>,
|
||||
<a href="http://github.com/coolaj86">AJ ONeal</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/alanmimms">Alan Mimms</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">
|
||||
|
||||
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
|
||||
|
||||
<table>
|
||||
<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/think.html">Tutorials</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/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>
|
||||
</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>
|
||||
</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://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>
|
||||
<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://github.com/amark/gun/wiki/Modules">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/Auth">Crypto Auth</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://gun.eco/docs/Roadmap">Roadmap</a></h3></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
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!
|
||||
|
||||
|
@ -73,7 +73,6 @@
|
||||
<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/sea.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/gun/lib/open.js"></script>
|
||||
|
||||
<script>
|
||||
//var gun = Gun();
|
||||
@ -104,7 +103,6 @@ gun.on('auth', function(){
|
||||
$('#profile input').on('keyup', function(e){
|
||||
if(!user.is){ return }
|
||||
var id = LI.busy = $(this).attr('id');
|
||||
console.log("Huh?", id, user);
|
||||
user.get('profile').get(id).secret($(this).val());
|
||||
}).on('blur', function(){ LI.busy = false })
|
||||
|
@ -11,16 +11,16 @@
|
||||
|
||||
<form id="said">
|
||||
<input id="say">
|
||||
<input id="speak" type="button" value="speak">
|
||||
<input id="speak" type="submit" value="speak">
|
||||
</form>
|
||||
|
||||
<script src="../../examples/jquery.js"></script>
|
||||
<script src="../../gun.js"></script>
|
||||
<script src="../../sea.js"></script>
|
||||
<script src="../jquery.js"></script>
|
||||
<script src="../../../gun/gun.js"></script>
|
||||
<script src="../../../gun/sea.js"></script>
|
||||
|
||||
<script>
|
||||
var gun = Gun();
|
||||
var user = gun.user();
|
||||
var gun = Gun(); //Gun(['http://localhost:8765/gun', 'https://guntest.herokuapp.com/gun']);
|
||||
var user = gun.user().recall({sessionStorage: true});
|
||||
|
||||
$('#up').on('click', function(e){
|
||||
user.create($('#alias').val(), $('#pass').val());
|
||||
@ -33,7 +33,7 @@ $('#sign').on('submit', function(e){
|
||||
|
||||
gun.on('auth', function(){
|
||||
$('#sign').hide();
|
||||
user.get('said').map().val(UI);
|
||||
user.get('said').map().on(UI);
|
||||
});
|
||||
|
||||
$('#said').on('submit', function(e){
|
@ -1,20 +1,21 @@
|
||||
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')
|
||||
;(function(){
|
||||
var cluster = require('cluster');
|
||||
if(cluster.isMaster){
|
||||
return cluster.fork() && cluster.on('exit', function(){ cluster.fork() });
|
||||
}
|
||||
|
||||
if(process.env.HTTPS_KEY){
|
||||
config.key = fs.readFileSync(process.env.HTTPS_KEY);
|
||||
config.cert = fs.readFileSync(process.env.HTTPS_CERT);
|
||||
config.server = require('https').createServer(config, Gun.serve(__dirname));
|
||||
} else {
|
||||
config.server = require('http').createServer(Gun.serve(__dirname));
|
||||
}
|
||||
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')
|
||||
|
||||
var gun = Gun({
|
||||
web: config.server
|
||||
});
|
||||
if(process.env.HTTPS_KEY){
|
||||
config.key = fs.readFileSync(process.env.HTTPS_KEY);
|
||||
config.cert = fs.readFileSync(process.env.HTTPS_CERT);
|
||||
config.server = require('https').createServer(config, Gun.serve(__dirname));
|
||||
} else {
|
||||
config.server = require('http').createServer(Gun.serve(__dirname));
|
||||
}
|
||||
|
||||
config.server.listen(config.port);
|
||||
console.log('Server started on port ' + config.port + ' with /gun');
|
||||
var gun = Gun({web: config.server.listen(config.port) });
|
||||
console.log('Relay peer started on port ' + config.port + ' with /gun');
|
||||
}());
|
@ -667,26 +667,7 @@
|
||||
</style>
|
||||
<p class="mid black">Hello world!</p>
|
||||
</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>
|
||||
|
||||
$.as.route.page('person', () => {
|
||||
|
@ -329,4 +329,23 @@ ul, li {
|
||||
0% {opacity: 1;}
|
||||
50% {opacity: 0.5;}
|
||||
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;
|
||||
}
|
||||
}
|
79
gun.js
79
gun.js
@ -1,22 +1,22 @@
|
||||
;(function(){
|
||||
|
||||
/* UNBUILD */
|
||||
var root;
|
||||
if(typeof window !== "undefined"){ root = window }
|
||||
if(typeof global !== "undefined"){ root = global }
|
||||
root = root || {};
|
||||
var console = root.console || {log: function(){}};
|
||||
function USE(arg){
|
||||
return arg.slice? USE[R(arg)] : function(mod, path){
|
||||
arg(mod = {exports: {}});
|
||||
USE[R(path)] = mod.exports;
|
||||
}
|
||||
function R(p){
|
||||
return p.split('/').slice(-1).toString().replace('.js','');
|
||||
}
|
||||
}
|
||||
if(typeof module !== "undefined"){ var common = module }
|
||||
/* UNBUILD */
|
||||
/* UNBUILD */
|
||||
var root;
|
||||
if(typeof window !== "undefined"){ root = window }
|
||||
if(typeof global !== "undefined"){ root = global }
|
||||
root = root || {};
|
||||
var console = root.console || {log: function(){}};
|
||||
function USE(arg, req){
|
||||
return req? require(arg) : arg.slice? USE[R(arg)] : function(mod, path){
|
||||
arg(mod = {exports: {}});
|
||||
USE[R(path)] = mod.exports;
|
||||
}
|
||||
function R(p){
|
||||
return p.split('/').slice(-1).toString().replace('.js','');
|
||||
}
|
||||
}
|
||||
if(typeof module !== "undefined"){ var common = module }
|
||||
/* UNBUILD */
|
||||
|
||||
;USE(function(module){
|
||||
// Generic javascript utilities.
|
||||
@ -638,7 +638,7 @@
|
||||
dup.to = setTimeout(function(){
|
||||
var now = time_is();
|
||||
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);
|
||||
});
|
||||
dup.to = null;
|
||||
@ -1661,7 +1661,8 @@
|
||||
data = tmp.put;
|
||||
}
|
||||
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(){
|
||||
val.call({as:opt}, msg, eve, tmp || 1);
|
||||
}, opt.wait || 99);
|
||||
@ -1901,7 +1902,7 @@
|
||||
if(data){ disk = data }
|
||||
try{store.setItem(opt.prefix, JSON.stringify(disk));
|
||||
}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});
|
||||
}
|
||||
if(!err && !Gun.obj.empty(opt.peers)){ return } // only ack if there are no peers.
|
||||
@ -1921,7 +1922,10 @@
|
||||
|
||||
function Mesh(ctx){
|
||||
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;
|
||||
if(this.to){ this.to.next(msg) }
|
||||
@ -1947,8 +1951,9 @@
|
||||
mesh.hear = function(raw, peer){
|
||||
if(!raw){ return }
|
||||
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);
|
||||
}catch(e){console.log('DAM JSON parse error', e)}
|
||||
}catch(e){opt.log('DAM JSON parse error', e)}
|
||||
if('{' === tmp){
|
||||
if(!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(peer.batch){
|
||||
peer.batch.push(raw);
|
||||
return;
|
||||
peer.tail = (peer.tail || 0) + raw.length;
|
||||
if(peer.tail <= opt.pack){
|
||||
peer.batch.push(raw);
|
||||
return;
|
||||
}
|
||||
flush(peer);
|
||||
}
|
||||
peer.batch = [];
|
||||
setTimeout(function(){
|
||||
var tmp = peer.batch;
|
||||
if(!tmp){ return }
|
||||
peer.batch = null;
|
||||
if(!tmp.length){ return }
|
||||
send(JSON.stringify(tmp), peer);
|
||||
}, opt.gap || opt.wait || 1);
|
||||
setTimeout(function(){flush(peer)}, opt.gap);
|
||||
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){
|
||||
var wire = peer.wire;
|
||||
try{
|
||||
@ -2111,6 +2121,7 @@
|
||||
ctx.on('bye', peer);
|
||||
}
|
||||
|
||||
mesh.hear['!'] = function(msg, peer){ opt.log('Error:', msg.err) }
|
||||
mesh.hear['?'] = function(msg, peer){
|
||||
if(!msg.pid){ return mesh.say({dam: '?', pid: opt.pid, '@': msg['#']}, peer) }
|
||||
peer.id = peer.id || msg.pid;
|
||||
@ -2162,7 +2173,7 @@
|
||||
|
||||
var wire = opt.wire;
|
||||
opt.wire = open;
|
||||
function open(peer){
|
||||
function open(peer){ try{
|
||||
if(!peer || !peer.url){ return wire && wire(peer) }
|
||||
var url = peer.url.replace('http', 'ws');
|
||||
var wire = peer.wire = new opt.WebSocket(url);
|
||||
@ -2185,7 +2196,7 @@
|
||||
opt.mesh.hear(msg.data || msg, peer);
|
||||
};
|
||||
return wire;
|
||||
}
|
||||
}catch(e){}}
|
||||
|
||||
function reconnect(peer){
|
||||
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;
|
||||
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);
|
||||
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){
|
||||
|
||||
opt = opt || {};
|
||||
opt.log = opt.log || console.log;
|
||||
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.batch = opt.batch || 10 * 1000;
|
||||
opt.chunk = opt.chunk || (1024 * 1024 * 10); // 10MB
|
||||
@ -14,16 +16,16 @@
|
||||
var map = Gun.obj.map;
|
||||
|
||||
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){
|
||||
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){
|
||||
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){
|
||||
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.save(batch, function(err, ok){
|
||||
if(++i > 1){ return }
|
||||
if(err){ Gun.log('err', err) }
|
||||
if(err){ opt.log('err', err) }
|
||||
map(batch.acks, function(cb){ cb(err, ok) });
|
||||
thrash.at = null;
|
||||
thrash.ing = false;
|
||||
@ -141,6 +143,7 @@
|
||||
f.file = file;
|
||||
f.each = function(val, key, k, pre){
|
||||
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';
|
||||
if((opt.chunk < f.text.length + enc.length) && (1 < f.count) && !force){
|
||||
f.text = '';
|
||||
@ -206,7 +209,7 @@
|
||||
g.file = file;
|
||||
}
|
||||
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 }
|
||||
disk = Q[g.file]; delete Q[g.file];
|
||||
map(disk, g.ack);
|
||||
@ -243,6 +246,16 @@
|
||||
//return cb(err, u);//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;
|
||||
while(tmp){
|
||||
k = v = u;
|
||||
@ -309,7 +322,7 @@
|
||||
}
|
||||
r.list.init = function(err, disk){
|
||||
if(err){
|
||||
Gun.log('list', err);
|
||||
opt.log('list', err);
|
||||
setTimeout(function(){ r.parse(f, r.list.init) }, 1000);
|
||||
return;
|
||||
}
|
||||
|
@ -49,7 +49,7 @@
|
||||
return radix;
|
||||
};
|
||||
|
||||
Radix.map = function map(radix, cb, opt, pre){ pre = pre || [];
|
||||
Radix.map = function map(radix, cb, opt, pre){ pre = pre || [];
|
||||
var t = radix[_] || radix, keys = radix.sort || (radix.sort = Object.keys(t).sort()), i = 0, l = keys.length;
|
||||
for(;i < l; i++){ var key = keys[i], tree = t[key], tmp;
|
||||
if(u !== (tmp = tree[$])){
|
||||
|
@ -22,7 +22,6 @@ function Store(opt){
|
||||
}
|
||||
Gun.log("ERROR:", err)
|
||||
}
|
||||
if(data){ data = data.toString() }
|
||||
cb(err, data);
|
||||
});
|
||||
};
|
||||
|
@ -62,7 +62,7 @@ function Store(opt){
|
||||
if(!ack){ cb(null); return; }
|
||||
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);
|
||||
});
|
||||
};
|
||||
|
@ -4,9 +4,10 @@ var path = require('path');
|
||||
function CDN(dir){
|
||||
return function(req, res){
|
||||
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.end(fs.readFileSync(path.join(dir, 'index.html'))); // or default to index
|
||||
res.end(tmp+''); // or default to index
|
||||
}).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;
|
||||
rad(tmp, function(err, val){
|
||||
if(val){
|
||||
Radix.map(val, each);
|
||||
if(val && typeof val !== 'string'){ Radix.map(val, each) }
|
||||
if(!node){ each(val, key) }
|
||||
}
|
||||
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);
|
||||
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.on('connection', function(wire){ var peer;
|
||||
wire.upgradeReq = wire.upgradeReq || {};
|
||||
|
47
package-lock.json
generated
47
package-lock.json
generated
@ -1,28 +1,29 @@
|
||||
{
|
||||
"name": "gun",
|
||||
"version": "0.9.99992",
|
||||
"version": "0.9.99995",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
"@trust/keyto": {
|
||||
"version": "0.3.2",
|
||||
"resolved": "https://registry.npmjs.org/@trust/keyto/-/keyto-0.3.2.tgz",
|
||||
"integrity": "sha512-ywlelg2ePNpX4IlN+A3qXySzKBAZmI2ZxMdDL3amJLCTYhYhemYcv6Aa+PTETojUfB+k4z4X4970q/jjSzyLvw==",
|
||||
"version": "0.3.4",
|
||||
"resolved": "https://registry.npmjs.org/@trust/keyto/-/keyto-0.3.4.tgz",
|
||||
"integrity": "sha512-OAqKvuSEPIu2zCnIHzBthvGnV8nKmpv7cBlRMngLzJZzZI9CanyuSfnEI1xC4sH4TwqA0XJR7Mb0oX4bwymXIw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"asn1.js": "^4.9.1",
|
||||
"base64url": "^2.0.0",
|
||||
"base64url": "^3.0.0",
|
||||
"elliptic": "^6.4.0"
|
||||
}
|
||||
},
|
||||
"@trust/webcrypto": {
|
||||
"version": "0.7.1",
|
||||
"resolved": "https://registry.npmjs.org/@trust/webcrypto/-/webcrypto-0.7.1.tgz",
|
||||
"integrity": "sha512-aix+LOG/3Ku3MzClfVxVH88QbSdIL1HcBQ+gjXL/VnX05uyORf28CaQZOvsoEcCzGnWIVBUNwE2gxLBapWANWw==",
|
||||
"version": "0.9.2",
|
||||
"resolved": "https://registry.npmjs.org/@trust/webcrypto/-/webcrypto-0.9.2.tgz",
|
||||
"integrity": "sha512-5iMAVcGYKhqLJGjefB1nzuQSqUJTru0nG4CytpBT/GGp1Piz/MVnj2jORdYf4JBYzggCIa8WZUr2rchP2Ngn/w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@trust/keyto": "^0.3.1",
|
||||
"base64url": "^2.0.0",
|
||||
"@trust/keyto": "^0.3.4",
|
||||
"base64url": "^3.0.0",
|
||||
"elliptic": "^6.4.0",
|
||||
"node-rsa": "^0.4.0",
|
||||
"text-encoding": "^0.6.1"
|
||||
}
|
||||
@ -126,9 +127,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"base64url": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/base64url/-/base64url-2.0.0.tgz",
|
||||
"integrity": "sha1-6sFuA+oUOO/5Qj1puqNiYu0fcLs=",
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/base64url/-/base64url-3.0.0.tgz",
|
||||
"integrity": "sha512-LIVmqIrIWuiqTvn4RzcrwCOuHo2DD6tKmKBPXXlr4p4n4l6BZBkwFTIa3zu1XkX5MbZgro4a6BvPi+n2Mns5Gg==",
|
||||
"dev": true
|
||||
},
|
||||
"better-assert": {
|
||||
@ -309,9 +310,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"elliptic": {
|
||||
"version": "6.4.0",
|
||||
"resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.4.0.tgz",
|
||||
"integrity": "sha1-ysmvh2LIWDYYcAPI3+GT5eLq5d8=",
|
||||
"version": "6.4.1",
|
||||
"resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.4.1.tgz",
|
||||
"integrity": "sha512-BsXLz5sqX8OHcsh7CqBMztyXARmGQ3LWPtGjJi6DiJHq5C/qvi9P3OqgswKSDftbu8+IoI/QDTAm2fFnQ9SZSQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"bn.js": "^4.4.0",
|
||||
@ -606,13 +607,13 @@
|
||||
"dev": true
|
||||
},
|
||||
"hash.js": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.3.tgz",
|
||||
"integrity": "sha512-/UETyP0W22QILqS+6HowevwhEFJ3MBJnwTf75Qob9Wz9t0DPuisL8kW8YZMK62dHAKE1c1p+gY1TtOLY+USEHA==",
|
||||
"version": "1.1.5",
|
||||
"resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.5.tgz",
|
||||
"integrity": "sha512-eWI5HG9Np+eHV1KQhisXWwM+4EPPYe5dFX1UZZH7k/E3JzDEazVH+VGlZi6R94ZqImq+A3D1mCEtrFIfg/E7sA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"inherits": "^2.0.3",
|
||||
"minimalistic-assert": "^1.0.0"
|
||||
"minimalistic-assert": "^1.0.1"
|
||||
}
|
||||
},
|
||||
"he": {
|
||||
@ -853,9 +854,9 @@
|
||||
}
|
||||
},
|
||||
"node-webcrypto-ossl": {
|
||||
"version": "1.0.37",
|
||||
"resolved": "https://registry.npmjs.org/node-webcrypto-ossl/-/node-webcrypto-ossl-1.0.37.tgz",
|
||||
"integrity": "sha512-AQSux10u8NoUhRPqb2bapqM8EKMawKGYIBbGNWsBUTeq0uXYtuIheujcaJo5XY1Yy6HffC/fP7AHHtSA4KP2ig==",
|
||||
"version": "1.0.38",
|
||||
"resolved": "https://registry.npmjs.org/node-webcrypto-ossl/-/node-webcrypto-ossl-1.0.38.tgz",
|
||||
"integrity": "sha512-UiQcDiBDNzaZbP0WVgz4QvVTVI4uR4jrFAtOtFsKbDDNOMFWc9+3mVeiF1hVvdLlv3ILC0ODgs8Wp/hp7SMoLA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"mkdirp": "^0.5.1",
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "gun",
|
||||
"version": "0.9.99993",
|
||||
"version": "0.9.99995",
|
||||
"description": "A realtime, decentralized, offline-first, graph data synchronization engine.",
|
||||
"main": "index.js",
|
||||
"browser": "gun.min.js",
|
||||
@ -51,13 +51,13 @@
|
||||
"ws": "~>5.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@trust/webcrypto": "^0.7.1",
|
||||
"node-webcrypto-ossl": "^1.0.37",
|
||||
"@trust/webcrypto": "^0.9.2",
|
||||
"aws-sdk": ">=2.153.0",
|
||||
"concat-map": "^0.0.1",
|
||||
"express": ">=4.15.2",
|
||||
"ip": "^1.1.5",
|
||||
"concat-map": "^0.0.1",
|
||||
"mocha": ">=3.2.0",
|
||||
"node-webcrypto-ossl": "^1.0.38",
|
||||
"panic-manager": "^1.2.0",
|
||||
"panic-server": "^1.1.1",
|
||||
"text-encoding": "^0.6.4",
|
||||
|
192
sea.js
192
sea.js
@ -6,8 +6,8 @@
|
||||
if(typeof global !== "undefined"){ root = global }
|
||||
root = root || {};
|
||||
var console = root.console || {log: function(){}};
|
||||
function USE(arg){
|
||||
return arg.slice? USE[R(arg)] : function(mod, path){
|
||||
function USE(arg, req){
|
||||
return req? require(arg) : arg.slice? USE[R(arg)] : function(mod, path){
|
||||
arg(mod = {exports: {}});
|
||||
USE[R(path)] = mod.exports;
|
||||
}
|
||||
@ -20,12 +20,24 @@
|
||||
|
||||
;USE(function(module){
|
||||
// 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!
|
||||
|
||||
function SEA(){}
|
||||
if(typeof window !== "undefined"){ (SEA.window = window).SEA = SEA }
|
||||
if(typeof window !== "undefined"){ module.window = window }
|
||||
|
||||
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;
|
||||
})(USE, './root');
|
||||
|
||||
@ -148,7 +160,7 @@
|
||||
const Buffer = USE('./buffer')
|
||||
const api = {Buffer: Buffer}
|
||||
|
||||
if (typeof __webpack_require__ === 'function' || typeof window !== 'undefined') {
|
||||
if (typeof window !== 'undefined') {
|
||||
var crypto = window.crypto || window.msCrypto;
|
||||
var subtle = crypto.subtle || crypto.webkitSubtle;
|
||||
const TextEncoder = window.TextEncoder
|
||||
@ -162,9 +174,9 @@
|
||||
})
|
||||
} else {
|
||||
try{
|
||||
var crypto = require('crypto');
|
||||
const { subtle } = require('@trust/webcrypto') // All but ECDH
|
||||
const { TextEncoder, TextDecoder } = require('text-encoding')
|
||||
var crypto = USE('crypto', 1);
|
||||
const { subtle } = USE('@trust/webcrypto', 1) // All but ECDH
|
||||
const { TextEncoder, TextDecoder } = USE('text-encoding', 1)
|
||||
Object.assign(api, {
|
||||
crypto,
|
||||
subtle,
|
||||
@ -173,7 +185,7 @@
|
||||
random: (len) => Buffer.from(crypto.randomBytes(len))
|
||||
});
|
||||
//try{
|
||||
const WebCrypto = require('node-webcrypto-ossl')
|
||||
const WebCrypto = USE('node-webcrypto-ossl', 1)
|
||||
api.ossl = new WebCrypto({directory: 'ossl'}).subtle // ECDH
|
||||
//}catch(e){
|
||||
//console.log("node-webcrypto-ossl is optionally needed for ECDH, please install if needed.");
|
||||
@ -189,6 +201,7 @@
|
||||
})(USE, './shim');
|
||||
|
||||
;USE(function(module){
|
||||
const SEA = USE('./root');
|
||||
const Buffer = USE('./buffer')
|
||||
const settings = {}
|
||||
// Encryption parameters
|
||||
@ -225,6 +238,7 @@
|
||||
jwk: keysToEcdsaJwk,
|
||||
recall: authsettings
|
||||
})
|
||||
SEA.opt = settings;
|
||||
module.exports = settings
|
||||
})(USE, './settings');
|
||||
|
||||
@ -267,49 +281,40 @@
|
||||
var SEA = USE('./root');
|
||||
var shim = USE('./shim');
|
||||
var S = USE('./settings');
|
||||
var sha = USE('./sha256');
|
||||
var u;
|
||||
|
||||
SEA.work = async (data, pair, cb) => { try { // used to be named `proof`
|
||||
var salt = pair.epub || pair; // epub not recommended, salt should be random!
|
||||
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 opt = opt || {};
|
||||
if(salt instanceof Function){
|
||||
cb = salt;
|
||||
salt = u;
|
||||
}
|
||||
salt = salt || shim.random(9);
|
||||
if (SEA.window) {
|
||||
// For browser subtle works fine
|
||||
const key = await shim.subtle.importKey(
|
||||
'raw', new shim.TextEncoder().encode(data), { name: 'PBKDF2' }, false, ['deriveBits']
|
||||
)
|
||||
const result = await shim.subtle.deriveBits({
|
||||
name: 'PBKDF2',
|
||||
iterations: S.pbkdf2.iter,
|
||||
salt: new shim.TextEncoder().encode(salt),
|
||||
hash: S.pbkdf2.hash,
|
||||
}, key, S.pbkdf2.ks * 8)
|
||||
data = shim.random(data.length) // Erase data in case of passphrase
|
||||
const r = shim.Buffer.from(result, 'binary').toString('utf8')
|
||||
if(cb){ try{ cb(r) }catch(e){console.log(e)} }
|
||||
return r;
|
||||
if('SHA-256' === opt.name){
|
||||
var rsha = shim.Buffer.from(await sha(data), 'binary').toString('utf8')
|
||||
if(cb){ try{ cb(rsha) }catch(e){console.log(e)} }
|
||||
return rsha;
|
||||
}
|
||||
// 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()
|
||||
const key = await (shim.ossl || shim.subtle).importKey(
|
||||
'raw', new shim.TextEncoder().encode(data), { name: opt.name || 'PBKDF2' }, false, ['deriveBits']
|
||||
)
|
||||
data = shim.random(data.length) // Erase passphrase for app
|
||||
const r = hash && hash.toString('utf8')
|
||||
const result = await (shim.ossl || shim.subtle).deriveBits({
|
||||
name: opt.name || 'PBKDF2',
|
||||
iterations: opt.iterations || S.pbkdf2.iter,
|
||||
salt: new shim.TextEncoder().encode(opt.salt || salt),
|
||||
hash: opt.hash || S.pbkdf2.hash,
|
||||
}, key, opt.length || (S.pbkdf2.ks * 8))
|
||||
data = shim.random(data.length) // Erase data in case of passphrase
|
||||
const r = shim.Buffer.from(result, 'binary').toString('utf8')
|
||||
if(cb){ try{ cb(r) }catch(e){console.log(e)} }
|
||||
return r;
|
||||
} catch(e) {
|
||||
SEA.err = e;
|
||||
if(cb){ cb() }
|
||||
return;
|
||||
}}
|
||||
}});
|
||||
|
||||
module.exports = SEA.work;
|
||||
})(USE, './work');
|
||||
@ -321,7 +326,7 @@
|
||||
var Buff = (typeof Buffer !== 'undefined')? Buffer : shim.Buffer;
|
||||
|
||||
//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
|
||||
// First: ECDSA keys for signing/verifying...
|
||||
@ -372,7 +377,7 @@
|
||||
SEA.err = e;
|
||||
if(cb){ cb() }
|
||||
return;
|
||||
}}
|
||||
}});
|
||||
|
||||
module.exports = SEA.pair;
|
||||
})(USE, './pair');
|
||||
@ -383,7 +388,7 @@
|
||||
var S = USE('./settings');
|
||||
var sha256hash = USE('./sha256');
|
||||
|
||||
SEA.sign = async (data, pair, cb) => { try {
|
||||
SEA.sign = SEA.sign || (async (data, pair, cb) => { try {
|
||||
if(data && data.slice
|
||||
&& 'SEA{' === data.slice(0,4)
|
||||
&& '"m":' === data.slice(4,8)){
|
||||
@ -409,7 +414,7 @@
|
||||
SEA.err = e;
|
||||
if(cb){ cb() }
|
||||
return;
|
||||
}}
|
||||
}});
|
||||
|
||||
module.exports = SEA.sign;
|
||||
})(USE, './sign');
|
||||
@ -422,7 +427,7 @@
|
||||
var parse = USE('./parse');
|
||||
var u;
|
||||
|
||||
SEA.verify = async (data, pair, cb) => { try {
|
||||
SEA.verify = SEA.verify || (async (data, pair, cb) => { try {
|
||||
const json = parse(data)
|
||||
if(false === pair){ // don't verify!
|
||||
const raw = (json !== data)?
|
||||
@ -447,7 +452,7 @@
|
||||
SEA.err = e;
|
||||
if(cb){ cb() }
|
||||
return;
|
||||
}}
|
||||
}});
|
||||
|
||||
module.exports = SEA.verify;
|
||||
})(USE, './verify');
|
||||
@ -472,13 +477,13 @@
|
||||
var S = USE('./settings');
|
||||
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 || {};
|
||||
const key = pair.epriv || pair;
|
||||
const msg = JSON.stringify(data)
|
||||
const rand = {s: shim.random(8), iv: shim.random(16)};
|
||||
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)
|
||||
}, aes, new shim.TextEncoder().encode(msg)))
|
||||
const r = 'SEA'+JSON.stringify({
|
||||
@ -493,7 +498,7 @@
|
||||
SEA.err = e;
|
||||
if(cb){ cb() }
|
||||
return;
|
||||
}}
|
||||
}});
|
||||
|
||||
module.exports = SEA.encrypt;
|
||||
})(USE, './encrypt');
|
||||
@ -505,23 +510,22 @@
|
||||
var aeskey = USE('./aeskey');
|
||||
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 || {};
|
||||
const key = pair.epriv || pair;
|
||||
const json = parse(data)
|
||||
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'))
|
||||
}, aes, new Uint8Array(shim.Buffer.from(json.ct, 'utf8'))))
|
||||
const r = parse(new shim.TextDecoder('utf8').decode(ct))
|
||||
|
||||
if(cb){ try{ cb(r) }catch(e){console.log(e)} }
|
||||
return r;
|
||||
} catch(e) {
|
||||
SEA.err = e;
|
||||
if(cb){ cb() }
|
||||
return;
|
||||
}}
|
||||
}});
|
||||
|
||||
module.exports = SEA.decrypt;
|
||||
})(USE, './decrypt');
|
||||
@ -531,7 +535,7 @@
|
||||
var shim = USE('./shim');
|
||||
var S = USE('./settings');
|
||||
// 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 epub = pair.epub
|
||||
const epriv = pair.epriv
|
||||
@ -555,7 +559,7 @@
|
||||
SEA.err = e;
|
||||
if(cb){ cb() }
|
||||
return;
|
||||
}}
|
||||
}});
|
||||
|
||||
const keysToEcdhJwk = (pub, d) => { // d === priv
|
||||
//const [ x, y ] = Buffer.from(pub, 'base64').toString('utf8').split(':') // old
|
||||
@ -605,7 +609,7 @@
|
||||
SEA.encrypt = USE('./encrypt');
|
||||
SEA.decrypt = USE('./decrypt');
|
||||
|
||||
SEA.random = getRandomBytes;
|
||||
SEA.random = SEA.random || getRandomBytes;
|
||||
|
||||
// This is easy way to use IndexedDB, all methods are Promises
|
||||
// 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.
|
||||
// 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
|
||||
// async/await (compatible) code, use those like Promises.
|
||||
@ -621,7 +625,7 @@
|
||||
// Creates a wrapper library around Web Crypto API
|
||||
// for various AES, ECDSA, PBKDF2 functions we called above.
|
||||
// Calculate public key KeyID aka PGPv4 (result: 8 bytes as hex string)
|
||||
SEA.keyid = async (pub) => {
|
||||
SEA.keyid = SEA.keyid || (async (pub) => {
|
||||
try {
|
||||
// base64('base64(x):base64(y)') => Buffer(xy)
|
||||
const pb = Buffer.concat(
|
||||
@ -639,7 +643,7 @@
|
||||
console.log(e)
|
||||
throw e
|
||||
}
|
||||
}
|
||||
});
|
||||
// all done!
|
||||
// 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.
|
||||
@ -649,7 +653,7 @@
|
||||
// But all other behavior needs to be equally easy, like opinionated ways of
|
||||
// Adding friends (trusted public keys), sending private messages, etc.
|
||||
// Cheers! Tell me what you think.
|
||||
var Gun = (SEA.window||{}).Gun || require('./gun');
|
||||
var Gun = (SEA.window||{}).Gun || USE('./gun', 1);
|
||||
Gun.SEA = SEA;
|
||||
SEA.Gun = Gun;
|
||||
|
||||
@ -662,9 +666,9 @@
|
||||
// This is internal func queries public key(s) for alias.
|
||||
const queryGunAliases = (alias, gunRoot) => new Promise((resolve, reject) => {
|
||||
// load all public keys associated with the username alias we want to log in with.
|
||||
gunRoot.get('~@'+alias).get((rat, rev) => {
|
||||
rev.off();
|
||||
if (!rat.put) {
|
||||
gunRoot.get('~@'+alias).once((data, key) => {
|
||||
//rev.off();
|
||||
if (!data) {
|
||||
// if no user, don't do anything.
|
||||
const err = 'No user!'
|
||||
Gun.log(err)
|
||||
@ -674,19 +678,18 @@
|
||||
const aliases = []
|
||||
let c = 0
|
||||
// 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)) {
|
||||
// TODO: ... this would then be .filter((at, pub))
|
||||
return
|
||||
}
|
||||
++c
|
||||
// grab the account associated with this public key.
|
||||
gunRoot.get(pub).get((at, ev) => {
|
||||
gunRoot.get(pub).once(data => {
|
||||
pub = pub.slice(1)
|
||||
ev.off()
|
||||
--c
|
||||
if (at.put){
|
||||
aliases.push({ pub, at })
|
||||
if (data){
|
||||
aliases.push({ pub, put: data })
|
||||
}
|
||||
if (!c && (c = -1)) {
|
||||
resolve(aliases)
|
||||
@ -710,7 +713,7 @@
|
||||
const authenticate = async (alias, pass, gunRoot) => {
|
||||
// load all public keys associated with the username alias we want to log in with.
|
||||
const aliases = (await queryGunAliases(alias, gunRoot))
|
||||
.filter(({ pub, at: { put } = {} } = {}) => !!pub && !!put)
|
||||
.filter(a => !!a.pub && !!a.put)
|
||||
// Got any?
|
||||
if (!aliases.length) {
|
||||
throw { err: 'Public key does not exist!' }
|
||||
@ -718,14 +721,14 @@
|
||||
let err
|
||||
// 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)
|
||||
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.)
|
||||
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.
|
||||
// SEA.verify(at.put.auth, pub).then(function(auth){
|
||||
try {
|
||||
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.
|
||||
/*
|
||||
MARK TO @mhelander : pub vs epub!???
|
||||
@ -733,24 +736,24 @@
|
||||
const salt = auth.salt
|
||||
const sea = await SEA.decrypt(auth.ek, proof)
|
||||
if (!sea) {
|
||||
err = 'Failed to decrypt secret! ' + i +'/'+aliases.length;
|
||||
err = 'Failed to decrypt secret! ' + (i+1) +'/'+aliases.length;
|
||||
return
|
||||
}
|
||||
// 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!
|
||||
const priv = sea.priv
|
||||
const epriv = sea.epriv
|
||||
const epub = at.put.epub
|
||||
const epub = a.put.epub
|
||||
// TODO: 'salt' needed?
|
||||
err = null
|
||||
if(typeof window !== 'undefined'){
|
||||
var tmp = window.sessionStorage;
|
||||
if(SEA.window){
|
||||
var tmp = SEA.window.sessionStorage;
|
||||
if(tmp && gunRoot._.opt.remember){
|
||||
window.sessionStorage.alias = alias;
|
||||
window.sessionStorage.tmp = pass;
|
||||
SEA.window.sessionStorage.alias = alias;
|
||||
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) {
|
||||
err = 'Failed to decrypt secret!'
|
||||
throw { err }
|
||||
@ -862,10 +865,25 @@
|
||||
const finalizeLogin = async (alias, key, gunRoot, opts) => {
|
||||
const user = gunRoot._.user
|
||||
// add our credentials in-memory only to our root gun instance
|
||||
//var tmp = user._.tag;
|
||||
var tmp = user._.tag;
|
||||
var opt = user._.opt;
|
||||
user._ = key.at.$._;
|
||||
user._ = gunRoot.get('~'+key.pub)._;
|
||||
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;
|
||||
// so that way we can use the credentials to encrypt/decrypt data
|
||||
// that is input/output through gun (see below)
|
||||
@ -880,7 +898,8 @@
|
||||
//await authPersist(user._, key.proof, opts) // temporarily disabled
|
||||
// emit an auth event, useful for page redirects and stuff.
|
||||
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) {
|
||||
console.log('Your \'auth\' callback crashed with:', e)
|
||||
}
|
||||
@ -1300,7 +1319,8 @@
|
||||
}
|
||||
// If authentication is to be remembered over reloads or browser closing,
|
||||
// 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)
|
||||
|
||||
let validity
|
||||
@ -1317,7 +1337,7 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
return this;
|
||||
return gun;
|
||||
}
|
||||
|
||||
if (!Gun.val.is(setvalidity)) {
|
||||
@ -1340,13 +1360,15 @@
|
||||
authsettings.hook = (Gun.obj.has(opts, 'hook') && typeof opts.hook === 'function')
|
||||
? opts.hook : _initial_authsettings.hook
|
||||
// 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) {
|
||||
const err = 'No session!'
|
||||
Gun.log(err)
|
||||
// NOTE! It's fine to resolve recall with reason why not successful
|
||||
// instead of rejecting...
|
||||
return { err: (e && e.err) || err }
|
||||
//return { err: (e && e.err) || err }
|
||||
return gun;
|
||||
}
|
||||
}
|
||||
User.prototype.alive = async function(){
|
||||
@ -1563,7 +1585,7 @@
|
||||
if(tmp = relpub(soul)){
|
||||
check['any'+soul+key] = 1;
|
||||
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)){
|
||||
(at.sea.own[rel] = at.sea.own[rel] || {})[pub] = true;
|
||||
}
|
||||
|
@ -7,7 +7,7 @@
|
||||
const authenticate = async (alias, pass, gunRoot) => {
|
||||
// load all public keys associated with the username alias we want to log in with.
|
||||
const aliases = (await queryGunAliases(alias, gunRoot))
|
||||
.filter(({ pub, at: { put } = {} } = {}) => !!pub && !!put)
|
||||
.filter(a => !!a.pub && !!a.put)
|
||||
// Got any?
|
||||
if (!aliases.length) {
|
||||
throw { err: 'Public key does not exist!' }
|
||||
@ -15,14 +15,14 @@
|
||||
let err
|
||||
// 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)
|
||||
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.)
|
||||
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.
|
||||
// SEA.verify(at.put.auth, pub).then(function(auth){
|
||||
try {
|
||||
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.
|
||||
/*
|
||||
MARK TO @mhelander : pub vs epub!???
|
||||
@ -30,24 +30,24 @@
|
||||
const salt = auth.salt
|
||||
const sea = await SEA.decrypt(auth.ek, proof)
|
||||
if (!sea) {
|
||||
err = 'Failed to decrypt secret! ' + i +'/'+aliases.length;
|
||||
err = 'Failed to decrypt secret! ' + (i+1) +'/'+aliases.length;
|
||||
return
|
||||
}
|
||||
// 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!
|
||||
const priv = sea.priv
|
||||
const epriv = sea.epriv
|
||||
const epub = at.put.epub
|
||||
const epub = a.put.epub
|
||||
// TODO: 'salt' needed?
|
||||
err = null
|
||||
if(typeof window !== 'undefined'){
|
||||
var tmp = window.sessionStorage;
|
||||
if(SEA.window){
|
||||
var tmp = SEA.window.sessionStorage;
|
||||
if(tmp && gunRoot._.opt.remember){
|
||||
window.sessionStorage.alias = alias;
|
||||
window.sessionStorage.tmp = pass;
|
||||
SEA.window.sessionStorage.alias = alias;
|
||||
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) {
|
||||
err = 'Failed to decrypt secret!'
|
||||
throw { err }
|
||||
|
@ -202,7 +202,8 @@
|
||||
}
|
||||
// If authentication is to be remembered over reloads or browser closing,
|
||||
// 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)
|
||||
|
||||
let validity
|
||||
@ -219,7 +220,7 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
return this;
|
||||
return gun;
|
||||
}
|
||||
|
||||
if (!Gun.val.is(setvalidity)) {
|
||||
@ -242,13 +243,15 @@
|
||||
authsettings.hook = (Gun.obj.has(opts, 'hook') && typeof opts.hook === 'function')
|
||||
? opts.hook : _initial_authsettings.hook
|
||||
// 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) {
|
||||
const err = 'No session!'
|
||||
Gun.log(err)
|
||||
// NOTE! It's fine to resolve recall with reason why not successful
|
||||
// instead of rejecting...
|
||||
return { err: (e && e.err) || err }
|
||||
//return { err: (e && e.err) || err }
|
||||
return gun;
|
||||
}
|
||||
}
|
||||
User.prototype.alive = async function(){
|
||||
|
@ -5,23 +5,22 @@
|
||||
var aeskey = require('./aeskey');
|
||||
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 || {};
|
||||
const key = pair.epriv || pair;
|
||||
const json = parse(data)
|
||||
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'))
|
||||
}, aes, new Uint8Array(shim.Buffer.from(json.ct, 'utf8'))))
|
||||
const r = parse(new shim.TextDecoder('utf8').decode(ct))
|
||||
|
||||
if(cb){ try{ cb(r) }catch(e){console.log(e)} }
|
||||
return r;
|
||||
} catch(e) {
|
||||
SEA.err = e;
|
||||
if(cb){ cb() }
|
||||
return;
|
||||
}}
|
||||
}});
|
||||
|
||||
module.exports = SEA.decrypt;
|
||||
|
@ -4,13 +4,13 @@
|
||||
var S = require('./settings');
|
||||
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 || {};
|
||||
const key = pair.epriv || pair;
|
||||
const msg = JSON.stringify(data)
|
||||
const rand = {s: shim.random(8), iv: shim.random(16)};
|
||||
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)
|
||||
}, aes, new shim.TextEncoder().encode(msg)))
|
||||
const r = 'SEA'+JSON.stringify({
|
||||
@ -25,7 +25,7 @@
|
||||
SEA.err = e;
|
||||
if(cb){ cb() }
|
||||
return;
|
||||
}}
|
||||
}});
|
||||
|
||||
module.exports = SEA.encrypt;
|
||||
|
@ -62,6 +62,7 @@
|
||||
// if there is a request to read data from us, then...
|
||||
var soul = msg.get['#'];
|
||||
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?
|
||||
return to.next(msg); // yes.
|
||||
} else
|
||||
@ -149,7 +150,7 @@
|
||||
if(tmp = relpub(soul)){
|
||||
check['any'+soul+key] = 1;
|
||||
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)){
|
||||
(at.sea.own[rel] = at.sea.own[rel] || {})[pub] = true;
|
||||
}
|
||||
@ -225,4 +226,4 @@
|
||||
to.next(msg); // pass forward any data we do not know how to handle or process (this allows custom security protocols).
|
||||
}
|
||||
|
||||
|
||||
|
22
sea/login.js
22
sea/login.js
@ -4,10 +4,25 @@
|
||||
const finalizeLogin = async (alias, key, gunRoot, opts) => {
|
||||
const user = gunRoot._.user
|
||||
// add our credentials in-memory only to our root gun instance
|
||||
//var tmp = user._.tag;
|
||||
var tmp = user._.tag;
|
||||
var opt = user._.opt;
|
||||
user._ = key.at.$._;
|
||||
user._ = gunRoot.get('~'+key.pub)._;
|
||||
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;
|
||||
// so that way we can use the credentials to encrypt/decrypt data
|
||||
// that is input/output through gun (see below)
|
||||
@ -22,7 +37,8 @@
|
||||
//await authPersist(user._, key.proof, opts) // temporarily disabled
|
||||
// emit an auth event, useful for page redirects and stuff.
|
||||
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) {
|
||||
console.log('Your \'auth\' callback crashed with:', e)
|
||||
}
|
||||
|
@ -5,7 +5,7 @@
|
||||
var Buff = (typeof Buffer !== 'undefined')? Buffer : shim.Buffer;
|
||||
|
||||
//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
|
||||
// First: ECDSA keys for signing/verifying...
|
||||
@ -56,7 +56,7 @@
|
||||
SEA.err = e;
|
||||
if(cb){ cb() }
|
||||
return;
|
||||
}}
|
||||
}});
|
||||
|
||||
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.
|
||||
const queryGunAliases = (alias, gunRoot) => new Promise((resolve, reject) => {
|
||||
// load all public keys associated with the username alias we want to log in with.
|
||||
gunRoot.get('~@'+alias).get((rat, rev) => {
|
||||
rev.off();
|
||||
if (!rat.put) {
|
||||
gunRoot.get('~@'+alias).once((data, key) => {
|
||||
//rev.off();
|
||||
if (!data) {
|
||||
// if no user, don't do anything.
|
||||
const err = 'No user!'
|
||||
Gun.log(err)
|
||||
@ -16,19 +16,18 @@
|
||||
const aliases = []
|
||||
let c = 0
|
||||
// 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)) {
|
||||
// TODO: ... this would then be .filter((at, pub))
|
||||
return
|
||||
}
|
||||
++c
|
||||
// grab the account associated with this public key.
|
||||
gunRoot.get(pub).get((at, ev) => {
|
||||
gunRoot.get(pub).once(data => {
|
||||
pub = pub.slice(1)
|
||||
ev.off()
|
||||
--c
|
||||
if (at.put){
|
||||
aliases.push({ pub, at })
|
||||
if (data){
|
||||
aliases.push({ pub, put: data })
|
||||
}
|
||||
if (!c && (c = -1)) {
|
||||
resolve(aliases)
|
||||
|
18
sea/root.js
18
sea/root.js
@ -1,10 +1,22 @@
|
||||
|
||||
// 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!
|
||||
|
||||
function SEA(){}
|
||||
if(typeof window !== "undefined"){ (SEA.window = window).SEA = SEA }
|
||||
if(typeof window !== "undefined"){ module.window = window }
|
||||
|
||||
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;
|
||||
|
10
sea/sea.js
10
sea/sea.js
@ -29,7 +29,7 @@
|
||||
SEA.encrypt = require('./encrypt');
|
||||
SEA.decrypt = require('./decrypt');
|
||||
|
||||
SEA.random = getRandomBytes;
|
||||
SEA.random = SEA.random || getRandomBytes;
|
||||
|
||||
// This is easy way to use IndexedDB, all methods are Promises
|
||||
// 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.
|
||||
// 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
|
||||
// async/await (compatible) code, use those like Promises.
|
||||
@ -45,7 +45,7 @@
|
||||
// Creates a wrapper library around Web Crypto API
|
||||
// for various AES, ECDSA, PBKDF2 functions we called above.
|
||||
// Calculate public key KeyID aka PGPv4 (result: 8 bytes as hex string)
|
||||
SEA.keyid = async (pub) => {
|
||||
SEA.keyid = SEA.keyid || (async (pub) => {
|
||||
try {
|
||||
// base64('base64(x):base64(y)') => Buffer(xy)
|
||||
const pb = Buffer.concat(
|
||||
@ -63,7 +63,7 @@
|
||||
console.log(e)
|
||||
throw e
|
||||
}
|
||||
}
|
||||
});
|
||||
// all done!
|
||||
// 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.
|
||||
@ -73,7 +73,7 @@
|
||||
// But all other behavior needs to be equally easy, like opinionated ways of
|
||||
// Adding friends (trusted public keys), sending private messages, etc.
|
||||
// Cheers! Tell me what you think.
|
||||
var Gun = (SEA.window||{}).Gun || require('./gun');
|
||||
var Gun = (SEA.window||{}).Gun || require('./gun', 1);
|
||||
Gun.SEA = SEA;
|
||||
SEA.Gun = Gun;
|
||||
|
||||
|
@ -3,7 +3,7 @@
|
||||
var shim = require('./shim');
|
||||
var S = require('./settings');
|
||||
// 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 epub = pair.epub
|
||||
const epriv = pair.epriv
|
||||
@ -27,7 +27,7 @@
|
||||
SEA.err = e;
|
||||
if(cb){ cb() }
|
||||
return;
|
||||
}}
|
||||
}});
|
||||
|
||||
const keysToEcdhJwk = (pub, d) => { // d === priv
|
||||
//const [ x, y ] = Buffer.from(pub, 'base64').toString('utf8').split(':') // old
|
||||
|
@ -1,4 +1,5 @@
|
||||
|
||||
const SEA = require('./root');
|
||||
const Buffer = require('./buffer')
|
||||
const settings = {}
|
||||
// Encryption parameters
|
||||
@ -35,5 +36,6 @@
|
||||
jwk: keysToEcdsaJwk,
|
||||
recall: authsettings
|
||||
})
|
||||
SEA.opt = settings;
|
||||
module.exports = settings
|
||||
|
10
sea/shim.js
10
sea/shim.js
@ -2,7 +2,7 @@
|
||||
const Buffer = require('./buffer')
|
||||
const api = {Buffer: Buffer}
|
||||
|
||||
if (typeof __webpack_require__ === 'function' || typeof window !== 'undefined') {
|
||||
if (typeof window !== 'undefined') {
|
||||
var crypto = window.crypto || window.msCrypto;
|
||||
var subtle = crypto.subtle || crypto.webkitSubtle;
|
||||
const TextEncoder = window.TextEncoder
|
||||
@ -16,9 +16,9 @@
|
||||
})
|
||||
} else {
|
||||
try{
|
||||
var crypto = require('crypto');
|
||||
const { subtle } = require('@trust/webcrypto') // All but ECDH
|
||||
const { TextEncoder, TextDecoder } = require('text-encoding')
|
||||
var crypto = require('crypto', 1);
|
||||
const { subtle } = require('@trust/webcrypto', 1) // All but ECDH
|
||||
const { TextEncoder, TextDecoder } = require('text-encoding', 1)
|
||||
Object.assign(api, {
|
||||
crypto,
|
||||
subtle,
|
||||
@ -27,7 +27,7 @@
|
||||
random: (len) => Buffer.from(crypto.randomBytes(len))
|
||||
});
|
||||
//try{
|
||||
const WebCrypto = require('node-webcrypto-ossl')
|
||||
const WebCrypto = require('node-webcrypto-ossl', 1)
|
||||
api.ossl = new WebCrypto({directory: 'ossl'}).subtle // ECDH
|
||||
//}catch(e){
|
||||
//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 sha256hash = require('./sha256');
|
||||
|
||||
SEA.sign = async (data, pair, cb) => { try {
|
||||
if(data.slice
|
||||
SEA.sign = SEA.sign || (async (data, pair, cb) => { try {
|
||||
if(data && data.slice
|
||||
&& 'SEA{' === data.slice(0,4)
|
||||
&& '"m":' === data.slice(4,8)){
|
||||
// TODO: This would prevent pair2 signing pair1's signature.
|
||||
@ -19,8 +19,8 @@
|
||||
const jwk = S.jwk(pub, priv)
|
||||
const msg = JSON.stringify(data)
|
||||
const hash = await sha256hash(msg)
|
||||
const sig = await 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!
|
||||
const sig = await (shim.ossl || shim.subtle).importKey('jwk', jwk, S.ecdsa.pair, false, ['sign'])
|
||||
.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')});
|
||||
|
||||
if(cb){ try{ cb(r) }catch(e){console.log(e)} }
|
||||
@ -30,7 +30,7 @@
|
||||
SEA.err = e;
|
||||
if(cb){ cb() }
|
||||
return;
|
||||
}}
|
||||
}});
|
||||
|
||||
module.exports = SEA.sign;
|
||||
|
@ -6,7 +6,7 @@
|
||||
var parse = require('./parse');
|
||||
var u;
|
||||
|
||||
SEA.verify = async (data, pair, cb) => { try {
|
||||
SEA.verify = SEA.verify || (async (data, pair, cb) => { try {
|
||||
const json = parse(data)
|
||||
if(false === pair){ // don't verify!
|
||||
const raw = (json !== data)?
|
||||
@ -31,7 +31,7 @@
|
||||
SEA.err = e;
|
||||
if(cb){ cb() }
|
||||
return;
|
||||
}}
|
||||
}});
|
||||
|
||||
module.exports = SEA.verify;
|
||||
|
47
sea/work.js
47
sea/work.js
@ -2,49 +2,40 @@
|
||||
var SEA = require('./root');
|
||||
var shim = require('./shim');
|
||||
var S = require('./settings');
|
||||
var sha = require('./sha256');
|
||||
var u;
|
||||
|
||||
SEA.work = async (data, pair, cb) => { try { // used to be named `proof`
|
||||
var salt = pair.epub || pair; // epub not recommended, salt should be random!
|
||||
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 opt = opt || {};
|
||||
if(salt instanceof Function){
|
||||
cb = salt;
|
||||
salt = u;
|
||||
}
|
||||
salt = salt || shim.random(9);
|
||||
if (SEA.window) {
|
||||
// For browser subtle works fine
|
||||
const key = await shim.subtle.importKey(
|
||||
'raw', new shim.TextEncoder().encode(data), { name: 'PBKDF2' }, false, ['deriveBits']
|
||||
)
|
||||
const result = await shim.subtle.deriveBits({
|
||||
name: 'PBKDF2',
|
||||
iterations: S.pbkdf2.iter,
|
||||
salt: new shim.TextEncoder().encode(salt),
|
||||
hash: S.pbkdf2.hash,
|
||||
}, key, S.pbkdf2.ks * 8)
|
||||
data = shim.random(data.length) // Erase data in case of passphrase
|
||||
const r = shim.Buffer.from(result, 'binary').toString('utf8')
|
||||
if(cb){ try{ cb(r) }catch(e){console.log(e)} }
|
||||
return r;
|
||||
if('SHA-256' === opt.name){
|
||||
var rsha = shim.Buffer.from(await sha(data), 'binary').toString('utf8')
|
||||
if(cb){ try{ cb(rsha) }catch(e){console.log(e)} }
|
||||
return rsha;
|
||||
}
|
||||
// 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()
|
||||
const key = await (shim.ossl || shim.subtle).importKey(
|
||||
'raw', new shim.TextEncoder().encode(data), { name: opt.name || 'PBKDF2' }, false, ['deriveBits']
|
||||
)
|
||||
data = shim.random(data.length) // Erase passphrase for app
|
||||
const r = hash && hash.toString('utf8')
|
||||
const result = await (shim.ossl || shim.subtle).deriveBits({
|
||||
name: opt.name || 'PBKDF2',
|
||||
iterations: opt.iterations || S.pbkdf2.iter,
|
||||
salt: new shim.TextEncoder().encode(opt.salt || salt),
|
||||
hash: opt.hash || S.pbkdf2.hash,
|
||||
}, key, opt.length || (S.pbkdf2.ks * 8))
|
||||
data = shim.random(data.length) // Erase data in case of passphrase
|
||||
const r = shim.Buffer.from(result, 'binary').toString('utf8')
|
||||
if(cb){ try{ cb(r) }catch(e){console.log(e)} }
|
||||
return r;
|
||||
} catch(e) {
|
||||
SEA.err = e;
|
||||
if(cb){ cb() }
|
||||
return;
|
||||
}}
|
||||
}});
|
||||
|
||||
module.exports = SEA.work;
|
||||
|
@ -129,7 +129,7 @@ Gun.on('create', function(root){
|
||||
if(data){ disk = data }
|
||||
try{store.setItem(opt.prefix, JSON.stringify(disk));
|
||||
}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});
|
||||
}
|
||||
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){
|
||||
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;
|
||||
if(this.to){ this.to.next(msg) }
|
||||
@ -29,8 +32,9 @@ function Mesh(ctx){
|
||||
mesh.hear = function(raw, peer){
|
||||
if(!raw){ return }
|
||||
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);
|
||||
}catch(e){console.log('DAM JSON parse error', e)}
|
||||
}catch(e){opt.log('DAM JSON parse error', e)}
|
||||
if('{' === tmp){
|
||||
if(!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(peer.batch){
|
||||
peer.batch.push(raw);
|
||||
return;
|
||||
peer.tail = (peer.tail || 0) + raw.length;
|
||||
if(peer.tail <= opt.pack){
|
||||
peer.batch.push(raw);
|
||||
return;
|
||||
}
|
||||
flush(peer);
|
||||
}
|
||||
peer.batch = [];
|
||||
setTimeout(function(){
|
||||
var tmp = peer.batch;
|
||||
if(!tmp){ return }
|
||||
peer.batch = null;
|
||||
if(!tmp.length){ return }
|
||||
send(JSON.stringify(tmp), peer);
|
||||
}, opt.gap || opt.wait || 1);
|
||||
setTimeout(function(){flush(peer)}, opt.gap);
|
||||
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){
|
||||
var wire = peer.wire;
|
||||
try{
|
||||
@ -193,6 +202,7 @@ function Mesh(ctx){
|
||||
ctx.on('bye', peer);
|
||||
}
|
||||
|
||||
mesh.hear['!'] = function(msg, peer){ opt.log('Error:', msg.err) }
|
||||
mesh.hear['?'] = function(msg, peer){
|
||||
if(!msg.pid){ return mesh.say({dam: '?', pid: opt.pid, '@': msg['#']}, peer) }
|
||||
peer.id = peer.id || msg.pid;
|
||||
|
@ -21,7 +21,7 @@ Gun.on('opt', function(root){
|
||||
|
||||
var wire = opt.wire;
|
||||
opt.wire = open;
|
||||
function open(peer){
|
||||
function open(peer){ try{
|
||||
if(!peer || !peer.url){ return wire && wire(peer) }
|
||||
var url = peer.url.replace('http', 'ws');
|
||||
var wire = peer.wire = new opt.WebSocket(url);
|
||||
@ -44,7 +44,7 @@ Gun.on('opt', function(root){
|
||||
opt.mesh.hear(msg.data || msg, peer);
|
||||
};
|
||||
return wire;
|
||||
}
|
||||
}catch(e){}}
|
||||
|
||||
function reconnect(peer){
|
||||
clearTimeout(peer.defer);
|
||||
|
@ -16,7 +16,7 @@ function Dup(opt){
|
||||
dup.to = setTimeout(function(){
|
||||
var now = time_is();
|
||||
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);
|
||||
});
|
||||
dup.to = null;
|
||||
|
@ -84,7 +84,8 @@ function val(msg, eve, to){
|
||||
data = tmp.put;
|
||||
}
|
||||
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(){
|
||||
val.call({as:opt}, msg, eve, tmp || 1);
|
||||
}, opt.wait || 99);
|
||||
|
@ -1,16 +1,17 @@
|
||||
|
||||
var root;
|
||||
if(typeof window !== "undefined"){ root = window }
|
||||
if(typeof global !== "undefined"){ root = global }
|
||||
root = root || {};
|
||||
var console = root.console || {log: function(){}};
|
||||
function USE(arg){
|
||||
return arg.slice? USE[R(arg)] : function(mod, path){
|
||||
arg(mod = {exports: {}});
|
||||
USE[R(path)] = mod.exports;
|
||||
}
|
||||
function R(p){
|
||||
return p.split('/').slice(-1).toString().replace('.js','');
|
||||
}
|
||||
}
|
||||
if(typeof module !== "undefined"){ var common = module }
|
||||
var root;
|
||||
if(typeof window !== "undefined"){ root = window }
|
||||
if(typeof global !== "undefined"){ root = global }
|
||||
root = root || {};
|
||||
var console = root.console || {log: function(){}};
|
||||
function USE(arg, req){
|
||||
return req? require(arg) : arg.slice? USE[R(arg)] : function(mod, path){
|
||||
arg(mod = {exports: {}});
|
||||
USE[R(path)] = mod.exports;
|
||||
}
|
||||
function R(p){
|
||||
return p.split('/').slice(-1).toString().replace('.js','');
|
||||
}
|
||||
}
|
||||
if(typeof module !== "undefined"){ var common = module }
|
||||
|
@ -153,11 +153,11 @@ Gun.dup = require('./dup');
|
||||
Gun.on.get = function(msg, gun){
|
||||
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];
|
||||
if(get['*']){ // TEMPORARY HACK FOR MARTTI, TESTING
|
||||
if(obj_has(soul, '*')){ // TEMPORARY HACK FOR MARTTI, TESTING
|
||||
var graph = {};
|
||||
Gun.obj.map(root.graph, function(node, soul){
|
||||
if(Gun.text.match(soul, get)){
|
||||
graph[soul] = Gun.obj.copy(node);
|
||||
Gun.obj.map(root.graph, function(node, s){
|
||||
if(Gun.text.match(s, soul)){
|
||||
graph[s] = Gun.obj.copy(node);
|
||||
}
|
||||
});
|
||||
if(!Gun.obj.empty(graph)){
|
||||
|
@ -1,17 +1,25 @@
|
||||
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();
|
||||
data += data + data;
|
||||
|
||||
console.log(data.length);
|
||||
|
||||
var gun = Gun('http://localhost:8080/gun');
|
||||
var gun = Gun('http://localhost:8765/gun');
|
||||
//var gun = Gun();
|
||||
|
||||
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!");
|
||||
gun.get('bigsync').get('raw').put(data);
|
||||
gun.get('bigsync').get('raw').put(data, function(ack){console.log(ack)});
|
||||
|
||||
/*var req = require('http').request({
|
||||
host: 'localhost'
|
||||
|
@ -1,18 +1,11 @@
|
||||
<h1>RindexedDB</h1>
|
||||
|
||||
<script src="../../examples/jquery.js"></script>
|
||||
<script src="../../gun.js"></script>
|
||||
<script src="../../lib/radix.js"></script>
|
||||
<script src="../../lib/radisk.js"></script>
|
||||
<script src="../../lib/store.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>
|
||||
localStorage.clear();
|
||||
|
||||
@ -21,4 +14,4 @@ var opt = {};
|
||||
opt.store = RindexedDB(opt);
|
||||
|
||||
var gun = Gun(opt);
|
||||
</script>
|
||||
</script>
|
||||
|
@ -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