Initial Commit.

This commit is contained in:
2022-01-26 19:20:35 +00:00
commit 7733b8b640
25 changed files with 1757 additions and 0 deletions

47
app.js Normal file
View File

@ -0,0 +1,47 @@
var createError = require('http-errors');
var express = require('express');
var path = require('path');
var cookieParser = require('cookie-parser');
var logger = require('morgan');
var indexRouter = require('./routes/index');
var usersRouter = require('./routes/users');
var pgpRouter = require('./routes/pgp');
var walletRouter = require('./routes/wallet');
var transactionRouter = require('./routes/transaction');
var app = express();
// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'ejs');
app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));
app.use('/', indexRouter);
app.use('/users', usersRouter);
app.use('/pgp', pgpRouter);
app.use('/wallet', walletRouter);
app.use('/transaction', transactionRouter);
// catch 404 and forward to error handler
app.use(function(req, res, next) {
next(createError(404));
});
// error handler
app.use(function(err, req, res, next) {
// set locals, only providing error in development
res.locals.message = err.message;
res.locals.error = req.app.get('env') === 'development' ? err : {};
// render the error page
res.status(err.status || 500);
res.render('error');
});
module.exports = app;

90
bin/www Executable file
View File

@ -0,0 +1,90 @@
#!/usr/bin/env node
/**
* Module dependencies.
*/
var app = require('../app');
var debug = require('debug')('nodetest:server');
var http = require('http');
/**
* Get port from environment and store in Express.
*/
var port = normalizePort(process.env.PORT || '3000');
app.set('port', port);
/**
* Create HTTP server.
*/
var server = http.createServer(app);
/**
* Listen on provided port, on all network interfaces.
*/
server.listen(port);
server.on('error', onError);
server.on('listening', onListening);
/**
* Normalize a port into a number, string, or false.
*/
function normalizePort(val) {
var port = parseInt(val, 10);
if (isNaN(port)) {
// named pipe
return val;
}
if (port >= 0) {
// port number
return port;
}
return false;
}
/**
* Event listener for HTTP server "error" event.
*/
function onError(error) {
if (error.syscall !== 'listen') {
throw error;
}
var bind = typeof port === 'string'
? 'Pipe ' + port
: 'Port ' + port;
// handle specific listen errors with friendly messages
switch (error.code) {
case 'EACCES':
console.error(bind + ' requires elevated privileges');
process.exit(1);
break;
case 'EADDRINUSE':
console.error(bind + ' is already in use');
process.exit(1);
break;
default:
throw error;
}
}
/**
* Event listener for HTTP server "listening" event.
*/
function onListening() {
var addr = server.address();
var bind = typeof addr === 'string'
? 'pipe ' + addr
: 'port ' + addr.port;
debug('Listening on ' + bind);
}

443
package-lock.json generated Normal file
View File

