diff options
| -rw-r--r-- | app.js | 16 | ||||
| -rw-r--r-- | public/js/setup.coffee | 104 | ||||
| -rw-r--r-- | public/js/wallet.coffee | 144 | ||||
| -rw-r--r-- | routes/users.js | 15 | ||||
| -rw-r--r-- | src/app.coffee | 11 | ||||
| -rw-r--r-- | src/routes/users.coffee | 11 | ||||
| -rw-r--r-- | views/layout.html | 5 | ||||
| -rw-r--r-- | views/users/edit.html | 70 | ||||
| -rw-r--r-- | views/users/profile.html | 2 | ||||
| -rw-r--r-- | views/users/wallet.html | 78 |
10 files changed, 266 insertions, 190 deletions
@@ -1,5 +1,5 @@ (function() { - var RedisStore, app, authorize, bodyParser, cache, calculator, config, cookieParser, express, fetchRates, fs, passport, path, request, session, sessionStore, sessions, tips, transactions, users; + var RedisStore, app, authorize, bodyParser, cache, calculator, config, cookieParser, express, fetchRates, fs, passport, path, request, session, sessionStore, sessions, transactions, users; request = require('request'); @@ -103,15 +103,15 @@ return setTimeout(fetchRates, 120000); })(); - tips = app.get('/', cache, sessions["new"]); + app.get('/', cache, sessions["new"]); - app.get('/register', cache, users["new"]); + app.get('/address', cache, calculator.address); - app.get('/ticker', cache, calculator.ticker); + app.get('/register', cache, users["new"]); app.get('/sweep', calculator.sweep); - app.get('/address', cache, calculator.address); + app.get('/ticker', cache, calculator.ticker); app.get('/tips', cache, function(req, res) { return res.render('tips', { @@ -138,11 +138,13 @@ app.get('/verify/:token', users.verify); - app.get('/:user/profile', authorize, users.profile); + app.post('/:user', authorize, users.update); app.get('/:user/edit', authorize, users.edit); - app.post('/:user', authorize, users.update); + app.get('/:user/profile', authorize, users.profile); + + app.get('/:user/wallet', authorize, users.wallet); app.get('/:user/transactions.json', authorize, transactions.json); diff --git a/public/js/setup.coffee b/public/js/setup.coffee index 465cfb39..01705cdd 100644 --- a/public/js/setup.coffee +++ b/public/js/setup.coffee @@ -56,7 +56,6 @@ $(-> $('#email').val(data.email) $('#title').val(data.title) $('#logo').val(data.logo) - $('#pubkey').val(data.pubkey) $('#address').val(data.address) $("#symbol option[value='#{data.symbol}']").attr('selected', 'selected') $('#commission').val(data.commission) @@ -72,96 +71,6 @@ $(-> $('#unit').append("<option value='#{v}'>#{v}</option>") ) - $('#pubkey').blur(-> - if check_address($(this).val()) - $(this).parent().removeClass('has-error') - else - $(this).parent().addClass('has-error') - ) - - $('#generate').click(-> - key = new Key() - g.pubkey = key.extended_public_key_string() - g.privkey = key.extended_private_key_string() - - $('#modal').modal() - $('#privkey_text').html(g.privkey) - $('#qr').html('') - new QRCode('qr', text: g.privkey, width: 260, height: 260) - ) - - $('#encryption-password').keyup((e) -> - return if e.keyCode is 9 or e.keyCode is 16 - g.enc_privkey = CryptoJS.AES.encrypt(g.privkey, $(this).val()).toString() - g.enc_privkey = g.privkey if $(this).val() == '' - $('#privkey_text').html(g.enc_privkey) - $('#qr').html('') - new QRCode('qr', text: g.enc_privkey, width: 260, height: 260) - - if $(this).val() - $('#status').html('(Encrypted)') - else - $('#status').html('(Unencrypted)') - ) - - $('#confirm').blur(-> - $('#confirm_error').remove() - if $('#password').val() != $('#confirm').val() - $('#password, #confirm').parent().addClass('has-error') - $('#confirm').parent().after('<div id="confirm_error" class="alert alert-danger">Passwords don\'t match</div>') - else - $('#password, #confirm').parent().removeClass('has-error') - ) - - $('#encryption-password, #encryption-confirm').keyup((e) -> check_passwords(e) unless e.keyCode is 9) - $('#encryption-password, #encryption-confirm').blur((e) -> check_passwords(e)) - - check_passwords = (e) -> - if $('#encryption-password').val() != $('#encryption-confirm').val() or $('#encryption-password').val() is '' - $('#encryption-password, #encryption-confirm').parent().addClass('has-error') - $('#modal .alert').removeClass('alert-success').addClass('alert-danger') - else - $('#encryption-password, #encryption-confirm').parent().removeClass('has-error') - $('#modal .alert').removeClass('alert-danger').addClass('alert-success') - - if $('.progress-bar-success').length > 0 - $('#encryption-password').parent().removeClass('has-error') - else - $('#encryption-password').parent().addClass('has-error') - - $('#print').click(-> - $('#key-dialog').printElement(printMode: 'popup') - ) - - $('a[data-toggle="tab"]').on('shown.bs.tab', (e) -> - $('#print').toggle(e.target.attributes.href.value == '#step1') - ) - - $('#close').click(-> - $('#modal .form-control').blur() - if $('#modal .has-error').length > 0 - $('#step1_link').click() - $('#modal .has-error').effect('shake', 500) - else - g.update_key = true - $('#modal').modal('hide') - ) - $('#encrypt').submit(-> $('#close').click(); return false) - - $('#modal').on('show.bs.modal', -> - $('#encryption-password, #encryption-confirm').val('').keyup().parent().removeClass('has-error') - $('#modal .alert').removeClass('alert-success').addClass('alert-danger') - ) - - $('#modal').on('shown.bs.modal', -> - g.update_key = false - $('#encryption-password').focus() - ).on('hidden.bs.modal', -> - if g.update_key - $('#pubkey').val(g.pubkey) - $('#privkey').val(g.enc_privkey) - ) - $('#setup').submit(-> $('#setup .form-control').blur() if $('#setup .has-error').length > 0 @@ -170,16 +79,3 @@ $(-> ) ) -check_address = (address) -> - try - bitcoin.Address.fromBase58Check(address) - return true - catch - return isBip32(address) - -isBip32 = (address) -> - try - new BIP32(address) - return true - catch - return false diff --git a/public/js/wallet.coffee b/public/js/wallet.coffee new file mode 100644 index 00000000..8ced085f --- /dev/null +++ b/public/js/wallet.coffee @@ -0,0 +1,144 @@ +#= require ../js/jquery-1.8.2.min.js +#= require ../js/jquery-ui.min.js +#= require ../js/jquery.printElement.min.js +#= require ../js/check_password.js +#= require ../js/bootstrap.min.js +#= require ../js/jsbn.js +#= require ../js/jsbn2.js +#= require ../js/crypto-min.js +#= require ../js/2.5.3-crypto-sha256.js +#= require ../js/sha512.js +#= require ../js/rfc1751.js +#= require ../js/bitcoinjs-min-1.0.2.js +#= require ../js/bitcoinjs-min.js +#= require ../js/modsqrt.js +#= require ../js/bip32.js +#= require ../js/secure_random.js +#= require ../js/qrcode.js +#= require ../js/aes.js +#= require ../js/key.js + +g = exports ? this +g.proceed = false + +$(-> + $('#encryption-password').pwstrength(showVerdicts: false) + + user = $('#username').val() + $.getJSON("/#{user}.json", (data) -> + $('#pubkey').val(data.pubkey) + $('#address').val(data.address) + $('#setup').fadeIn() + ) + + $('#pubkey').blur(-> + if check_address($(this).val()) + $(this).parent().removeClass('has-error') + else + $(this).parent().addClass('has-error') + ) + + $('#generate').click(-> + key = new Key() + g.pubkey = key.extended_public_key_string() + g.privkey = key.extended_private_key_string() + + $('#modal').modal() + $('#privkey_text').html(g.privkey) + $('#qr').html('') + new QRCode('qr', text: g.privkey, width: 260, height: 260) + ) + + $('#encryption-password').keyup((e) -> + return if e.keyCode is 9 or e.keyCode is 16 + g.enc_privkey = CryptoJS.AES.encrypt(g.privkey, $(this).val()).toString() + g.enc_privkey = g.privkey if $(this).val() == '' + $('#privkey_text').html(g.enc_privkey) + $('#qr').html('') + new QRCode('qr', text: g.enc_privkey, width: 260, height: 260) + + if $(this).val() + $('#status').html('(Encrypted)') + else + $('#status').html('(Unencrypted)') + ) + + $('#confirm').blur(-> + $('#confirm_error').remove() + if $('#password').val() != $('#confirm').val() + $('#password, #confirm').parent().addClass('has-error') + $('#confirm').parent().after('<div id="confirm_error" class="alert alert-danger">Passwords don\'t match</div>') + else + $('#password, #confirm').parent().removeClass('has-error') + ) + + $('#encryption-password, #encryption-confirm').keyup((e) -> check_passwords(e) unless e.keyCode is 9) + $('#encryption-password, #encryption-confirm').blur((e) -> check_passwords(e)) + + check_passwords = (e) -> + if $('#encryption-password').val() != $('#encryption-confirm').val() or $('#encryption-password').val() is '' + $('#encryption-password, #encryption-confirm').parent().addClass('has-error') + $('#modal .alert').removeClass('alert-success').addClass('alert-danger') + else + $('#encryption-password, #encryption-confirm').parent().removeClass('has-error') + $('#modal .alert').removeClass('alert-danger').addClass('alert-success') + + if $('.progress-bar-success').length > 0 + $('#encryption-password').parent().removeClass('has-error') + else + $('#encryption-password').parent().addClass('has-error') + + $('#print').click(-> + $('#key-dialog').printElement(printMode: 'popup') + ) + + $('a[data-toggle="tab"]').on('shown.bs.tab', (e) -> + $('#print').toggle(e.target.attributes.href.value == '#step1') + ) + + $('#close').click(-> + $('#modal .form-control').blur() + if $('#modal .has-error').length > 0 + $('#step1_link').click() + $('#modal .has-error').effect('shake', 500) + else + g.update_key = true + $('#modal').modal('hide') + ) + $('#encrypt').submit(-> $('#close').click(); return false) + + $('#modal').on('show.bs.modal', -> + $('#encryption-password, #encryption-confirm').val('').keyup().parent().removeClass('has-error') + $('#modal .alert').removeClass('alert-success').addClass('alert-danger') + ) + + $('#modal').on('shown.bs.modal', -> + g.update_key = false + $('#encryption-password').focus() + ).on('hidden.bs.modal', -> + if g.update_key + $('#pubkey').val(g.pubkey) + $('#privkey').val(g.enc_privkey) + ) + + $('#setup').submit(-> + $('#setup .form-control').blur() + if $('#setup .has-error').length > 0 + $('#setup .has-error').effect('shake', 500) + return false + ) +) + +check_address = (address) -> + try + bitcoin.Address.fromBase58Check(address) + return true + catch + return isBip32(address) + +isBip32 = (address) -> + try + new BIP32(address) + return true + catch + return false diff --git a/routes/users.js b/routes/users.js index 28b89797..36914152 100644 --- a/routes/users.js +++ b/routes/users.js @@ -54,7 +54,7 @@ if (req.query.verified != null) { options.verified = true; } - if (obj.logo.length > 3) { + if (obj.logo && obj.logo.length > 3) { ext = obj.logo.substr(obj.logo.length - 3); path = "public/assets/img/logos/" + obj.username + "." + ext; fs.lstat(path, function(err, stats) { @@ -243,6 +243,19 @@ }); } }); + }, + wallet: function(req, res) { + return res.render('users/wallet', { + user: req.params.user, + layout: 'layout', + navigation: true, + js: (function() { + return global.js; + }), + css: (function() { + return global.css; + }) + }); } }; }; diff --git a/src/app.coffee b/src/app.coffee index f62b7696..554ef303 100644 --- a/src/app.coffee +++ b/src/app.coffee @@ -62,13 +62,11 @@ do fetchRates = -> ) setTimeout(fetchRates, 120000) -tips = - app.get('/', cache, sessions.new) +app.get('/address', cache, calculator.address) app.get('/register', cache, users.new) -app.get('/ticker', cache, calculator.ticker) app.get('/sweep', calculator.sweep) -app.get('/address', cache, calculator.address) +app.get('/ticker', cache, calculator.ticker) app.get('/tips', cache, (req, res) -> res.render('tips', notice: true, @@ -86,9 +84,10 @@ app.get('/users/new', cache, users.new) app.post('/users', users.create) app.get('/verify/:token', users.verify) -app.get('/:user/profile', authorize, users.profile) -app.get('/:user/edit', authorize, users.edit) app.post('/:user', authorize, users.update) +app.get('/:user/edit', authorize, users.edit) +app.get('/:user/profile', authorize, users.profile) +app.get('/:user/wallet', authorize, users.wallet) app.get('/:user/transactions.json', authorize, transactions.json) app.post('/:user/transactions', transactions.create) diff --git a/src/routes/users.coffee b/src/routes/users.coffee index b1e4fd9a..014b74b3 100644 --- a/src/routes/users.coffee +++ b/src/routes/users.coffee @@ -35,7 +35,7 @@ module.exports = (sessions) -> if req.query.verified? options.verified = true - if obj.logo.length > 3 + if obj.logo and obj.logo.length > 3 ext = obj.logo.substr(obj.logo.length - 3) path = "public/assets/img/logos/#{obj.username}.#{ext}" fs.lstat(path, (err, stats) -> @@ -192,3 +192,12 @@ module.exports = (sessions) -> res.redirect("/#{reply.toString()}?verified") ) ) + + wallet: (req, res) -> + res.render('users/wallet', + user: req.params.user, + layout: 'layout', + navigation: true, + js: (-> global.js), + css: (-> global.css) + ) diff --git a/views/layout.html b/views/layout.html index 5be45ded..e63e3c47 100644 --- a/views/layout.html +++ b/views/layout.html @@ -20,8 +20,9 @@ <div class="navbar"> <ul class="nav navbar-nav"> <li><a href="/{{user}}"><span class="glyphicon glyphicon-home"></span> Home</a></li> - <li><a href="/{{user}}/report"><span class="glyphicon glyphicon-list-alt"></span> Transactions</a></li> - <li><a href="/{{user}}/profile"><span class="glyphicon glyphicon-user"></span> Account Profile</a></li> + <li><a href="/{{user}}/report"><span class="glyphicon glyphicon-list-alt"></span> Sales</a></li> + <li><a href="/{{user}}/wallet"><span class="fa fa-bank"></span> Wallet</a></li> + <li><a href="/{{user}}/profile"><span class="glyphicon glyphicon-user"></span> Account</a></li> <li><a href="/{{user}}/edit"><span class="glyphicon glyphicon-cog"></span> Settings</a></li> <li><a href="/logout"><span class="glyphicon glyphicon-off"></span> Logout</a></li> <!-- diff --git a/views/users/edit.html b/views/users/edit.html index 0e0af3df..344d4543 100644 --- a/views/users/edit.html +++ b/views/users/edit.html @@ -9,27 +9,16 @@ <input type="hidden" id="email" name="email" /> <div class="form-group"> - <label for="title">Store Name:</label> + <label for="title">Store Name <small>(optional)</small>:</label> <input type="text" id="title" name="title" class="form-control" /> </div> <div class="form-group"> - <label for="logo">Logo:</label> + <label for="logo">Logo <small>(optional)</small>:</label> <input type="text" id="logo" name="logo" class="form-control" /> </div> <div class="form-group"> - <label for="pubkey">Bitcoin Public Key:</label> - <div class="input-group"> - <input type="text" id="pubkey" name="pubkey" class="form-control" placeholder="xpub..." /> - <input type="hidden" id="privkey" name="privkey" /> - <span class="input-group-btn"> - <button id="generate" type="button" class="btn">New</button> - </span> - </div> - </div> - - <div class="form-group"> <label for="currency">Currency:</label> <select id="currency" name="currency" class="form-control"> </select> @@ -54,58 +43,3 @@ <input class="btn btn-primary" type="submit" value="Done" /> </form> - -<div class="modal fade" id="modal" tabindex="-1" role="dialog"> - <div class="modal-dialog"> - <div class="modal-content"> - <div class="modal-header"> - <button type="button" class="close" data-dismiss="modal"><span aria-hidden="true">×</span><span class="sr-only">Close</span></button> - <h4 class="modal-title">New Extended Key</h4> - </div> - - <div class="modal-body"> - <ul class="nav nav-tabs" role="tablist"> - <li class="active"><a id="step1_link" href="#step1" role="tab" data-toggle="tab">Key</a></li> - <li><a href="#step2" role="tab" data-toggle="tab">What's this?</a></li> - </ul> - <div class="tab-content"> - <div id="step1" class="tab-pane active"> - <form id="encrypt"> - <div class="form-group"> - <label for="encryption-password">Encryption Password:</label> - <input type="password" id="encryption-password" name="encryption-password" class="form-control" /> - </div> - - <div class="form-group"> - <label for="encryption-confirm">Confirm Password:</label> - <input type="password" id="encryption-confirm" name="encryption-confirm" class="form-control" /> - </div> - - <input type="submit" style="position: absolute; left: -9999px" /> - </form> - - <div id="key-dialog" class="alert alert-danger"> - Extended Private Key <span id="status">(Unencrypted)</span> - <pre id="privkey_text"></pre> - <div id="qr"></div> - </div> - </div> - <div id="step2" class="tab-pane"> - <p>This is a Bitcoin extended key, also known as an <a href="https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki"><abbr title="Hierarchical Deterministic Wallet">HD Wallet</abbr></a>.</p> - <p>The <b>public</b> portion of the key is stored and used by CoinOS to generate a separate receiving address for every payment to your account. This prevents people from seeing transactions to your account other than their own.</p> - <p>The <b>private</b> portion of the key (displayed on the 'Key' tab) will not be accessible after you close this window. You are responsible for safely storing it and making copies of it, either by printing it out or saving it somewhere on your computer or online.</p> - <p>Encrypting the key with a strong password provides a safeguard against someone gaining access to a copy of the key. They will need to know your password to decrypt the key. Keep the password written down and backed up separately from the key.</p> - <p class="text-danger"><b>IMPORTANT: We cannot reset your password. If you lose it or forget it, any funds sent to your account will be lost forever.</b></p> - - <img src="/assets/img/forever.gif" width="370px" /> - </div> - - </div> - </div> - <div class="modal-footer"> - <button type="button" id="print" class="btn btn-primary"><span class="glyphicon glyphicon-print"></span> Print</button> - <button type="button" id="close" class="btn btn-primary">Got it!</button> - </div> - </div> - </div> -</div> diff --git a/views/users/profile.html b/views/users/profile.html index 6d05d61d..dffa559c 100644 --- a/views/users/profile.html +++ b/views/users/profile.html @@ -21,7 +21,7 @@ </div> <div class="form-group"> - <label for="confirm">Confirm New Password:</label> + <label for="confirm">Confirm Password:</label> <input type="password" id="confirm" name="confirm" class="form-control" /> </div> diff --git a/views/users/wallet.html b/views/users/wallet.html new file mode 100644 index 00000000..f17b4ae6 --- /dev/null +++ b/views/users/wallet.html @@ -0,0 +1,78 @@ +{{#yield-scripts}} + {{#js}}js/wallet{{/js}} +{{/yield-scripts}} + +<form id="setup" action="/{{user}}" method="post" style="display: none"> + <h1>Wallet</h1> + + <input type="hidden" id="username" name="username" value="{{user}}" /> + <input type="hidden" id="email" name="email" /> + + <div class="form-group"> + <label for="pubkey">Bitcoin Public Key:</label> + <div class="input-group"> + <input type="text" id="pubkey" name="pubkey" class="form-control" placeholder="xpub..." /> + <input type="hidden" id="privkey" name="privkey" /> + <span class="input-group-btn"> + <button id="generate" type="button" class="btn">New</button> + </span> + </div> + </div> + + <input class="btn btn-primary" type="submit" value="Done" /> +</form> + +<div class="modal fade" id="modal" tabindex="-1" role="dialog"> + <div class="modal-dialog"> + <div class="modal-content"> + <div class="modal-header"> + <button type="button" class="close" data-dismiss="modal"><span aria-hidden="true">×</span><span class="sr-only">Close</span></button> + <h4 class="modal-title">New Extended Key</h4> + </div> + + <div class="modal-body"> + <ul class="nav nav-tabs" role="tablist"> + <li class="active"><a id="step1_link" href="#step1" role="tab" data-toggle="tab">Key</a></li> + <li><a href="#step2" role="tab" data-toggle="tab">What's this?</a></li> + </ul> + <div class="tab-content"> + <div id="step1" class="tab-pane active"> + <form id="encrypt"> + <div class="form-group"> + <label for="encryption-password">Encryption Password:</label> + <input type="password" id="encryption-password" name="encryption-password" class="form-control" /> + </div> + + <div class="form-group"> + <label for="encryption-confirm">Confirm Password:</label> + <input type="password" id="encryption-confirm" name="encryption-confirm" class="form-control" /> + </div> + + <input type="submit" style="position: absolute; left: -9999px" /> + </form> + + <div id="key-dialog" class="alert alert-danger"> + Extended Private Key <span id="status">(Unencrypted)</span> + <pre id="privkey_text"></pre> + <div id="qr"></div> + </div> + </div> + <div id="step2" class="tab-pane"> + <p>This is a Bitcoin extended key, also known as an <a href="https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki"><abbr title="Hierarchical Deterministic Wallet">HD Wallet</abbr></a>.</p> + <p>The <b>public</b> portion of the key is stored and used by CoinOS to generate a separate receiving address for every payment to your account. This prevents people from seeing transactions to your account other than their own.</p> + <p>The <b>private</b> portion of the key (displayed on the 'Key' tab) will not be accessible after you close this window. You are responsible for safely storing it and making copies of it, either by printing it out or saving it somewhere on your computer or online.</p> + <p>Encrypting the key with a strong password provides a safeguard against someone gaining access to a copy of the key. They will need to know your password to decrypt the key. Keep the password written down and backed up separately from the key.</p> + <p class="text-danger"><b>IMPORTANT: We cannot reset your password. If you lose it or forget it, any funds sent to your account will be lost forever.</b></p> + + <img src="/assets/img/forever.gif" width="370px" /> + </div> + + </div> + </div> + <div class="modal-footer"> + <button type="button" id="print" class="btn btn-primary"><span class="glyphicon glyphicon-print"></span> Print</button> + <button type="button" id="close" class="btn btn-primary">Got it!</button> + </div> + </div> + </div> +</div> |
