aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--app.js16
-rw-r--r--public/js/setup.coffee104
-rw-r--r--public/js/wallet.coffee144
-rw-r--r--routes/users.js15
-rw-r--r--src/app.coffee11
-rw-r--r--src/routes/users.coffee11
-rw-r--r--views/layout.html5
-rw-r--r--views/users/edit.html70
-rw-r--r--views/users/profile.html2
-rw-r--r--views/users/wallet.html78
10 files changed, 266 insertions, 190 deletions
diff --git a/app.js b/app.js
index dab0d431..5f381065 100644
--- a/app.js
+++ b/app.js
@@ -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">&times;</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">&times;</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>