@ -0,0 +1,443 @@
{
"name": "nodetest",
"version": "0.0.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
"accepts": {
"version": "1.3.7",
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz",
"integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==",
"requires": {
"mime-types": "~2.1.24",
"negotiator": "0.6.2"
}
},
"array-flatten": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
"integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI="
},
"asn1.js": {
"version": "5.4.1",
"resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-5.4.1.tgz",
"integrity": "sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA==",
"requires": {
"bn.js": "^4.0.0",
"inherits": "^2.0.1",
"minimalistic-assert": "^1.0.0",
"safer-buffer": "^2.1.0"
}
},
"basic-auth": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz",
"integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==",
"requires": {
"safe-buffer": "5.1.2"
}
},
"bn.js": {
"version": "4.12.0",
"resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz",
"integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA=="
},
"body-parser": {
"version": "1.18.3",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.3.tgz",
"integrity": "sha1-WykhmP/dVTs6DyDe0FkrlWlVyLQ=",
"requires": {
"bytes": "3.0.0",
"content-type": "~1.0.4",
"debug": "2.6.9",
"depd": "~1.1.2",
"http-errors": "~1.6.3",
"iconv-lite": "0.4.23",
"on-finished": "~2.3.0",
"qs": "6.5.2",
"raw-body": "2.3.3",
"type-is": "~1.6.16"
}
},
"bytes": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz",
"integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg="
},
"content-disposition": {
"version": "0.5.2",
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz",
"integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ="
},
"content-type": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz",
"integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA=="
},
"cookie": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz",
"integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg=="
},
"cookie-parser": {
"version": "1.4.5",
"resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.5.tgz",
"integrity": "sha512-f13bPUj/gG/5mDr+xLmSxxDsB9DQiTIfhJS/sqjrmfAWiAN+x2O4i/XguTL9yDZ+/IFDanJ+5x7hC4CXT9Tdzw==",
"requires": {
"cookie": "0.4.0",
"cookie-signature": "1.0.6"
}
},
"cookie-signature": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
"integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw="
},
"crypto-js": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.0.0.tgz",
"integrity": "sha512-bzHZN8Pn+gS7DQA6n+iUmBfl0hO5DJq++QP3U6uTucDtk/0iGpXd/Gg7CGR0p8tJhofJyaKoWBuJI4eAO00BBg=="
},
"debug": {
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
"requires": {
"ms": "2.0.0"
}
},
"depd": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
"integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak="
},
"destroy": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz",
"integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA="
},
"ee-first": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
"integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0="
},
"ejs": {
"version": "2.6.2",
"resolved": "https://registry.npmjs.org/ejs/-/ejs-2.6.2.tgz",
"integrity": "sha512-PcW2a0tyTuPHz3tWyYqtK6r1fZ3gp+3Sop8Ph+ZYN81Ob5rwmbHEzaqs10N3BEsaGTkh/ooniXK+WwszGlc2+Q=="
},
"encodeurl": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
"integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k="
},
"escape-html": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
"integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg="
},
"etag": {
"version": "1.8.1",
"resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
"integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc="
},
"express": {
"version": "4.16.4",
"resolved": "https://registry.npmjs.org/express/-/express-4.16.4.tgz",
"integrity": "sha512-j12Uuyb4FMrd/qQAm6uCHAkPtO8FDTRJZBDd5D2KOL2eLaz1yUNdUB/NOIyq0iU4q4cFarsUCrnFDPBcnksuOg==",
"requires": {
"accepts": "~1.3.5",
"array-flatten": "1.1.1",
"body-parser": "1.18.3",
"content-disposition": "0.5.2",
"content-type": "~1.0.4",
"cookie": "0.3.1",
"cookie-signature": "1.0.6",
"debug": "2.6.9",
"depd": "~1.1.2",
"encodeurl": "~1.0.2",
"escape-html": "~1.0.3",
"etag": "~1.8.1",
"finalhandler": "1.1.1",
"fresh": "0.5.2",
"merge-descriptors": "1.0.1",
"methods": "~1.1.2",
"on-finished": "~2.3.0",
"parseurl": "~1.3.2",
"path-to-regexp": "0.1.7",
"proxy-addr": "~2.0.4",
"qs": "6.5.2",
"range-parser": "~1.2.0",
"safe-buffer": "5.1.2",
"send": "0.16.2",
"serve-static": "1.13.2",
"setprototypeof": "1.1.0",
"statuses": "~1.4.0",
"type-is": "~1.6.16",
"utils-merge": "1.0.1",
"vary": "~1.1.2"
},
"dependencies": {
"cookie": {
"version": "0.3.1",
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz",
"integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s="
}
}
},
"finalhandler": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz",
"integrity": "sha512-Y1GUDo39ez4aHAw7MysnUD5JzYX+WaIj8I57kO3aEPT1fFRL4sr7mjei97FgnwhAyyzRYmQZaTHb2+9uZ1dPtg==",
"requires": {
"debug": "2.6.9",
"encodeurl": "~1.0.2",
"escape-html": "~1.0.3",
"on-finished": "~2.3.0",
"parseurl": "~1.3.2",
"statuses": "~1.4.0",
"unpipe": "~1.0.0"
}
},
"forwarded": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz",
"integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ="
},
"fresh": {
"version": "0.5.2",
"resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
"integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac="
},
"http-errors": {
"version": "1.6.3",
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz",
"integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=",
"requires": {
"depd": "~1.1.2",
"inherits": "2.0.3",
"setprototypeof": "1.1.0",
"statuses": ">= 1.4.0 < 2"
}
},
"iconv-lite": {
"version": "0.4.23",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz",
"integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==",
"requires": {
"safer-buffer": ">= 2.1.2 < 3"
}
},
"inherits": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
},
"ipaddr.js": {
"version": "1.9.1",
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
"integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g=="
},
"kyoto-tycoon": {
"version": "0.0.7",
"resolved": "https://registry.npmjs.org/kyoto-tycoon/-/kyoto-tycoon-0.0.7.tgz",
"integrity": "sha1-gQlZ1+t0GiOtQa6YxMKlhBCpu7M="
},
"media-typer": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
"integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g="
},
"merge-descriptors": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
"integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E="
},
"methods": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
"integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4="
},
"mime": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz",
"integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ=="
},
"mime-db": {
"version": "1.47.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.47.0.tgz",
"integrity": "sha512-QBmA/G2y+IfeS4oktet3qRZ+P5kPhCKRXxXnQEudYqUaEioAU1/Lq2us3D/t1Jfo4hE9REQPrbB7K5sOczJVIw=="
},
"mime-types": {
"version": "2.1.30",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.30.tgz",
"integrity": "sha512-crmjA4bLtR8m9qLpHvgxSChT+XoSlZi8J4n/aIdn3z92e/U47Z0V/yl+Wh9W046GgFVAmoNR/fmdbZYcSSIUeg==",
"requires": {
"mime-db": "1.47.0"
}
},
"minimalistic-assert": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz",
"integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A=="
},
"morgan": {
"version": "1.9.1",
"resolved": "https://registry.npmjs.org/morgan/-/morgan-1.9.1.tgz",
"integrity": "sha512-HQStPIV4y3afTiCYVxirakhlCfGkI161c76kKFca7Fk1JusM//Qeo1ej2XaMniiNeaZklMVrh3vTtIzpzwbpmA==",
"requires": {
"basic-auth": "~2.0.0",
"debug": "2.6.9",
"depd": "~1.1.2",
"on-finished": "~2.3.0",
"on-headers": "~1.0.1"
}
},
"ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
},
"negotiator": {
"version": "0.6.2",
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz",
"integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw=="
},
"on-finished": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
"integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=",
"requires": {
"ee-first": "1.1.1"
}
},
"on-headers": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz",
"integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA=="
},
"openpgp": {
"version": "5.0.0-2",
"resolved": "https://registry.npmjs.org/openpgp/-/openpgp-5.0.0-2.tgz",
"integrity": "sha512-es+5A50Y+4JbtV+eugLPW9v/UkUIOufeOUyTcjbG8SMILNaLY9nEwUSJKDjQOadY+16w7Uqt0FDQ3Z1Vq7/F9g==",
"requires": {
"asn1.js": "^5.0.0"
}
},
"parseurl": {
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
"integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ=="
},
"path-to-regexp": {
"version": "0.1.7",
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
"integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w="
},
"proxy-addr": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz",
"integrity": "sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==",
"requires": {
"forwarded": "~0.1.2",
"ipaddr.js": "1.9.1"
}
},
"qs": {
"version": "6.5.2",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz",
"integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA=="
},
"range-parser": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
"integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg=="
},
"raw-body": {
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.3.tgz",
"integrity": "sha512-9esiElv1BrZoI3rCDuOuKCBRbuApGGaDPQfjSflGxdy4oyzqghxu6klEkkVIvBje+FF0BX9coEv8KqW6X/7njw==",
"requires": {
"bytes": "3.0.0",
"http-errors": "1.6.3",
"iconv-lite": "0.4.23",
"unpipe": "1.0.0"
}
},
"safe-buffer": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
},
"safer-buffer": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
},
"send": {
"version": "0.16.2",
"resolved": "https://registry.npmjs.org/send/-/send-0.16.2.tgz",
"integrity": "sha512-E64YFPUssFHEFBvpbbjr44NCLtI1AohxQ8ZSiJjQLskAdKuriYEP6VyGEsRDH8ScozGpkaX1BGvhanqCwkcEZw==",
"requires": {
"debug": "2.6.9",
"depd": "~1.1.2",
"destroy": "~1.0.4",
"encodeurl": "~1.0.2",
"escape-html": "~1.0.3",
"etag": "~1.8.1",
"fresh": "0.5.2",
"http-errors": "~1.6.2",
"mime": "1.4.1",
"ms": "2.0.0",
"on-finished": "~2.3.0",
"range-parser": "~1.2.0",
"statuses": "~1.4.0"
}
},
"serve-static": {
"version": "1.13.2",
"resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.2.tgz",
"integrity": "sha512-p/tdJrO4U387R9oMjb1oj7qSMaMfmOyd4j9hOFoxZe2baQszgHcSWjuya/CiT5kgZZKRudHNOA0pYXOl8rQ5nw==",
"requires": {
"encodeurl": "~1.0.2",
"escape-html": "~1.0.3",
"parseurl": "~1.3.2",
"send": "0.16.2"
}
},
"setprototypeof": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz",
"integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ=="
},
"statuses": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz",
"integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew=="
},
"type-is": {
"version": "1.6.18",
"resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
"integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
"requires": {
"media-typer": "0.3.0",
"mime-types": "~2.1.24"
}
},
"unpipe": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
"integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw="
},
"utils-merge": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
"integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM="
},
"vary": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
"integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw="
}
}
}

