From c413e457579b68b7a92ab47cdd867202ee7bb47e Mon Sep 17 00:00:00 2001 From: Adam Soltys Date: Sat, 21 Jan 2017 18:56:05 -0800 Subject: Ditch coffeescript --- app.js | 357 +++++++++++++++-------------- package.json | 29 ++- src/app.coffee | 151 ------------- src/bcoin.coffee | 87 -------- src/components/SignUp.vue | 39 ++++ src/install.coffee | 16 -- src/main.js | 9 +- src/passport.coffee | 35 --- src/redis.coffee | 7 - src/routes/sessions.coffee | 25 --- src/routes/transactions.coffee | 105 --------- src/routes/users.coffee | 31 --- static/js/calculator.coffee | 268 ---------------------- static/js/generate_address.coffee | 74 ------- static/js/index.coffee | 44 ---- static/js/profile.coffee | 30 --- static/js/register.coffee | 53 ----- static/js/setup.coffee | 65 ------ static/js/setup.js | 4 +- static/js/signin.coffee | 6 - static/js/wallet.coffee | 456 -------------------------------------- 21 files changed, 247 insertions(+), 1644 deletions(-) delete mode 100644 src/app.coffee delete mode 100644 src/bcoin.coffee create mode 100644 src/components/SignUp.vue delete mode 100644 src/install.coffee delete mode 100644 src/passport.coffee delete mode 100644 src/redis.coffee delete mode 100644 src/routes/sessions.coffee delete mode 100644 src/routes/transactions.coffee delete mode 100644 src/routes/users.coffee delete mode 100644 static/js/calculator.coffee delete mode 100644 static/js/generate_address.coffee delete mode 100644 static/js/index.coffee delete mode 100644 static/js/profile.coffee delete mode 100644 static/js/register.coffee delete mode 100644 static/js/setup.coffee delete mode 100644 static/js/signin.coffee delete mode 100644 static/js/wallet.coffee diff --git a/app.js b/app.js index 0703d01f..260fa207 100644 --- a/app.js +++ b/app.js @@ -1,193 +1,186 @@ (function() { - var app, config, engines, express, fetchRates, fs, path, request, route, routes, view, _fn; - - fs = require('fs'); - - express = require('express'); - - path = require('path'); - - engines = require('consolidate'); - - request = require('request'); - - config = require('./config'); - - app = express(); - - app.enable('trust proxy'); - - app.engine('html', require('mmm').__express); - - app.set('view engine', 'html'); - - app.set('views', __dirname + '/views'); - - app.use(express["static"](__dirname + '/public')); - - app.use(express.bodyParser()); + const indexOf = [].indexOf || + function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i } return -1 } + + const request = require('request') + const express = require('express') + const bodyParser = require('body-parser') + const cookieParser = require('cookie-parser') + const path = require('path') + const passport = require('./passport') + const config = require('./config') + const fs = require('fs') + const proxyMiddleware = require('http-proxy-middleware') + const sessions = require("./routes/sessions")(passport) + const transactions = require("./routes/transactions") + const twilio = require('twilio') + const users = require("./routes/users")(sessions) + const session = require('express-session') + const RedisStore = require('connect-redis')(session) + + sessionStore = new RedisStore(require('./redis').host, { + ttl: 172800 + }) + + proxyContext = '/blockcypher' + + proxyOptions = { + target: 'https://api.blockcypher.com', + changeOrigin: true, + pathRewrite: { + '^/blockcypher/': '/' + }, + onProxyReq: function(proxyReq, req, res) { + var symbol + symbol = indexOf.call(proxyReq.path, '?') >= 0 ? '&' : '?' + return proxyReq.path += symbol + "token=" + config.blockcypher_token + } + } - app.use(express.cookieParser()); + proxy = proxyMiddleware(proxyContext, proxyOptions) + + app = express() + app.enable('trust proxy') + app.engine('html', require('hogan-express')) + app.set('view engine', 'html') + app.set('views', __dirname + '/views') + app.use(express["static"](__dirname + '/public')) + app.use(proxy) + + app.use(bodyParser.urlencoded({ + extended: true + })) + + app.use(bodyParser.json()) + + app.use(bodyParser.json({ + type: 'application/vnd.api+json' + })) + + app.use(cookieParser(config.secret)) + + app.use(session({ + resave: true, + saveUninitialized: true, + secret: config.secret, + store: sessionStore, + cookie: { + maxAge: 1209600000 + }, + key: 'coinos.sid' + })) + + app.use(passport.initialize()) + + app.use(passport.session()) + + const authorize = function(req, res, next) { + var ref, ref1 + if (req.params.user === ((ref = req.user) != null ? ref.username : void 0) || ((ref1 = req.user) != null ? ref1.username : void 0) === 'admin') { + return next() + } + req.session.redirect = req.path + return res.redirect('/login') + } - app.use(app.router); + const cache = function(req, res, next) { + if (req.path !== '/login') { + res.setHeader("Cache-Control", "public, max-age=900") + } + return next() + } + let fetchRates (fetchRates = function() { request("https://api.bitcoinaverage.com/exchanges/all", function(error, response, body) { - var file, stream; - try { - require('util').isDate(JSON.parse(body).timestamp); - file = 'public/js/rates.json'; - stream = fs.createWriteStream(file); - return fs.truncate(file, 0, function() { - return stream.write(body); - }); - } catch (_error) {} - }); - return setTimeout(fetchRates, 120000); - })(); - - routes = { - '/': 'index', - '/about': 'about', - '/coinfest': 'coinfest', - '/coinos': 'coinos', - '/contact': 'contact', - '/directors': 'directors', - '/membership': 'membership', - '/merchants': 'merchants', - '/partners': 'partners', - '/register': 'register' - }; - - _fn = function(route, view) { - return app.get(route, function(req, res) { - return res.render(view, { - js: (function() { - return global.js; - }), - css: (function() { - return global.css; - }), - layout: 'layout' - }); - }); - }; - for (route in routes) { - view = routes[route]; - _fn(route, view); - } - - app.get('/users', function(req, res) { - var db; - db = require('./redis'); - return db.keys('member:*', function(err, keys) { - var i, key, users, _i, _len, _results; - users = []; - _results = []; - for (i = _i = 0, _len = keys.length; _i < _len; i = ++_i) { - key = keys[i]; - _results.push((function(i, db) { - return db.hgetall(key, function(err, user) { - if (!user["private"]) { - users.push(user); - } - if (i >= keys.length - 1) { - users.sort(function(a, b) { - if (parseInt(a.number) < parseInt(b.number)) { - return -1; - } - if (parseInt(a.number) > parseInt(b.number)) { - return 1; - } - return 0; - }); - res.write(JSON.stringify(users)); - return res.end(); - } - }); - })(i, db)); - } - return _results; - }); - }); - - app.post('/users', function(req, res) { - var db, userkey; - db = require('./redis'); - userkey = "member:" + req.body.email; - return db.hgetall(userkey, function(err, obj) { - if (obj) { - res.status(500).send("Sorry, that email address is already registered"); - return; - } - db.sadd("users", userkey); - return db.incr('members', function(err, number) { - return db.hmset(userkey, { - name: req.body.name, - email: req.body.email, - address: req.body.address, - number: number, - date: req.body.date, - txid: req.body.txid - }, function(err, obj) { - var email; - if (true || process.env.NODE_ENV === 'production') { - email = req.body.email; - res.render('welcome', { - layout: 'mail', - js: (function() { - return global.js; - }), - css: (function() { - return global.css; - }) - }, function(err, html) { - var sendgrid; - sendgrid = require('sendgrid')(config.sendgrid_user, config.sendgrid_password); - email = new sendgrid.Email({ - to: email, - from: 'info@bitcoincoop.org', - subject: 'Welcome to the Co-op!', - html: html - }); - return sendgrid.send(email); - }); - } - return res.end(); - }); - }); - }); - }); - - app.get('/ticker', function(req, res) { - var fd; - fs = require('fs'); - return fd = fs.readFile("./public/js/rates.json", function(err, data) { - var e, exchange, _base, _base1, _base2; - (_base = req.query).currency || (_base.currency = 'CAD'); - (_base1 = req.query).symbol || (_base1.symbol = 'quadrigacx'); - (_base2 = req.query).type || (_base2.type = 'bid'); try { - exchange = JSON.parse(data)[req.query.currency][req.query.symbol]['rates'][req.query.type].toString(); - } catch (_error) { - e = _error; - exchange = "0"; - } - res.writeHead(200, { - 'Content-Length': exchange.length, - 'Content-Type': 'text/plain' - }); - res.write(exchange); - return res.end(); - }); - }); + require('util').isDate(JSON.parse(body).timestamp) + app.set('rates', body) + } catch (undefined) {} + }) + return setTimeout(fetchRates, 120000) + })() + + require('./bcoin').init(app) + + app.get('/', cache, sessions["new"]) + + app.get('/address', cache, function(req, res) { + return res.render('address', { + layout: 'layout', + js: (function() { + return global.js + }), + css: (function() { + return global.css + }) + }) + }) + + app.get('/rates', cache, function(req, res) { + res.write(app.get('rates')) + }) + + app.get('/ticker', cache, function(req, res) { + fs = require('fs') + var base, base1, base2, e, error1, exchange + (base = req.query).currency || (base.currency = 'CAD') + (base1 = req.query).symbol || (base1.symbol = 'quadrigacx') + (base2 = req.query).type || (base2.type = 'bid') + try { + exchange = JSON.parse(app.get('rates'))[req.query.currency][req.query.symbol]['rates'][req.query.type].toString() + } catch (error1) { + e = error1 + exchange = "0" + } + res.writeHead(200, { + 'Content-Length': exchange.length, + 'Content-Type': 'text/plain' + }) + res.write(exchange) + return res.end() + }) + + app.get('/tips', cache, function(req, res) { + return res.render('tips', { + notice: true, + layout: 'layout', + js: (function() { + return global.js + }), + css: (function() { + return global.css + }) + }) + }) + + app.get('/login', cache, sessions["new"]) + app.post('/login', sessions.create) + app.get('/logout', sessions.destroy) + app.get('/users.json', users.index) + app.get('/register', cache, users["new"]) + app.get('/users/new', cache, users["new"]) + app.post('/users', users.create) + app.get('/verify/:token', users.verify) + 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) + app.post('/transactions/:txid', transactions.update) + app["delete"]('/:user/transactions/:txid', transactions["delete"]) + app.get('/:user/report', authorize, transactions.index) + app.get('/:user.json', users.json) + app.get('/:user', cache, users.show) app.use(function(err, req, res, next) { - res.status(500); - console.log(err); - return res.end(); - }); + res.status(500) + res.send('An error occurred') + console.error(err.stack) + return res.end() + }) - app.listen(3002); + app.listen(3000) -}).call(this); +}).call(this) diff --git a/package.json b/package.json index 2be452f9..4e3be1d5 100644 --- a/package.json +++ b/package.json @@ -17,6 +17,7 @@ "redis": "^2.6.3", "slick-carousel": "^1.6.0", "vue": "^2.1.0", + "vue-resource": "^1.0.3", "vue-router": "^2.1.1" }, "devDependencies": { @@ -31,7 +32,6 @@ "chai": "^3.5.0", "chalk": "^1.1.3", "chromedriver": "^2.21.2", - "coffee-loader": "^0.7.2", "connect-history-api-fallback": "^1.1.0", "cross-spawn": "^4.0.2", "css-loader": "^0.25.0", @@ -84,7 +84,32 @@ "webpack": "^1.13.2", "webpack-dev-middleware": "^1.8.3", "webpack-hot-middleware": "^2.12.2", - "webpack-merge": "^0.14.1" + "webpack-merge": "^0.14.1", + "async": "2.1.1", + "bcoin": "git+https://github.com/bcoin-org/bcoin.git#master", + "bcryptjs": "^2.3.0", + "bip39": "^2.2.0", + "bitcoinjs-lib": "^2.3.0", + "bluebird": "^3.4.6", + "body-parser": "^1.5.0", + "browser-sync": "^2.11.0", + "connect-assets": "*", + "connect-redis": "3.1.0", + "consolidate": "0.14.1", + "cookie-parser": "^1.3.2", + "express": "4.14.0", + "express-session": "^1.6.5", + "hogan-express": "0.5.2", + "http-proxy-middleware": "^0.17.2", + "less": "2.7.1", + "moment": "^2.10.6", + "node-http-proxy": "^0.2.3", + "passport": "0.3.2", + "passport-local": "1.0.0", + "redis": "2.6.2", + "request": "~2.75.0", + "sendgrid": "^4.3.0", + "twilio": "^2.11.1" }, "engines": { "node": ">= 4.0.0", diff --git a/src/app.coffee b/src/app.coffee deleted file mode 100644 index 1fc2099c..00000000 --- a/src/app.coffee +++ /dev/null @@ -1,151 +0,0 @@ -request = require('request') -express = require('express') -bodyParser = require('body-parser') -cookieParser = require('cookie-parser') -path = require('path') -passport = require('./passport') -config = require('./config') -fs = require('fs') -proxyMiddleware = require('http-proxy-middleware') -sessions = require("./routes/sessions")(passport) -transactions = require("./routes/transactions") -twilio = require('twilio') -users = require("./routes/users")(sessions) - -session = require('express-session') -RedisStore = require('connect-redis')(session) -sessionStore = new RedisStore(require('./redis').host, ttl: 172800) - - -proxyContext = '/blockcypher' -proxyOptions = - target: 'https://api.blockcypher.com' - changeOrigin: true - pathRewrite: - '^/blockcypher/': '/' - onProxyReq: (proxyReq, req, res) -> - symbol = if '?' in proxyReq.path then '&' else '?' - proxyReq.path += "#{symbol}token=#{config.blockcypher_token}" - -proxy = proxyMiddleware(proxyContext, proxyOptions) - -app = express() -app.enable('trust proxy') -app.engine('html', require('hogan-express')) -app.set('view engine', 'html') -app.set('views', __dirname + '/views') -app.use(express.static(__dirname + '/public')) -app.use(proxy) -app.use(bodyParser.urlencoded({ extended: true})) -app.use(bodyParser.json()) -app.use(bodyParser.json({ type: 'application/vnd.api+json' })) -app.use(cookieParser(config.secret)) -app.use(session( - resave: true - saveUninitialized: true - secret: config.secret - store: sessionStore - cookie: maxAge: 1209600000 - key: 'coinos.sid' -)) -app.use(passport.initialize()) -app.use(passport.session()) - -authorize = (req, res, next) -> - if req.params.user is req.user?.username or - req.user?.username is 'admin' - return next() - - req.session.redirect = req.path - res.redirect('/login') - -cache = (req, res, next) -> - unless req.path is '/login' - res.setHeader "Cache-Control", "public, max-age=900" - next() - -do fetchRates = -> - request("https://api.bitcoinaverage.com/exchanges/all", (error, response, body) -> - try - require('util').isDate(JSON.parse(body).timestamp) - file = 'public/js/rates.json' - stream = fs.createWriteStream(file) - fs.truncate(file, 0, -> - stream.write(body) - ) - ) - setTimeout(fetchRates, 120000) - -require('./bcoin').init(app) - -app.get('/', cache, sessions.new) - -app.get('/address', cache, (req, res) -> - res.render('address', - layout: 'layout', - js: (-> global.js), - css: (-> global.css) - ) -) - -app.get('/ticker', cache, (req, res) -> - fs = require('fs') - fs.readFile("./public/js/rates.json", (err, data) -> - req.query.currency ||= 'CAD' - req.query.symbol ||= 'quadrigacx' - req.query.type ||= 'bid' - - try - exchange = JSON.parse(data)[req.query.currency][req.query.symbol]['rates'][req.query.type].toString() - catch e - exchange = "0" - - res.writeHead(200, - 'Content-Length': exchange.length, - 'Content-Type': 'text/plain') - res.write(exchange) - res.end() - ) -) - -app.get('/tips', cache, (req, res) -> - res.render('tips', - notice: true, - layout: 'layout', - js: (-> global.js), - css: (-> global.css) - ) -) - -app.get('/login', cache, sessions.new) -app.post('/login', sessions.create) -app.get('/logout', sessions.destroy) - -app.get('/users.json', users.index) -app.get('/register', cache, users.new) -app.get('/users/new', cache, users.new) -app.post('/users', users.create) -app.get('/verify/:token', users.verify) - -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) -app.post('/transactions/:txid', transactions.update) -app.delete('/:user/transactions/:txid', transactions.delete) -app.get('/:user/report', authorize, transactions.index) - -app.get('/:user.json', users.json) -app.get('/:user', cache, users.show) - -app.use((err, req, res, next) -> - res.status(500) - res.send('An error occurred') - console.error(err.stack) - res.end() -) - -app.listen(3000) diff --git a/src/bcoin.coffee b/src/bcoin.coffee deleted file mode 100644 index d0a8a089..00000000 --- a/src/bcoin.coffee +++ /dev/null @@ -1,87 +0,0 @@ -bcoin = require('bcoin') -config = require('./config') -db = require('./redis') -twilio = require('twilio') - -module.exports = - init: (app) -> - users = {} - - chain = new bcoin.chain( - db: 'leveldb' - name: 'spvchain' - location: __dirname + '/db/spvchain' - spv: true - ) - - pool = new bcoin.pool( - chain: chain - spv: true - size: 1 - maxPeers: 1 - seeds: ['dctrl.ca'] - ) - - # pool.logger.level = 4 - - pool.open().then(-> - db.keysAsync("user:*").then((keys) -> - Promise.all(keys.map((key) -> - db.hgetallAsync(key).then((user) -> - if user.address - pool.watchAddress(user.address) - users[user.address] = - email: user.email - currency: user.currency - symbol: user.symbol - phone: user.phone - ) - )) - ) - - pool.connect() - pool.startSync() - - pool.on('error', (err) -> - console.log(err) - ) - - pool.on('tx', (tx) -> - for output in tx.outputs - value = (output.value / 100000000).toFixed(8) - address = output.getAddress().toBase58() - if Object.keys(users).includes(address) - app.render('payment', { value: value, address: address }, (err, html) -> - helper = require('sendgrid').mail - from_email = new helper.Email('info@coinos.io') - to_email = new helper.Email(users[address].email) - subject = 'Received Payment' - content = new helper.Content('text/html', html) - mail = new helper.Mail(from_email, subject, to_email, content) - - sg = require('sendgrid')(config.sendgrid_token) - request = sg.emptyRequest( - method: 'POST' - path: '/v3/mail/send' - body: mail.toJSON() - ) - - sg.API(request, (error, response) -> - console.log(response.statusCode) - console.log(response.body) - console.log(response.headers) - ) - ) - - if users[address].phone - client = new twilio.RestClient(config.twilio_sid, config.twilio_token) - - client.messages.create( - to: user.phone - from: config.twilio_number - body: "You received a payment of #{value} BTC" - , (err, message) -> - console.log(message.sid) - ) - ) - ) diff --git a/src/components/SignUp.vue b/src/components/SignUp.vue new file mode 100644 index 00000000..51583b82 --- /dev/null +++ b/src/components/SignUp.vue @@ -0,0 +1,39 @@ + + + diff --git a/src/install.coffee b/src/install.coffee deleted file mode 100644 index 7ba2449d..00000000 --- a/src/install.coffee +++ /dev/null @@ -1,16 +0,0 @@ -db = require("./redis") -bcrypt = require('bcryptjs') - -db.get("user:admin", (err, res) -> - if !res - bcrypt.hash('admin', 12, (err, hash) -> - db.sadd("users","user:admin") - db.hmset("user:admin", - username: 'admin' - password: hash, - , -> - console.log("Created admin user with password 'admin'") - process.exit() - ) - ) -) diff --git a/src/main.js b/src/main.js index 0fb682f3..cfaeb1ac 100644 --- a/src/main.js +++ b/src/main.js @@ -1,18 +1,18 @@ -// The Vue build version to load with the `import` command -// (runtime-only or standalone) has been set in webpack.base.conf with an alias. import Vue from 'vue' import VueRouter from 'vue-router' import App from './App' import SignIn from './components/SignIn' +import SignUp from './components/SignUp' import Sales from './components/Sales' const routes = [ - { path: '/', component: SignIn }, + { path: '/signin', component: SignIn }, + { path: '/signup', component: SignUp }, { path: '/sales', component: Sales } ] const router = new VueRouter({ - routes // short for routes: routes + routes }) Vue.use(VueRouter) @@ -24,4 +24,3 @@ new Vue({ components: { App }, router: router }) - diff --git a/src/passport.coffee b/src/passport.coffee deleted file mode 100644 index 7fd99668..00000000 --- a/src/passport.coffee +++ /dev/null @@ -1,35 +0,0 @@ -db = require("redis").createClient() -bcrypt = require('bcryptjs') - -passport = require('passport') -LocalStrategy = require('passport-local').Strategy - -passport.serializeUser((user, done) -> - done(null, user.username.toLowerCase()) -) - -passport.deserializeUser((username, done) -> - db.hgetall("user:"+username.toLowerCase(), (err, user) -> - return done(null, user) - ) -) - -passport.use(new LocalStrategy( - (username, password, done) -> - db.hget("user:"+username.toLowerCase(), 'password', (err, hash) -> - return done(err) if err - if hash - bcrypt.compare(password, hash, (err, match) -> - if match - db.hgetall("user:"+username.toLowerCase(), (err, user) -> - return done(null, user) - ) - else - return done(null, false) - ) - else - return done(null, false) - ) -)) - -module.exports = passport diff --git a/src/redis.coffee b/src/redis.coffee deleted file mode 100644 index 7f28fae0..00000000 --- a/src/redis.coffee +++ /dev/null @@ -1,7 +0,0 @@ -Promise = require('bluebird') - -settings = - port: 6379 - host: '127.0.0.1' - -module.exports = Promise.promisifyAll(require('redis')).createClient(settings.port, settings.host) diff --git a/src/routes/sessions.coffee b/src/routes/sessions.coffee deleted file mode 100644 index 70c18b4e..00000000 --- a/src/routes/sessions.coffee +++ /dev/null @@ -1,25 +0,0 @@ -module.exports = (passport) -> - new: (req, res) -> - res.render('sessions/new', - js: (-> global.js), - css: (-> global.css) - ) - - create: (req, res, next) -> - passport.authenticate('local', (err, user, info) -> - if (err) - return next(err) - if (!user) - return res.redirect('/login') - req.login(user, (err) -> - if (err) - return next(err) - url = req.headers['referer'] - url = "/#{user.username}" unless /edit/.test(url) - return res.redirect(url) - ) - )(req, res, next) - - destroy: (req, res) -> - req.logout() - res.redirect('/login') diff --git a/src/routes/transactions.coffee b/src/routes/transactions.coffee deleted file mode 100644 index 2eca7766..00000000 --- a/src/routes/transactions.coffee +++ /dev/null @@ -1,105 +0,0 @@ -Promise = require('bluebird') -db = require('../redis') -config = require("../config") -moment = require('moment') - -txid = (transaction, user) -> - return transaction if transaction.match(/[a-z]/i) - return user + ":transactions:" + transaction - -module.exports = - index: (req, res) -> - res.render( - 'transactions/index' - user: req.params.user - currency: req.session.user.currency - layout: 'layout' - navigation: true - js: (-> global.js) - css: (-> global.css) - ) - - json: (req, res) -> - result = 'transactions': [] - promises = [] - pattern = req.params.user - if req.session.user.username is 'admin' - x = '*' - - db.keysAsync("#{pattern}:transactions").then((keys) -> - Promise.all(keys.map((key) -> - user = key.substr(0, key.indexOf(':')) - db.lrangeAsync(key, 0, -1).then((transactions) -> - Promise.all(transactions.map((transaction) -> - db.hgetallAsync(txid(transaction, user)).then((transaction) -> - if transaction - transaction.user = user - result.transactions.push(transaction) - ) - )) - ) - )) - ).then(-> - res.write(JSON.stringify(result)) - res.end() - ) - - - create: (req, res) -> - finish = -> - db.hgetall("user:"+req.params.user.toLowerCase(), (err, user) -> - res.render('transactions/notification', - layout: 'mail' - amount: (req.body.received * req.body.exchange).toFixed(2).toString() + ' ' + user.currency - address: req.body.address - txid: req.body.txid - js: (-> global.js) - css: (-> global.css) - (err, html) -> - helper = require('sendgrid').mail - from_email = new helper.Email('info@coinos.io') - to_email = new helper.Email(user.email) - subject = 'Payment Received' - content = new helper.Content('text/html', html) - mail = new helper.Mail(from_email, subject, to_email, content) - - sg = require('sendgrid')(config.sendgrid_token) - request = sg.emptyRequest( - method: 'POST' - path: '/v3/mail/send' - body: mail.toJSON() - ) - - sg.API(request, (error, response) -> - console.log(response.statusCode) - console.log(response.body) - console.log(response.headers) - ) - ) - ) - res.write(JSON.stringify(req.body)) - res.end() - - db.watch(req.body.txid) - db.exists(req.body.txid, (err, result) -> - return finish() if result - - multi = db.multi() - multi.hmset(req.body.txid, req.body) - multi.rpush("#{req.params.user}:transactions", req.body.txid) - multi.exec((err, replies) -> - finish() - ) - ) - - update: (req, res) -> - db.hset(req.params.txid, 'notes', req.body.notes, -> - res.end() - ) - - delete: (req, res) -> - db.del(req.params.txid, -> - db.lrem(req.params.user + ":transactions", 0, req.params.txid, -> - res.end() - ) - ) diff --git a/src/routes/users.coffee b/src/routes/users.coffee deleted file mode 100644 index 1611f691..00000000 --- a/src/routes/users.coffee +++ /dev/null @@ -1,31 +0,0 @@ -db = require("redis").createClient() -bcrypt = require('bcrypt') - -module.exports = (sessions) -> - exists: (req, res) -> - db.hgetall(req.params.user, (err, obj) -> - if obj? then res.write('true') else res.write('false') - res.end() - ) - - new: (req, res) -> - res.render('users/new', - js: (-> global.js), - css: (-> global.css), - ) - - create: (req, res) -> - db.hgetall(req.body.username, (err, obj) -> - if obj - res.redirect(req.body.username) - else - bcrypt.hash(req.body.password, 12, (err, hash) -> - db.hmset( - req.body.username, - {username: req.body.username, password: hash}, - -> - req.headers['referer'] = "/#{req.body.username}/edit" - sessions.create(req, res) - ) - ) - ) diff --git a/static/js/calculator.coffee b/static/js/calculator.coffee deleted file mode 100644 index 75192596..00000000 --- a/static/js/calculator.coffee +++ /dev/null @@ -1,268 +0,0 @@ -EXCHANGE_FAIL = "Problem fetching exchange rate" -SOCKET_FAIL = "Problem connecting to payment server, notifications may not appear" -ADDRESS_FAIL = "Invalid address" -API_URL = "wss://socket.blockcypher.com/v1/btc/main" -TEST_URL = "wss://socket.blockcypher.com/v1/btc/test3" - -g = exports ? this -g.errors = [] -g.amount_requested = 0 -g.amount = 0.toFixed(2) -g.tip = 1 -g.transactions = [] - -$(-> - $.ajax( - url: $('#user').val() + '.json', - dataType: 'json', - success: (data) -> - g.user = data - setup() - ) - - $('button').click((e) -> - e.preventDefault() - ) - - $('.numpad .btn').off('click').click((e) -> - e.preventDefault() - - m = $(this).html() - n = parseFloat($('.amount').html()) - - if m is 'C' - g.amount = 0.toFixed(2) - updateTotal() - return - - return if n > 10000000 - - if m is '00' - n = 100 * n - else - n = 10 * n + parseFloat(m) / 100 - - g.amount = n.toFixed(2) - - updateTotal() - ) - - $('.numpad .btn:last').off('click').click((e) -> - e.preventDefault() - - g.amount = (Math.floor(100 * (parseFloat($('.amount').html()) / 10)) / 100).toFixed(2) - updateTotal() - ) - - $('.tippad .btn').off('click').click((e) -> - e.preventDefault() - - text = $(this).html() - if text is 'No Tip' - value = 0 - else - value = text.slice(1, -1) - - $(this).addClass('active') - $(this).siblings().removeClass('active') - g.tip = 1 + (parseFloat(value)/100) - updateTotal() - ) - - $('#received').click(-> window.location = "/#{g.user.username}/report") - $('#slide').click(-> - $('#controls').slideToggle() - $(this).find('i').toggleClass('fa-sort-up').toggleClass('fa-sort-down') - ) -) - -setup = -> - g.user.address or= '' - g.user.commission or= 0 - g.user.currency or= 'CAD' - g.user.symbol or= 'quadrigacx' - g.user.unit or= 'BTC' - - if g.user.title - $('#title').html("#{g.user.title}").show() - - if g.user.logo - ext = g.user.logo.substr(g.user.logo.length - 3) - src = "/img/logos/#{g.user.username}.#{ext}" - $('#logo').attr('src', src).show() - - getAddress() - if g.user.address[0] is 'm' - API_URL = TEST_URL - - $('.symbol').html(g.user.currency) - $('.currency').html(g.user.currency) - $('.unit').html(g.user.unit) - - $('#received').hide() - - fetchExchangeRate() - listen() - -fetchExchangeRate = -> - $.ajax( - url: "ticker?currency=#{g.user.currency}&symbol=#{g.user.symbol}&type=bid", - success: (exchange) -> - if exchange? - clear(EXCHANGE_FAIL) - else - fail(EXCHANGE_FAIL) - return - - g.exchange = (exchange - exchange * g.user.commission * 0.01).toFixed(2) - $('#exchange').html(g.exchange) - updateTotal() - - unless g.setupComplete - g.setupComplete = true - - error: -> fail(EXCHANGE_FAIL) - ) - setTimeout(fetchExchangeRate, 900000) - -updateTotal = -> - precision = 9 - multiplier().toString().length - subtotal = parseFloat(g.amount * g.tip).toFixed(2) - total = (subtotal * multiplier() / g.exchange).toFixed(precision) - g.amount_requested = (subtotal / g.exchange).toFixed(8) - - unless $.isNumeric(total) - total = '' - - $('#received').hide() - $('#payment').fadeIn('slow') - - $('.tip').html((subtotal - g.amount).toFixed(2)) - $('.subtotal').html(subtotal.toString()) - $('.amount').html(g.amount) - $('.exchange').html(g.exchange) - $('.symbol').html(g.user.symbol) - - if g.user.commission < 0 - $('.commission').html("+#{Math.abs(g.user.commission)}%") - - - $('#total').html(total.toString()) - - size = 300 - $('#qr').css('height', size) - - time = 0 - if g.timeout - time = 10 - - clearTimeout(g.timeout) - g.timeout = setTimeout(-> - $('#qr').html('') - new QRCode('qr', - text: "bitcoin:#{g.user.address}?amount=#{g.amount_requested.toString()}" - width: size - height: size - ) - , time) - -listen = -> - g.attempts++ - if g.blockchain and g.blockchain.readyState is 1 - setTimeout(listen, 12000) - else - fail(SOCKET_FAIL) if g.attempts > 3 - g.blockchain = new WebSocket(API_URL) - - g.blockchain.onopen = -> - if g.blockchain.readyState is 1 - g.attempts = 0 - clear(SOCKET_FAIL) - g.blockchain.send('{"event":"unconfirmed-tx", "address":"' + g.user.address + '"}') - else - setTimeout(g.blockchain.onopen, 12000 * g.attempts) - - g.blockchain.onerror = -> - fail(SOCKET_FAIL) - g.blockchain.close() - delete g.blockchain - listen() - - g.blockchain.onclose = -> - delete g.blockchain - listen() - - g.blockchain.onmessage = (e) -> - tx = JSON.parse(e.data) - amount = 0 - - return if tx.hash == g.last_seen - g.last_seen = tx.hash - - for output in tx.outputs - if (output.addresses[0] == g.user.address) - amount += output.value / 100000000 - - logTransaction(tx.hash, amount) - -logTransaction = (txid, amount) -> - if $('#received').is(":hidden") and amount >= g.amount_requested - $('#payment').hide() - $('#received').fadeIn('slow') - $('#success')[0].play() - g.user.index++ - - if g.transactions.indexOf(txid) is -1 - g.transactions.push(txid) - $.post("/#{g.user.username}/transactions", - txid: txid, - address: g.user.address, - date: moment().format("YYYY-MM-DD HH:mm:ss"), - received: amount, - exchange: g.exchange - tip: g.tip - ) - - getAddress() - -getAddress = -> - try - bitcoin.address.fromBase58Check(g.user.pubkey) - g.user.address = g.user.pubkey - catch - master = bitcoin.HDNode.fromBase58(g.user.pubkey) - child = master.derive(0).derive(g.user.index) - g.user.address = child.getAddress().toString() - g.blockchain.close() if g.blockchain - listen() - - $('#address').html("#{g.user.address}") - -fail = (msg) -> - g.errors.push(msg) - g.errors = g.errors.uniq() - $('#error').show().html(g.errors.toString()) - -clear = (msg) -> - i = g.errors.indexOf(msg) - g.errors.splice(i, 1) if i >= 0 - if (g.errors.length > 0) - $('#error').show().html(g.errors.toString()) - else - $('#error').hide() - $('#calculator').fadeIn('slow') - -Array::uniq = -> - output = {} - output[@[key]] = @[key] for key in [0...@length] - value for key, value of output - -multiplier = -> - switch g.user.unit - when 'BTC' then 1 - when 'mBTC' then 1000 - when 'µBTC' then 1000000 - when 'bits' then 1000000 - when 'satoshis' then 100000000 - - diff --git a/static/js/generate_address.coffee b/static/js/generate_address.coffee deleted file mode 100644 index face65ea..00000000 --- a/static/js/generate_address.coffee +++ /dev/null @@ -1,74 +0,0 @@ -g = this - -$(-> - genKey() - $('.regenerate').click(genKey) - - $('#encrypt').submit(-> - $('.form-control').blur() - if $('.has-error').length > 0 - $('.has-error').effect('shake', 500) - return false - - $('#modal').modal() - return false - ) - - $('#modal').on('shown.bs.modal', -> - bip38 = new Bip38 - g.enc_privkey = bip38.encrypt(g.privkey, $('#password').val(), g.pubkey) - $('#modal-body').html('Done!') - setTimeout( - -> $('#modal').modal('hide') - 1000 - ) - ) - - $('#modal').on('hidden.bs.modal', -> - if $('#password').val() == '' - g.enc_privkey = g.privkey - $('#status').html('') - else - $('#status').html('Encrypted') - - $('#privkey').html(g.enc_privkey) - $('#privqr').html('') - new QRCode('privqr', text: $('#privkey').html(), width: 260, height: 260) - ) - - $('.print').click(-> window.print()) - $('#password').pwstrength(showVerdicts: false) - $('#password').blur(-> - $('#confirm').blur() - $(this).parent().next('.alert').remove() - - if $('.progress-bar-success').length > 0 - $(this).parent().removeClass('has-error') - else - $(this).parent().addClass('has-error') - ) - $('#confirm').blur(-> - if $('#password').val() == $('#confirm').val() - $('#confirm').parent().removeClass('has-error') - else - $('#confirm').parent().addClass('has-error') - ) -) - -genKey = -> - $('#password').focus() - key = bitcoin.ECKey.makeRandom(false) - g.privkey = key.toWIF() - g.pubkey = key.pub.getAddress().toString() - - $('#pubkey').html(g.pubkey) - $('#privkey').html(g.privkey) - - $('#pubqr, #privqr, #status').html('') - $('#password, #confirm').val('').keyup().parent().removeClass('has-error') - - new QRCode('pubqr', text: $('#pubkey').html(), width: 260, height: 260) - new QRCode('privqr', text: $('#privkey').html(), width: 260, height: 260) - - - diff --git a/static/js/index.coffee b/static/js/index.coffee deleted file mode 100644 index 090abbe2..00000000 --- a/static/js/index.coffee +++ /dev/null @@ -1,44 +0,0 @@ -#= require js/jquery-1.8.2.min.js -#= require js/bootstrap.min.js - -g = exports ? this - -$(-> - fetchExchangeRate('ask') - fetchExchangeRate('bid') - - $('#username').blur(-> - $.get( - "/#{$(this).val()}/exists", - username: $(this).val(), - (data) -> - if data is "true" - $('#username').prev().css('color', 'red').html('Username (taken)') - g.preventSubmit = true - else - $('#username').prev().css('color', 'black').html('Username') - g.preventSubmit = false - ) - ) - - $('#signup').submit(-> - return false if g.preventSubmit - return true - ) -) - -fetchExchangeRate = (type) -> - commission = 0.015 - commission *= -1 if type is 'bid' - - $.ajax( - url: "/ticker?symbol=virtexCAD&type=#{type}&amount=100", - success: (exchange) -> - if not exchange? - exchange = '??' - - exchange = exchange - exchange * commission - $("##{type}").html(exchange.toFixed(2)) - error: -> - $("##{type}").html('??') - ) diff --git a/static/js/profile.coffee b/static/js/profile.coffee deleted file mode 100644 index a894f275..00000000 --- a/static/js/profile.coffee +++ /dev/null @@ -1,30 +0,0 @@ -g = exports ? this -g.proceed = false - -$(-> - $('[data-toggle=popover]').popover() - $('#title').focus() - - user = $('#username').val() - $.getJSON("/#{user}.json", (data) -> - $('#email').val(data.email) - $('#phone').val(data.phone) - $('#profile').fadeIn() - ) - - $('#confirm').blur(-> - $('#confirm_error').remove() - if $('#password').val() != $('#confirm').val() - $('#password, #confirm').parent().addClass('has-error') - $('#confirm').parent().after('
Passwords don\'t match
') - else - $('#password, #confirm').parent().removeClass('has-error') - ) - - $('#profile').submit(-> - $('#profile .form-control').blur() - if $('#profile .has-error').length > 0 - $('#profile .has-error').effect('shake', 500) - return false - ) -) diff --git a/static/js/register.coffee b/static/js/register.coffee deleted file mode 100644 index 98842328..00000000 --- a/static/js/register.coffee +++ /dev/null @@ -1,53 +0,0 @@ -g = this - -$(-> - $('#username').focus() - - $('#username').blur(-> - $(this).parent().next('.alert').remove() - - if /^[a-z]+$/.test($(this).val()) - $(this).parent().removeClass('has-error') - else - $(this).parent().addClass('has-error') - $(this).parent().after('
Username must be lowercase
') - ) - - $('#password').blur(-> - $('#confirm').blur() - $(this).parent().next('.alert').remove() - ) - - $('#confirm').blur(-> - return if $(this).val() == '' - $(this).parent().next('.alert').remove() - - if $('#password').val() == $('#confirm').val() - $('#confirm').parent().removeClass('has-error') - else - $('#confirm').parent().addClass('has-error') - $('#confirm').parent().after('
Passwords don\'t match
') - ) - - $('#email').blur(-> - return if $(this).val() == '' - $(this).parent().next('.alert').remove() - if validateEmail($(this).val()) - $(this).parent().removeClass('has-error') - else - $(this).parent().addClass('has-error') - $(this).parent().after('
Invalid email
') - ) - - $('#register').submit(-> - $('.form-control').blur() - if $('.has-error').length > 0 - $('.has-error').effect('shake', 500) - return false - else - mnemonic = bip39.generateMnemonic() - key = bitcoin.HDNode.fromSeedBuffer(bip39.mnemonicToSeed(mnemonic)).deriveHardened(44).deriveHardened(0) - $('#pubkey').val(key.neutered().toString()) - $('#privkey').val(CryptoJS.AES.encrypt(key.toString(), $('#password').val()).toString()) - ) -) diff --git a/static/js/setup.coffee b/static/js/setup.coffee deleted file mode 100644 index d796c897..00000000 --- a/static/js/setup.coffee +++ /dev/null @@ -1,65 +0,0 @@ -g = exports ? this -g.proceed = false - -$(-> - $('#encryption-password').pwstrength(showVerdicts: false) - - $.getJSON("/js/rates.json", (data) -> - currencies = Object.keys(data) - currencies = currencies.sort() - currencies.pop() - - $.each(currencies, (i, v) -> - $('#currency').append("") - ) - $("#currency option[value='CAD']").attr('selected', 'selected') - - $('#currency').change(-> - currency = $(this).val() - return unless currency - - $('#symbol option').remove() - symbols = Object.keys(data[currency]) - $.each(symbols, (i, v) -> - return if v == 'localbitcoins' - $('#symbol').append("") - ) - - switch currency - when 'CAD' - $("#symbol option[value='quadrigacx']").attr('selected', 'selected') - when 'USD' - $("#symbol option[value='bitstamp']").attr('selected', 'selected') - ) - - user = $('#username').val() - $.getJSON("/#{user}.json", (data) -> - data.commission ?= 0 - data.unit ?= 'BTC' - $('#email').val(data.email) - $('#title').val(data.title) - $('#logo').val(data.logo) - $('#address').val(data.address) - $('#commission').val(data.commission) - $('#unit').val(data.unit) - $("#currency option[value='#{data.currency}']").attr('selected', 'selected') - $('#currency').change() - $("#symbol option[value='#{data.symbol}']").attr('selected', 'selected') - $('#setup').fadeIn() - $('#title').focus() - ) - ) - - units = ['BTC', 'mBTC', 'µBTC', 'bits', 'satoshis'] - $.each(units, (i, v) -> - $('#unit').append("") - ) - - $('#setup').submit(-> - $('#setup .form-control').blur() - if $('#setup .has-error').length > 0 - $('#setup .has-error').effect('shake', 500) - return false - ) -) - diff --git a/static/js/setup.js b/static/js/setup.js index e96bd00d..63b9e291 100644 --- a/static/js/setup.js +++ b/static/js/setup.js @@ -11,7 +11,7 @@ $('#encryption-password').pwstrength({ showVerdicts: false }); - $.getJSON("/js/rates.json", function(data) { + $.getJSON("/rates", function(data) { var currencies, user; currencies = Object.keys(data); currencies = currencies.sort(); @@ -79,4 +79,4 @@ },{}]},{},[1]) -; \ No newline at end of file +; diff --git a/static/js/signin.coffee b/static/js/signin.coffee deleted file mode 100644 index df7dbb39..00000000 --- a/static/js/signin.coffee +++ /dev/null @@ -1,6 +0,0 @@ -$(-> - $('#register').click(-> - window.location.href = '/register' - $(this).preventDefault() - ) -) diff --git a/static/js/wallet.coffee b/static/js/wallet.coffee deleted file mode 100644 index 1beefd23..00000000 --- a/static/js/wallet.coffee +++ /dev/null @@ -1,456 +0,0 @@ -g = exports ? this -g.proceed = false -g.api = '/blockcypher/v1/btc/main' - -validators = - address: (e) -> - return true if e.val() is '' - try - bitcoin.address.fromBase58Check(e.val()) - catch - return false - - key: (e) -> - $('#keytype').val() != 'unknown' - -errors = - address: 'Invalid address.' - key: 'Could not detect key type.' - -$(-> - getUser() - - $('#types').attr('data-content', """ - Bitcoin Address (starts with 1 or 3)
- Private Key (starts with 5 or L)
- HD Wallet pubkey (starts with xpub)
- HD Wallet private key (starts with xprv)
- AES Encrypted Private Key (starts with U)
- BIP38 Encrypted Private Key (starts with 6)
- BIP39 Mnemonic (series of 12-24 english words) - """) - .popover(html: true) - .on("show.bs.popover", -> $(this).data("bs.popover").tip().css(minWidth: "400px")) - - $('form').validator(custom: validators, errors: errors, delay: 1200) - $('[data-toggle=tooltip]').tooltip(trigger: 'hover') - - $('#key').keyup(-> - val = $(this).val() - - $('#keytype').val('unknown') - - switch val[0] - when '1' - try - bitcoin.address.fromBase58Check(val) - $('#keytype').val('address') - when 'm' - try - bitcoin.address.fromBase58Check(val) - $('#keytype').val('address') - when '5', \ - 'L', \ - 'K' - try - bitcoin.ECPair.fromWIF(val) - $('#keytype').val('wif') - when 'U' - try - if CryptoJS.AES.decrypt(val, g.password).toString(CryptoJS.enc.Utf8) - $('#keytype').val('aes') - when '6' - if bip38().verify(val) - $('#keytype').val('bip38') - when 'x' - if $(this).val()[3] is 'b' - try - bitcoin.HDNode.fromBase58(val) - $('#keytype').val('xpub') - else - try - bitcoin.HDNode.fromBase58(val) - $('#keytype').val('xprv') - else - if val.split(' ').length in [12, 15, 18, 21, 24] and bip39.validateMnemonic(val) - $('#keytype').val('bip39') - ) - - $('#save').click(-> - $('.form-control').blur() - if $('#keys .has-error').length > 0 - $('#keys .has-error').effect('shake', 500) - return - - key = $('#key').val() - proceed = true - - switch $('#keytype').val() - when 'address' - $('#pubkey').val(key) - $('#privkey').val('') - when 'wif' - pubkey = bitcoin.ECPair.fromWIF(key).getAddress() - privkey = CryptoJS.AES.encrypt(key, g.password) - $('#pubkey').val(pubkey) - $('#privkey').val(privkey) - when 'aes' - try - pubkey = bitcoin.HDNode.fromBase58(CryptoJS.AES.decrypt(key, g.password).toString(CryptoJS.enc.Utf8)).neutered().toString() - $('#pubkey').val(pubkey) - $('#privkey').val(key) - catch - try - pubkey = bitcoin.ECPair.fromWIF(CryptoJS.AES.decrypt(key, g.password).toString(CryptoJS.enc.Utf8)).getAddress() - $('#pubkey').val(pubkey) - $('#privkey').val(key) - catch - proceed = false - when 'bip38' - wif = bip38().decrypt(key, g.password) - pubkey = bitcoin.ECPair.fromWIF(wif).getAddress() - privkey = CryptoJS.AES.encrypt(wif, g.password) - $('#pubkey').val(pubkey) - $('#privkey, #key').val(privkey) - when 'xpub' - $('#pubkey').val(key) - $('#privkey').val('') - when 'xprv' - pubkey = bitcoin.HDNode.fromBase58(key).neutered().toString() - privkey = CryptoJS.AES.encrypt(key, g.password) - $('#pubkey').val(pubkey) - $('#privkey').val(privkey) - when 'bip39' - master = bitcoin.HDNode.fromSeedBuffer(bip39.mnemonicToSeed(key)).deriveHardened(44).deriveHardened(0) - pubkey = master.neutered().toString() - privkey = CryptoJS.AES.encrypt(master.toString(), g.password) - $('#pubkey').val(pubkey) - $('#privkey').val(privkey) - - if proceed - updateUser() - $('#keys').hide() - $('#keys_updated').fadeIn() - $('#balances').hide() - $('#fetching').show() - ) - - - $('#withdrawal form input[type=button]').click(sendTransaction) - - $('#currency_toggle').click(-> - if $(this).html() is g.user.unit - $(this).html(g.user.currency) - g.amount = parseFloat($('#amount').val()).toFixed(precision()) - amount = (g.amount * g.exchange / multiplier()).toFixed(2) - $('#amount').val(amount) - $('#amount').attr('step', 0.01) - $('#amount').attr('max', (g.balance * g.exchange / multiplier()).toFixed(2)) - else - $(this).html(g.user.unit) - $('#amount').val(convertedAmount()) - $('#amount').attr('step', 0.00000001 * multiplier()) - $('#amount').attr('max', g.balance) - ) - - $('#max').click(-> - if $('#currency_toggle').html() is g.user.unit - $('#amount').val(g.balance).blur() - else - g.amount = parseFloat(g.balance).toFixed(precision()) - amount = (g.balance / multiplier() * g.exchange).toFixed(2) - $('#amount').val(amount).blur() - ) - - $('#amount').change(-> - if $('#currency_toggle').html() is g.user.unit - $(this).val(parseFloat($(this).val()).toFixed(precision())) - else - $(this).val(parseFloat($(this).val()).toFixed(2)) - - if parseFloat($(this).val()) > parseFloat($(this).attr('max')) - $(this).val($(this).attr('max')) - ) - - $('#password').keyup(-> - try - if g.user.privkey - g.privkey = CryptoJS.AES.decrypt(g.user.privkey, $(this).val()).toString(CryptoJS.enc.Utf8) - - if g.privkey[0] is 'x' - g.key = bitcoin.HDNode.fromBase58(g.privkey) - else - g.key = bitcoin.ECPair.fromWIF(g.privkey) - - $('#key').val(g.privkey).keyup() - else - $('#key').val(g.user.pubkey).keyup() - - $(this).closest('.form-group').hide() - $('.wallet').fadeIn() - g.password = $(this).val() - ) - - $('#manage').click(-> - $('#withdrawal').hide() - $('#keys').show() - $('#privkey').val(g.user.privkey) - ) - - $('#withdraw').click(-> - $('#withdrawal').show() - $('#amount').focus() - $('#keys').hide() - $('#withdrawal form').validator('destroy') - $('#withdrawal form').validator(custom: validators, errors: errors, delay: 1200) - ) - - $('#cancel').click(-> - $('#keys').hide() - if g.balance > 0 and g.user.privkey - $('#withdrawal form').validator('destroy') - $('#withdrawal form').validator(custom: validators, errors: errors, delay: 1200) - $('#withdrawal').show() - $('#amount').focus() - ) - - $('#backup').click(-> - url = 'data:application/json;base64,' + btoa(JSON.stringify(g.user.privkey)) - a = document.createElement('a') - a.setAttribute('href', url) - a.setAttribute('download', 'wallet.json.aes') - a.click() - ) - - $('#generate').click(-> - bootbox.confirm('

Are you sure?

This will overwrite your existing wallet so make sure that you have the backup we sent to your email in case you want to restore it.

', (result) -> - if result - mnemonic = bip39.generateMnemonic() - key = bitcoin.HDNode.fromSeedBuffer(bip39.mnemonicToSeed(mnemonic)).deriveHardened(44).deriveHardened(0) - $('#key').val(key.toString()).effect('highlight', {}, 2000).keyup() - ) - ) - - $('.close').on('click', -> $(this).closest('.alert').fadeOut()) -) - -getUser = -> - $.getJSON("/#{$('#username').val()}.json", (user) -> - g.user = user - $('#key').val(user.pubkey) - $('#pubkey').val(user.pubkey) - $('#privkey').val(user.privkey) - $('#address').val(user.address) - $('.address').html(user.address) - $('#unit').html(user.unit) - $('#currency_toggle').html(user.unit) - $('#amount').attr('step', 0.00000001 * multiplier()) - $('#password').closest('.form-group').show() - $('#password').focus() - - getExchangeRate() - $('#password').keyup() - ) - -getExchangeRate = -> - $.get("/ticker?currency=#{g.user.currency}&symbol=#{g.user.symbol}&type=bid", (exchange) -> - g.exchange = exchange - createWallet() - ) - -createWallet = -> - $.get("#{g.api}/wallets", (data) -> - if g.user.username in data.wallet_names - getBalance() - else - params = name: g.user.username - - if isBip32(g.user.pubkey) - params.extended_public_key = g.user.pubkey - params.subchain_indexes = [0,1] - - $.post("#{g.api}/wallets/hd", JSON.stringify(params)).done(getBalance).fail(-> $('.wallet').fadeIn()) - else - params.addresses = [g.user.pubkey] - $.post("#{g.api}/wallets", JSON.stringify(params)).done(getBalance).fail(-> $('.wallet').fadeIn()) - ) - - -getBalance = -> - $.get("#{g.api}/addrs/#{g.user.username}/balance?omitWalletAddresses=true", (data) -> - balance = data.final_balance - g.balance = parseFloat(balance.toBTC()) - fiat = balance.toFiat() - $('#balance').html(g.balance) - $('#fiat').html("#{fiat} #{g.user.currency}") - $('#amount').attr('max', g.balance) - $('#balances').show() - $('#fetching').hide() - - if g.balance > 0 and g.user.privkey - $('#keys').hide() - $('#withdrawal form').validator('destroy') - $('#withdrawal form').validator(custom: validators, errors: errors, delay: 1200) - $('#withdrawal').show() - $('#amount').focus() - ) - -updateUser = -> - data = $('#keys form').serializeObject() - delete data['key'] - - $.post("/#{g.user.username}", data, -> - $.ajax( - url: "#{g.api}/wallets/#{g.user.username}" - type: 'DELETE' - ).always(-> - $.ajax( - url: "#{g.api}/wallets/hd/#{g.user.username}" - type: 'DELETE' - ).always(-> - getUser() - ) - ) - ) - -sendTransaction = -> - if g.key and not (typeof g.key.d is 'undefined' and typeof g.key.keyPair.d is 'undefined') - dialog = new BootstrapDialog( - title: '

Confirm Transaction

' - message: ' Calculating fee...' - buttons: [ - label: 'Send' - cssClass: 'btn-primary' - , - label: ' Cancel' - cssClass: 'btn-default' - action: (dialogItself) -> dialogItself.close() - icon: 'glyphicon glyphicon-ban-circle' - ] - ).open() - - params = - inputs: [{wallet_name: g.user.username, wallet_token: g.token}] - outputs: [{addresses: [$('#recipient').val()], value: 1}] - preference: $('#priority').val() - - $.post("#{g.api}/txs/new", JSON.stringify(params)).done((data) -> - if $('#currency_toggle').html() is g.user.unit - amount = $('#amount').val() - else - amount = convertedAmount() - - value = parseInt(amount * 100000000 / multiplier()) - - params.fees = data.tx.fees - params.outputs[0].value = value - if value > parseFloat(g.balance).toSatoshis() - params.fees - params.outputs[0].value -= params.fees - - $.post("#{g.api}/txs/new", JSON.stringify(params)).done((data) -> - data.pubkeys = [] - if g.key instanceof bitcoin.ECPair - data.signatures = data.tosign.map((tosign, i) -> - data.pubkeys.push(g.key.getPublicKeyBuffer().toString('hex')) - g.key.sign(new buffer.Buffer(tosign, "hex")).toDER().toString("hex") - ) - else - data.signatures = data.tosign.map((tosign, i) -> - path = data.tx.inputs[i].hd_path.split('/') - key = g.key.derive(parseInt(path[1])).derive(parseInt(path[2])) - data.pubkeys.push(key.keyPair.getPublicKeyBuffer().toString('hex')) - key.keyPair.sign(new buffer.Buffer(tosign, "hex")).toDER().toString("hex") - ) - - g.data = data - - amount = data.tx.outputs[0].value - fee = data.tx.fees - total = amount - if value > parseFloat(g.balance).toSatoshis() - data.tx.fees - total += fee - - - $('.dialog .amount').html("#{(amount.toBTC())} #{g.user.unit} (#{amount.toFiat()} #{g.user.currency})") - $('.dialog .fee').html("#{(fee.toBTC())} #{g.user.unit} (#{fee.toFiat()} #{g.user.currency})") - $('.dialog .total').html("#{(total.toBTC())} #{g.user.unit} (#{total.toFiat()} #{g.user.currency})") - $('.dialog .address').html(data.tx.outputs[0].addresses[0]) - - dialog.getModalBody().html($('.dialog').html()) - - if amount.toBTC() < 0.00000534 - displayErrors({errors: [error: 'Amount left over after fee is too small to send']}, dialog) - - dialog.getModal().find('.btn-primary').click(-> - $.post("#{g.api}/txs/send", JSON.stringify(g.data)).then((finaltx) -> - $('#transaction_sent').fadeIn() - balance = g.balance.toSatoshis() - finaltx.tx.outputs[0].value - finaltx.tx.fees - g.balance = balance.toBTC() - fiat = balance.toFiat() - $('#balance').html(g.balance) - $('#fiat').html("#{fiat} #{g.user.currency}") - $('#blockchain').off('click').on('click', -> window.open('https://blockchain.info/tx/' + finaltx.tx.hash, '_blank')) - dialog.close() - ) - ) - ).fail((data) -> - displayErrors(data.responseJSON, dialog) - ) - ).fail((data) -> - displayErrors(data.responseJSON, dialog) - ) - -displayErrors = (data, dialog) -> - if data.errors - dialog.getModalBody().html('') - dialog.getModal().find('.btn-primary').hide() - for e in data.errors - dialog.getModalBody().append("
#{e.error}
") - - -convertedAmount = -> - g.amount = amount unless g.amount and !isNaN(parseFloat(g.amount)) - amount = parseFloat($('#amount').val() * multiplier() / g.exchange).toFixed(precision()) - difference = parseFloat(Math.abs(g.amount - amount).toFixed(precision())) - tolerance = parseFloat((.00000002 * g.exchange * multiplier()).toFixed(precision())) - if difference > tolerance - return amount - else - return g.amount - - -check_address = (address) -> - try - bitcoin.address.fromBase58Check(address) - return true - catch - return isBip32(address) - -isBip32 = (address) -> - try - bitcoin.HDNode.fromBase58(address) - return true - catch - return false - -multiplier = -> - switch g.user.unit - when 'BTC' then 1 - when 'mBTC' then 1000 - when 'µBTC' then 1000000 - when 'bits' then 1000000 - when 'satoshis' then 100000000 - -precision = -> - 9 - multiplier().toString().length - -Number.prototype.toBTC = -> - parseFloat((this / 100000000 * multiplier())).toFixed(precision()) - -Number.prototype.toFiat = -> - parseFloat((this * g.exchange / 100000000)).toFixed(2) - -Number.prototype.toSatoshis = -> - parseInt(this * 100000000 / multiplier()) - - -- cgit v1.2.3