19
package.json Normal file
View File

@ -0,0 +1,19 @@
{
"name": "toy-blockchain",
"version": "0.0.0",
"private": true,
"scripts": {
"start": "node ./bin/www"
},
"dependencies": {
"cookie-parser": "~1.4.4",
"crypto-js": "^4.0.0",
"debug": "~2.6.9",
"ejs": "~2.6.1",
"express": "~4.16.1",
"http-errors": "~1.6.3",
"kyoto-tycoon": "0.0.7",
"morgan": "~1.9.1",
"openpgp": "^5.0.0-2"
}
}

View File

@ -0,0 +1,55 @@
/* globals Chart:false, feather:false */
(function () {
'use strict'
feather.replace()
// Graphs
var ctx = document.getElementById('myChart')
// eslint-disable-next-line no-unused-vars
var myChart = new Chart(ctx, {
type: 'line',
data: {
labels: [
'Sunday',
'Monday',
'Tuesday',
'Wednesday',
'Thursday',
'Friday',
'Saturday'
],
datasets: [{
data: [
15339,
21345,
18483,
24003,
23489,
24092,
12034
],
lineTension: 0,
backgroundColor: 'transparent',
borderColor: '#007bff',
borderWidth: 4,
pointBackgroundColor: '#007bff'
}]
},
options: {
scales: {
yAxes: [{
ticks: {
beginAtZero: false
}
}]
},
legend: {
display: false
}
}
})
})()

17
public/javascripts/openpgp.min.js vendored Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,100 @@
body {
font-size: .875rem;
}
.feather {
width: 16px;
height: 16px;
vertical-align: text-bottom;
}
/*
** Sidebar
**/
.sidebar {
position: fixed;
top: 0;
/* rtl:raw:
* right: 0;
**/
bottom: 0;
/* rtl:remove */
left: 0;
z-index: 100; /* Behind the navbar */
padding: 48px 0 0; /* Height of navbar */
box-shadow: inset -1px 0 0 rgba(0, 0, 0, .1);
}
@media (max-width: 767.98px) {
.sidebar {
top: 5rem;
}
}
.sidebar-sticky {
position: relative;
top: 0;
height: calc(100vh - 48px);
padding-top: .5rem;
overflow-x: hidden;
overflow-y: auto; /* Scrollable contents if viewport is shorter than content. */
}
.sidebar .nav-link {
font-weight: 500;
color: #333;
}
.sidebar .nav-link .feather {
margin-right: 4px;
color: #727272;
}
.sidebar .nav-link.active {
color: #007bff;
}
.sidebar .nav-link:hover .feather,
.sidebar .nav-link.active .feather {
color: inherit;
}
.sidebar-heading {
font-size: .75rem;
text-transform: uppercase;
}
/*
* * Navbar
* */
.navbar-brand {
padding-top: .75rem;
padding-bottom: .75rem;
font-size: 1rem;
background-color: rgba(0, 0, 0, .25);
box-shadow: inset -1px 0 0 rgba(0, 0, 0, .25);
}
.navbar .navbar-toggler {
top: .25rem;
right: 1rem;
}
.navbar .form-control {
padding: .75rem 1rem;
border-width: 0;
border-radius: 0;
}
.form-control-dark {
color: #fff;
background-color: rgba(255, 255, 255, .1);
border-color: rgba(255, 255, 255, .1);
}
.form-control-dark:focus {
border-color: transparent;
box-shadow: 0 0 0 3px rgba(255, 255, 255, .25);
}

View File

@ -0,0 +1,8 @@
body {
padding: 50px;
font: 14px "Lucida Grande", Helvetica, Arial, sans-serif;
}
a {
color: #00B7FF;
}

165
routes/index.js Normal file
View File

@ -0,0 +1,165 @@
var express = require('express');
var router = express.Router();
var KyotoTycoon = require('kyoto-tycoon').KyotoTycoon;
var CryptoJS = require('crypto-js');
var kt = new KyotoTycoon();
function randomString(length) {
chars = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
var result = '';
for (var i = length; i > 0; --i) result += chars[Math.floor(Math.random() * chars.length)];
return result;
}
function mineBlock(block, requirement) {
rand = '';
i = 0;
while(CryptoJS.SHA512(JSON.stringify(block.content)).toString().substring(0, requirement.length) != requirement) {
rand = randomString(32);
block.content.rand = rand;
i++;
console.log(i + ': ' + rand + ' : ' + CryptoJS.SHA512(JSON.stringify(block.content)).toString());
}
block.hash = CryptoJS.SHA512(JSON.stringify(block.content)).toString();
}
function checkBlock(id, badId, goodId) {
kt.get(id, function(err, data) {
if(err) {
console.log('Error getting block id ' + id);
return -1;
} else {
kt.get(id - 1, function(err, prevData){
if(err) {
console.log('Error getting block id ' + id - 1);
}
block = JSON.parse(data);
if((CryptoJS.SHA512(JSON.stringify(block.content)).toString() === block.hash) && (block.content.prevhash === JSON.parse(prevData).hash)) {
console.log('Block ' + block.content.id + ' is OK');
if(goodId) {
goodId(block.content.id);
}
} else {
console.log('Block ' + block.content.id + ' is bad');
if(badId) {
badId(block.content.id);
}
}
});
}
});
}
function checkChain() {
j = 0;
kt.status(':', async function (err, data) {
if(err) {
console.log('Error getting chain data.');
return -1;
} else {
for(i = 1; i < data.count; i++) {
await checkBlock(i, function(id) {
console.log('Handle badId: ' + id);
deleteChain(id, data.count - 1);
}, function(id) {
console.log('Handle goodId: ' + id)
});
}
}
});
}
function deleteChain(firstId, lastId) {
for(i = firstId; i <= lastId; i++) {
console.log('Deleteing block id ' + i);
kt.remove(i);
}
}
function getLastTransactions(count, next) {
result = [];
j = 0;
kt.status(':', async function(err, data) {
if(err) {
console.log('Error getting chain data.');
return -1;
}
for(i = data.count - count; i < data.count; i++) {
result.push(new Promise((resolve, reject) => {
kt.get(i, async function(err, block) {
if(err) reject(err);
resolve(JSON.parse(block));
});
}));
}
if(next) {
next(await Promise.all(result));
}
});
}
/* GET home page. */
router.get('/', function(req, res, next) {
getLastTransactions(15, async function(data) {
await console.log(JSON.stringify(data));
breadcrumb = [{title:'home', url:'/', stat:'active'}]
await res.render('index', { title: 'mCoin', data: data, breadcrumb: breadcrumb });
});
});
router.get('/new', function(req, res, next) {
block = {
content: {
message: {
from: 'Simona',
to: 'Matjaz',
amount: '200',
timestamp: '1234',
},
id: 1,
previd: 0,
prevhash: 'abc',
rand: '',
timestamp: '',
unixtime: '',
},
hash: '',
difficulty: '',
version: '0.01',
};
difficulty = '000';
kt.status(':', function(err, data) {
if(err) {
console.log(err);
}
kt.get(data.count - 1, function(err, prevData) {
if(err) {
console.log(err);
}
const utcDate = new Date();
block.content.timestamp = utcDate.toUTCString();
block.content.unixtime = Math.floor(new Date().getTime() / 1000);
block.content.prevhash = JSON.parse(prevData).hash;
block.content.id = data.count;
block.content.previd = data.count - 1;
block.difficulty = difficulty;
mineBlock(block, difficulty);
console.log(JSON.stringify(block));
kt.set(block.content.id.toString(), JSON.stringify(block), function() {
console.log('Block Written');
});
});
});
});
router.get('/check', function(req, res, next) {
badBlock = 0;
console.log('Checking the chain');
checkChain();
});
module.exports = router;

26
routes/pgp.js Normal file
View File

@ -0,0 +1,26 @@
var express = require('express');
var router = express.Router();
const openpgp = require('openpgp');
router.get('/', function(req, res, next) {
(async () => {
const { privateKeyArmored, publicKeyArmored, revocationCertificate } = await openpgp.generateKey({
type: 'ecc', // Type of the key, defaults to ECC
curve: 'curve25519', // ECC curve name, defaults to curve25519
userIDs: [{ name: 'Jon Doe', email: 'jon@doe.com' }], // you can pass multiple user IDs
passphrase: 'super long and hard to guess secret' // protects the private key
});
console.log(privateKeyArmored); // '-----BEGIN PGP PRIVATE KEY BLOCK ... '
console.log(publicKeyArmored); // '-----BEGIN PGP PUBLIC KEY BLOCK ... '
console.log(revocationCertificate); // '-----BEGIN PGP PUBLIC KEY BLOCK ... '
})();
res.send('respond with a resource');
});
module.exports = router;

10
routes/transaction.js Normal file
View File

@ -0,0 +1,10 @@
var express = require('express');
var router = express.Router();
/* GET users listing. */
router.get('/', function(req, res, next) {
breadcrumb = [{title:'home', url:'/', stat:''}, {title:'transaction', url:'/transaction', stat:'active'}];
res.render('transaction', {title: 'Transaction', breadcrumb: breadcrumb});
});
module.exports = router;

9
routes/users.js Normal file
View File

@ -0,0 +1,9 @@
var express = require('express');
var router = express.Router();
/* GET users listing. */
router.get('/', function(req, res, next) {
res.send('respond with a resource');
});
module.exports = router;

20
routes/wallet.js Normal file
View File

@ -0,0 +1,20 @@
var express = require('express');
var router = express.Router();
/* GET users listing. */
router.get('/', function(req, res, next) {
breadcrumb = [{title:'home', url:'/', stat:''}, {title:'wallet', url:'/wallet', stat:'active'}];
res.render('wallet', {title: 'Wallet Management', breadcrumb: breadcrumb});
});
router.get('/new', function(req, res, next) {
breadcrumb = [{title: 'home', url: '/', stat: ''}, {title: 'wallet', url:'/wallet', stat: ''}, {title:'new wallet', url:'/wallet/new', stat:'active'}];
res.render('new-wallet', {title: 'Create New Wallet', breadcrumb: breadcrumb});
});
router.get('/open', function(req, res, next) {
breadcrumb = [{title: 'home', url: '/', stat: ''}, {title: 'wallet', url:'/wallet', stat: ''}, {title:'open wallet', url:'/wallet/open', stat:'active'}];
res.render('open-wallet', {title: 'Open Wallet', breadcrumb: breadcrumb});
});
module.exports = router;

21
views/breadcrumb.ejs Normal file
View File

@ -0,0 +1,21 @@
<div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3">
<nav aria-label="breadcrumb">
<ol class="breadcrumb" id="breadcrumb-id">
<% breadcrumb.forEach(function(crumb) { %>
<li class="breadcrumb-item
<% if(crumb.stat === 'active') { %>
active" aria-current="page"><%- crumb.title%>
<% } else {%>
"><a href="<%- crumb.url %>"><%- crumb.title%></a>
</li>
<% }}) %>
</ol>
</nav>
</div>

3
views/error.ejs Normal file
View File

@ -0,0 +1,3 @@
<h1><%= message %></h1>
<h2><%= error.status %></h2>
<pre><%= error.stack %></pre>

129
views/footer.ejs Normal file
View File

@ -0,0 +1,129 @@
<!-- Footer -->
<footer
class="text-center text-lg-start text-white"
style="background-color: #1c2331"
>
<!-- Section: Social media -->
<section
class="d-flex justify-content-between p-4"
style="background-color: #6351ce"
>
<!-- Left -->
<div class="me-5">
<span>Get connected with us on social networks:</span>
</div>
<!-- Left -->
<!-- Right -->
<div>
<a href="" class="text-white me-4"><i class="fab fa-facebook-f"></i></a>
<a href="" class="text-white me-4"><i class="fab fa-twitter"></i></a>
<a href="" class="text-white me-4"><i class="fab fa-google"></i></a>
<a href="" class="text-white me-4"><i class="fab fa-instagram"></i></a>
<a href="" class="text-white me-4"><i class="fab fa-linkedin"></i></a>
<a href="" class="text-white me-4"><i class="fab fa-github"></i></a>
</div>
<!-- Right -->
</section>
<!-- Section: Social media -->
<!-- Section: Links -->
<section class="">
<div class="container text-center text-md-start mt-5">
<!-- Grid row -->
<div class="row mt-3">
<!-- Grid column -->
<div class="col-md-3 col-lg-4 col-xl-3 mx-auto mb-4">
<!-- Content -->
<h6 class="text-uppercase fw-bold">Company name</h6>
<hr
class="mb-4 mt-0 d-inline-block mx-auto"
style="width: 60px; background-color: #7c4dff; height: 2px"
/>
<p>
Here you can use rows and columns to organize your footer
content. Lorem ipsum dolor sit amet, consectetur adipisicing
elit.
</p>
</div>
<!-- Grid column -->
<!-- Grid column -->
<div class="col-md-2 col-lg-2 col-xl-2 mx-auto mb-4">
<!-- Links -->
<h6 class="text-uppercase fw-bold">Products</h6>
<hr
class="mb-4 mt-0 d-inline-block mx-auto"
style="width: 60px; background-color: #7c4dff; height: 2px"
/>
<p>
<a href="#!" class="text-white">MDBootstrap</a>
</p>
<p>
<a href="#!" class="text-white">MDWordPress</a>
</p>
<p>
<a href="#!" class="text-white">BrandFlow</a>
</p>
<p>
<a href="#!" class="text-white">Bootstrap Angular</a>
</p>
</div>
<!-- Grid column -->
<!-- Grid column -->
<div class="col-md-3 col-lg-2 col-xl-2 mx-auto mb-4">
<!-- Links -->
<h6 class="text-uppercase fw-bold">Useful links</h6>
<hr
class="mb-4 mt-0 d-inline-block mx-auto"
style="width: 60px; background-color: #7c4dff; height: 2px"
/>
<p>
<a href="#!" class="text-white">Your Account</a>
</p>
<p>
<a href="#!" class="text-white">Become an Affiliate</a>
</p>
<p>
<a href="#!" class="text-white">Shipping Rates</a>
</p>
<p>
<a href="#!" class="text-white">Help</a>
</p>
</div>
<!-- Grid column -->
<!-- Grid column -->
<div class="col-md-4 col-lg-3 col-xl-3 mx-auto mb-md-0 mb-4">
<!-- Links -->
<h6 class="text-uppercase fw-bold">Contact</h6>
<hr
class="mb-4 mt-0 d-inline-block mx-auto"
style="width: 60px; background-color: #7c4dff; height: 2px"
/>
<p><i class="fas fa-home mr-3"></i> New York, NY 10012, US</p>
<p><i class="fas fa-envelope mr-3"></i> info@example.com</p>
<p><i class="fas fa-phone mr-3"></i> + 01 234 567 88</p>
<p><i class="fas fa-print mr-3"></i> + 01 234 567 89</p>
</div>
<!-- Grid column -->
</div>
<!-- Grid row -->
</div>
</section>
<!-- Section: Links -->
<!-- Copyright -->
<div
class="text-center p-3"
style="background-color: rgba(0, 0, 0, 0.2)"
>
© 2020 Copyright:
<a class="text-white" href="https://mdbootstrap.com/"
>MDBootstrap.com</a
>
</div>
<!-- Copyright -->
</footer>
<!-- Footer -->

12
views/header.ejs Normal file
View File

@ -0,0 +1,12 @@
<header class="navbar navbar-dark sticky-top bg-dark flex-md-nowrap p-0 shadow">
<a class="navbar-brand col-md-3 col-lg-2 me-0 px-3" href="/">mCoin</a>
<button class="navbar-toggler position-absolute d-md-none collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#sidebarMenu" aria-controls="sidebarMenu" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<input class="form-control form-control-dark w-100" type="text" placeholder="Search" aria-label="Search">
<ul class="navbar-nav px-3">
<li class="nav-item text-nowrap">
<a class="nav-link" href="#">Sign out</a>
</li>
</ul>
</header>

69
views/index.ejs Normal file
View File

@ -0,0 +1,69 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title><%= title %></title>
<!--<link rel='stylesheet' href='/stylesheets/style.css' />-->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-wEmeIV1mKuiNpC+IOBjI7aAzPcEZeedi5yW5f2yOq55WWLwNGmvvx4Um1vskeMj0" crossorigin="anonymous">
<link rel="stylesheet" href="https://pro.fontawesome.com/releases/v5.10.0/css/all.css" integrity="sha384-AYmEC3Yw5cVb3ZcuHtOA93w35dYTsvhLPVnYs9eStHfGJvOvKxVfELGroGkvsg+p" crossorigin="anonymous"/>
<meta name="theme-color" content="#7952b3">
<link href="/stylesheets/dashboard.css" rel="stylesheet">
</head>
<body>
<%- include('header.ejs') %>
<div class="container-fluid">
<div class="row">
<%- include('sidebar.ejs') %>
<main class="col-md-9 ms-sm-auto col-lg-10 px-md-4">
<%- include('breadcrumb.ejs', {breadcrumb: breadcrumb}) %>
<div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom">
<h1 class="h2">Dashboard</h1>
<div class="btn-toolbar mb-2 mb-md-0">
<div class="btn-group me-2">
<button type="button" class="btn btn-sm btn-outline-secondary">Share</button>
<button type="button" class="btn btn-sm btn-outline-secondary">Export</button>
</div>
<button type="button" class="btn btn-sm btn-outline-secondary dropdown-toggle">
<span data-feather="calendar"></span>
This week
</button>
</div>
</div>
<canvas class="my-4 w-100" id="myChart" width="900" height="380"></canvas>
<h2><%= title %></h2>
<div class="table-responsive">
<table class="table table-striped table-sm">
<thead>
<tr>
<th>#</th>
<th>From</th>
<th>To</th>
<th>Amount</th>
<th>Block Timestamp</th>
</tr>
</thead>
<tbody>
<% data.forEach(function(block) {%>
<tr>
<td><%= block.content.id %></td>
<td><%= block.content.message.from %></td>
<td><%= block.content.message.to %></td>
<td><%= block.content.message.amount %> mCoins</td>
<td><%= block.content.timestamp %></td>
</tr>
<%}); %>
</tbody>
</table>
</div>
<%- include('footer.ejs') %>
</main>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0/dist/js/bootstrap.bundle.min.js" integrity="sha384-p34f1UUtsS3wqzfto5wAAmdvj+osOnFyQFpp4Ua3gs/ZVWx6oOypYoCJhGGScy+8" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/feather-icons@4.28.0/dist/feather.min.js" integrity="sha384-uO3SXW5IuS1ZpFPKugNNWqTZRRglnUJK6UAZ/gxOX80nxEkN9NcGZTftn6RzhGWE" crossorigin="anonymous"></script><script src="https://cdn.jsdelivr.net/npm/chart.js@2.9.4/dist/Chart.min.js" integrity="sha384-zNy6FEbO50N+Cg5wap8IKA4M/ZnLJgzc6w2NqACZaK0u0FXfOWRRJOnQtpZun8ha" crossorigin="anonymous"></script><script src="/javascripts/dashboard.js"></script>
</body>
</html>

21
views/navbar.ejs Normal file
View File

@ -0,0 +1,21 @@
<nav class="navbar navbar-expand-lg navbar-light" style="background-color: #e3f2fd;">
<div class="container-fluid">
<a class="navbar-brand" href="/">mCoin</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav">
<li class="nav-item">
<a class="nav-link active" aria-current="page" href="#">Home</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#">Features</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#">Pricing</a>
</li>
</ul>
</div>
</div>
</nav>

118
views/new-wallet.ejs Normal file
View File

@ -0,0 +1,118 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title><%= title %></title>
<!--<link rel='stylesheet' href='/stylesheets/style.css' />-->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-wEmeIV1mKuiNpC+IOBjI7aAzPcEZeedi5yW5f2yOq55WWLwNGmvvx4Um1vskeMj0" crossorigin="anonymous">
<link rel="stylesheet" href="https://pro.fontawesome.com/releases/v5.10.0/css/all.css" integrity="sha384-AYmEC3Yw5cVb3ZcuHtOA93w35dYTsvhLPVnYs9eStHfGJvOvKxVfELGroGkvsg+p" crossorigin="anonymous"/>
<meta name="theme-color" content="#7952b3">
<link href="/stylesheets/dashboard.css" rel="stylesheet">
<script>
function uuidv4() {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
return v.toString(16);
});
}
</script>
</head>
<body onload="generateWallet()">
<%- include('header.ejs') %>
<div class="container-fluid">
<div class="row">
<%- include('sidebar.ejs') %>
<main class="col-md-9 ms-sm-auto col-lg-10 px-md-4">
<%- include('breadcrumb.ejs', {breadcrumb: breadcrumb}) %>
<div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom">
<h2><%= title %></h2>
</div>
<form class="row g-3">
<div class="mb-3">
<label for="wallet-id" class="form-label">Wallet ID</label>
<input type="email" readonly class="form-control-plaintext" id="wallet-id" placeholder="id@uplink.si">
</div>
<div class="mb-3">
<label for="private-key-id" class="form-label">Private Key</label>
<textarea class="form-control-plaintext" readonly id="private-key-id" rows="5"></textarea>
</div>
<div>
<label for="public-key-id" class="form-label">Public Key</label>
<textarea class="form-control-plaintext" readonly id="public-key-id" rows="5"></textarea>
</div>
<div>
<button onclick="getFingerprintM()" type="button" class="btn btn-primary">Fingerprint</button>
</div>
<div>
<label for="fingerprint-id" class="form-label">Fingerprint</label>
<input class="form-control-plaintext" readonly id="fingerprint-id"></input>
</div>
<div>
<button onclick="saveKey()" type="button" class="btn btn-primary">Save Key</button>
</div>
</form>
</main>
</div>
</div>
<script>
function generateWallet() {
document.getElementById('wallet-id').value = uuidv4() + '@uplink.si';
generateKeys();
}
publicKey;
function generateKeys() {
(async () => {
const { privateKeyArmored, publicKeyArmored, revoationCertificate } = await openpgp.generateKey({
type: 'ecc', // Type of the key, defaults to ECC
curve: 'curve25519', // ECC curve name, defaults to curve25519
userIDs: [{ name: '', email: document.getElementById('wallet-id').value }], // you can pass multiple user IDs
passphrase: 'super long and hard to guess secret' // protects the private key
});
document.getElementById('private-key-id').value = privateKeyArmored;
document.getElementById('public-key-id').value = publicKeyArmored;
//console.log(privateKeyArmored); // '-----BEGIN PGP PRIVATE KEY BLOCK ... '
//console.log(publicKeyArmored); // '-----BEGIN PGP PUBLIC KEY BLOCK ... '
//console.log(revocationCertificate); // '-----BEGIN PGP PUBLIC KEY BLOCK ... '
publicKey = await openpgp.readKey({armoredKey: publicKeyArmored});
})();
}
async function getFingerprintM() {
document.getElementById('fingerprint-id').value = publicKey.getFingerprint();
}
function download(filename, text) {
var element = document.createElement('a');
element.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(text));
element.setAttribute('download', filename);
element.style.display = 'none';
document.body.appendChild(element);
element.click();
document.body.removeChild(element);
}
function saveKey() {
key = {};
key.privateKey = document.getElementById('private-key-id').value;
key.publicKey = document.getElementById('public-key-id').value;
key.fingerprint = document.getElementById('fingerprint-id').value;
key.user = document.getElementById('wallet-id').value;
download('key.txt',JSON.stringify(key));
if(typeof(Storage) !== "undefined") {
localStorage.setItem('privateKey', key.privateKey);
localStorage.setItem('publicKey', key.publicKey);
localStorage.setItem('fingerprint', key.fingerprint);
}
}
</script>
<script src="/javascripts/openpgp.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0/dist/js/bootstrap.bundle.min.js" integrity="sha384-p34f1UUtsS3wqzfto5wAAmdvj+osOnFyQFpp4Ua3gs/ZVWx6oOypYoCJhGGScy+8" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/feather-icons@4.28.0/dist/feather.min.js" integrity="sha384-uO3SXW5IuS1ZpFPKugNNWqTZRRglnUJK6UAZ/gxOX80nxEkN9NcGZTftn6RzhGWE" crossorigin="anonymous"></script><script src="https://cdn.jsdelivr.net/npm/chart.js@2.9.4/dist/Chart.min.js" integrity="sha384-zNy6FEbO50N+Cg5wap8IKA4M/ZnLJgzc6w2NqACZaK0u0FXfOWRRJOnQtpZun8ha" crossorigin="anonymous"></script><script src="/javascripts/dashboard.js"></script>
</body>
</html>

88
views/open-wallet.ejs Normal file
View File

@ -0,0 +1,88 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title><%= title %></title>
<!--<link rel='stylesheet' href='/stylesheets/style.css' />-->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-wEmeIV1mKuiNpC+IOBjI7aAzPcEZeedi5yW5f2yOq55WWLwNGmvvx4Um1vskeMj0" crossorigin="anonymous">
<link rel="stylesheet" href="https://pro.fontawesome.com/releases/v5.10.0/css/all.css" integrity="sha384-AYmEC3Yw5cVb3ZcuHtOA93w35dYTsvhLPVnYs9eStHfGJvOvKxVfELGroGkvsg+p" crossorigin="anonymous"/>
<meta name="theme-color" content="#7952b3">
<link href="/stylesheets/dashboard.css" rel="stylesheet">
<script>
function uuidv4() {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
return v.toString(16);
});
}
</script>
</head>
<body>
<%- include('header.ejs') %>
<div class="container-fluid">
<div class="row">
<%- include('sidebar.ejs') %>
<main class="col-md-9 ms-sm-auto col-lg-10 px-md-4">
<%- include('breadcrumb.ejs', {breadcrumb: breadcrumb}) %>
<div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom">
<h2><%= title %></h2>
</div>
<form class="row g-3">
<div>
<label for="file-selector-id" class="form-label">Open Key</label>
<input class="form-control" type="file" id="file-selector-id" multiple>
</div>
<div>
<button onclick="openKey()" type="button" class="btn btn-primary">Open Key</button>
</div>
<div id="alert">
</div>
</form>
</main>
</div>
</div>
<script>
myKey = {};
const fileSelector = document.getElementById('file-selector-id');
fileSelector.addEventListener('change', (event) => {
const fileList = event.target.files;
console.log(fileList);
readKeyFile(fileList[0]);
});
function readKeyFile(file) {
if(file.type && !file.type.startsWith('text/')) {
console.log('File is not a text file');
return;
}
const reader = new FileReader();
reader.addEventListener('load', (event) => {
key = event.target.result;
console.log(key);
myKey = JSON.parse(key);
});
reader.readAsText(file);
}
function openKey() {
if(typeof(Storage) !== 'undefined') {
localStorage.setItem('privateKey', myKey.privateKey);
localStorage.setItem('publicKey', myKey.publicKey);
localStorage.setItem('fingerprint', myKey.fingerprint);
if(localStorage.getItem('fingerprint') !== 'undefined') {
document.getElementById('alert').innerHTML = '<div class="alert alert-success" role="alert">Key opened successfully. Fingerprint: ' + localStorage.getItem('fingerprint') + '</div>';
} else {
document.getElementById('alert').innerHTML = '<div class="alert alert-danger" role="alert">Key not opened. Try again.</div>';
}
}
}
</script>
<script src="/javascripts/openpgp.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0/dist/js/bootstrap.bundle.min.js" integrity="sha384-p34f1UUtsS3wqzfto5wAAmdvj+osOnFyQFpp4Ua3gs/ZVWx6oOypYoCJhGGScy+8" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/feather-icons@4.28.0/dist/feather.min.js" integrity="sha384-uO3SXW5IuS1ZpFPKugNNWqTZRRglnUJK6UAZ/gxOX80nxEkN9NcGZTftn6RzhGWE" crossorigin="anonymous"></script><script src="https://cdn.jsdelivr.net/npm/chart.js@2.9.4/dist/Chart.min.js" integrity="sha384-zNy6FEbO50N+Cg5wap8IKA4M/ZnLJgzc6w2NqACZaK0u0FXfOWRRJOnQtpZun8ha" crossorigin="anonymous"></script><script src="/javascripts/dashboard.js"></script>
</body>
</html>

76
views/sidebar.ejs Normal file
View File

@ -0,0 +1,76 @@
<nav id="sidebarMenu" class="col-md-3 col-lg-2 d-md-block bg-light sidebar collapse">
<div class="position-sticky pt-3">
<ul class="nav flex-column">
<li class="nav-item">
<a class="nav-link active" aria-current="page" href="/">
<span data-feather="home"></span>
Dashboard
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/wallet">
<span data-feather="book"></span>
Wallet
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/transaction">
<span data-feather="zap"></span>
Transaction
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#">
<span data-feather="users"></span>
Customers
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#">
<span data-feather="bar-chart-2"></span>
Reports
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#">
<span data-feather="layers"></span>
Integrations
</a>
</li>
</ul>
<h6 class="sidebar-heading d-flex justify-content-between align-items-center px-3 mt-4 mb-1 text-muted">
<span>Saved reports</span>
<a class="link-secondary" href="#" aria-label="Add a new report">
<span data-feather="plus-circle"></span>
</a>
</h6>
<ul class="nav flex-column mb-2">
<li class="nav-item">
<a class="nav-link" href="#">
<span data-feather="file-text"></span>
Current month
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#">
<span data-feather="file-text"></span>
Last quarter
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#">
<span data-feather="file-text"></span>
Social engagement
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#">
<span data-feather="file-text"></span>
Year-end sale
</a>
</li>
</ul>
</div>
</nav>

148
views/transaction.ejs Normal file
View File

@ -0,0 +1,148 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title><%= title %></title>
<!--<link rel='stylesheet' href='/stylesheets/style.css' />-->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-wEmeIV1mKuiNpC+IOBjI7aAzPcEZeedi5yW5f2yOq55WWLwNGmvvx4Um1vskeMj0" crossorigin="anonymous">
<link rel="stylesheet" href="https://pro.fontawesome.com/releases/v5.10.0/css/all.css" integrity="sha384-AYmEC3Yw5cVb3ZcuHtOA93w35dYTsvhLPVnYs9eStHfGJvOvKxVfELGroGkvsg+p" crossorigin="anonymous"/>
<meta name="theme-color" content="#7952b3">
<link href="/stylesheets/dashboard.css" rel="stylesheet">
<script>
function uuidv4() {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
return v.toString(16);
});
}
</script>
</head>
<body>
<%- include('header.ejs') %>
<div class="container-fluid">
<div class="row">
<%- include('sidebar.ejs') %>
<main class="col-md-9 ms-sm-auto col-lg-10 px-md-4">
<%- include('breadcrumb.ejs', {breadcrumb:breadcrumb}) %>
<div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom">
<h2><%= title %></h2>
</div>
<form class="row g-3">
<div class="col-md-6">
<div class="input-group mb-3">
<span class="input-group-text" id="basic-addon">From</span>
<input type="text" class="form-control" id="from-id">
</div>
</div>
<div class="col-md-6">
<div class="input-group mb-3">
<span class="input-group-text" id="basic-addon">To</span>
<input type="text" class="form-control" id="to-id">
</div>
</div>
<div class="col-md-12">
<div class="input-group mb-3">
<span class="input-group-text" id="basic-addon">Amount</span>
<input type="" class="form-control" id="amount-id">
</div>
</div>
<div>
<button type="button" class="btn btn-primary btn-lg">Send</button>
</div>
</form>
<div class="mb-3">
<label for="message-id" class="form-label">Plaintext Message</label>
<textarea class="form-control" id="message-id" rows="5"></textarea>
</div>
<div class="mb-3">
<label for="private-key-id" class="form-label">Private Key</label>
<textarea class="form-control" id="private-key-id" rows="5"></textarea>
</div>
<div class="mb-3">
<button type="button" class="btn btn-primary col-auto" onclick="signMessageA()">Sign Message</button>
<button type="button" class="btn btn-primary col-auto">Primary</button>
</div>
<div class="mb-3">
<label for="signed-message-id" class="form-label">Signed Message</label>
<textarea class="form-control" id="signed-message-id" rows="5"></textarea>
</div>
<div class="mb-3">
<label for="public-key-id" class="form-label">Public Key</label>
<textarea class="form-control" id="public-key-id" rows="5"></textarea>
</div>
<div class="mb-3">
<button type="button" class="btn btn-primary col-auto" onclick="verifySig()">Verify Message</button>
</div>
</main>
</div>
</div>
<script>
const passphrase = 'super long and hard to guess secret';
var privateKey;
var privateKeyxx;
var unsignedMessageM;
async function signMessage0() {
privateKeyxx = await openpgp.readKey({ armoredKey: document.getElementById('private-key-id').value });
console.log('readKey done');
}
async function signMessage1() {
privateKey = await openpgp.decryptKey({
privateKey: privateKeyxx,
passphrase
});
console.log('decryptKey done');
}
async function signMessage2() {
unsignedMessageM = await openpgp.createCleartextMessage({ text: document.getElementById('message-id').value });
console.log('createCleartextMessage done');
}
async function signMessage3() {
document.getElementById('signed-message-id').value = await openpgp.sign({
message: unsignedMessageM, // CleartextMessage or Message object
privateKeys: privateKey, // for signing
detached: false
});
}
async function signMessageA() {
await signMessage0();
await signMessage1();
await signMessage2();
await signMessage3();
}
var signedMessage2;
var publicKey;
var verified;
async function verify0() {
signedMessage2 = await openpgp.readCleartextMessage({cleartextMessage: document.getElementById('signed-message-id').value});
}
async function verify1() {
publicKey = await openpgp.readKey({armoredKey: document.getElementById('public-key-id').value});
}
async function verify2() {
verified = await openpgp.verify({message: signedMessage2, publicKeys: publicKey});
}
async function verifySig() {
await verify0();
await verify1();
await verify2();
}
</script>
<script src="/javascripts/openpgp.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0/dist/js/bootstrap.bundle.min.js" integrity="sha384-p34f1UUtsS3wqzfto5wAAmdvj+osOnFyQFpp4Ua3gs/ZVWx6oOypYoCJhGGScy+8" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/feather-icons@4.28.0/dist/feather.min.js" integrity="sha384-uO3SXW5IuS1ZpFPKugNNWqTZRRglnUJK6UAZ/gxOX80nxEkN9NcGZTftn6RzhGWE" crossorigin="anonymous"></script><script src="https://cdn.jsdelivr.net/npm/chart.js@2.9.4/dist/Chart.min.js" integrity="sha384-zNy6FEbO50N+Cg5wap8IKA4M/ZnLJgzc6w2NqACZaK0u0FXfOWRRJOnQtpZun8ha" crossorigin="anonymous"></script><script src="/javascripts/dashboard.js"></script>
</body>
</html>

62
views/wallet.ejs Normal file
View File

@ -0,0 +1,62 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title><%= title %></title>
<!--<link rel='stylesheet' href='/stylesheets/style.css' />-->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-wEmeIV1mKuiNpC+IOBjI7aAzPcEZeedi5yW5f2yOq55WWLwNGmvvx4Um1vskeMj0" crossorigin="anonymous">
<link rel="stylesheet" href="https://pro.fontawesome.com/releases/v5.10.0/css/all.css" integrity="sha384-AYmEC3Yw5cVb3ZcuHtOA93w35dYTsvhLPVnYs9eStHfGJvOvKxVfELGroGkvsg+p" crossorigin="anonymous"/>
<meta name="theme-color" content="#7952b3">
<link href="/stylesheets/dashboard.css" rel="stylesheet">
</head>
<body>
<%- include('header.ejs') %>
<div class="container-fluid">
<div class="row">
<%- include('sidebar.ejs') %>
<main class="col-md-9 ms-sm-auto col-lg-10 px-md-4">
<%- include('breadcrumb.ejs', {breadcrumb: breadcrumb}) %>
<div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom">
<h2><%= title %></h2>
</div>
<div class="card-group">
<div class="card">
<div class="card-header">New Wallet</div>
<div class="card-body">
<h5 class="card-title">Create a New Wallet</h5>
<p class="card-text">If you do not already have a wallet, you can create a new one!</p>
<p class="card-text">Process of creation of new wallet is simple. Just visit the link below and a new wallet will be generated for you. You can then save it to jour computer and use it next time you visit this page.</p>
<p><a href="/wallet/new">New Wallet</a></p>
</div>
</div>
<div class="card">
<div class="card-header">Open Wallet</div>
<div class="card-body">
<h5 class="card-title">Use Existing Wallet</h5>
<p class="card-text">If you already have a wallet, it is easy to open it.</p>
<p class="card-text">Visit link below to open your wallet file. It is easy and secure.</p>
<p><a href="/wallet/open">Open Wallet</a></p>
</div>
</div>
<div class="card">
<div class="card-header">Bring Your Own</div>
<div class="card-body">
<h5 class="card-title">Create Your Own Wallet</h5>
<p class="card-text">Security is hard to get right. Sometimes it is difficult to trust the process of creation of wallet to an online web application.</p>
<p class="card-text">Luckily you can create your own wallet using instructions here. Follow the link to find out how.</p>
<p><a href="/wallet/how-to">Create Your Own</a></p>
</div>
</div>
</div>
</main>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0/dist/js/bootstrap.bundle.min.js" integrity="sha384-p34f1UUtsS3wqzfto5wAAmdvj+osOnFyQFpp4Ua3gs/ZVWx6oOypYoCJhGGScy+8" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/feather-icons@4.28.0/dist/feather.min.js" integrity="sha384-uO3SXW5IuS1ZpFPKugNNWqTZRRglnUJK6UAZ/gxOX80nxEkN9NcGZTftn6RzhGWE" crossorigin="anonymous"></script><script src="https://cdn.jsdelivr.net/npm/chart.js@2.9.4/dist/Chart.min.js" integrity="sha384-zNy6FEbO50N+Cg5wap8IKA4M/ZnLJgzc6w2NqACZaK0u0FXfOWRRJOnQtpZun8ha" crossorigin="anonymous"></script><script src="/javascripts/dashboard.js"></script>
</body>
</html>