diff --git a/.eslintrc b/.eslintrc index 8985849..0e493ba 100644 --- a/.eslintrc +++ b/.eslintrc @@ -1,4 +1,5 @@ { + "extends": "airbnb/base", "ecmaFeatures": { "modules": true, "jsx": true @@ -9,6 +10,6 @@ }, "rules": { "quotes": [2, "single"], - "no-underscore-dangle": [0] + "no-underscore-dangle": [0], } } diff --git a/api/app.js b/api/app.js index 071158a..480b46c 100644 --- a/api/app.js +++ b/api/app.js @@ -10,20 +10,20 @@ const debug = debugname('hostr-api'); const router = new Router(); -let statsdOpts = {prefix: 'hostr-api', host: process.env.STATSD_HOST || 'localhost'}; +const statsdOpts = {prefix: 'hostr-api', host: process.env.STATSD_HOST || 'localhost'}; router.use(stats(statsdOpts)); -let statsd = new StatsD(statsdOpts); -router.use(function*(next) { +const statsd = new StatsD(statsdOpts); +router.use(function* statsMiddleware(next) { this.statsd = statsd; yield next; }); router.use(cors({ origin: '*', - credentials: true + credentials: true, })); -router.use('/*',function* (next) { +router.use('/*', function* authMiddleware(next) { try { yield next; if (this.response.status === 404 && !this.response.body) { @@ -35,13 +35,13 @@ router.use('/*',function* (next) { this.set('WWW-Authenticate', 'Basic'); this.status = 401; this.body = err.message; - } else if(err.status === 404) { + } else if (err.status === 404) { this.status = 404; this.body = { error: { message: 'File not found', - code: 604 - } + code: 604, + }, }; } else { if (!err.status) { @@ -70,7 +70,7 @@ router.delete('/file/:id', auth, file.del); router.delete('/file/:id', auth, file.del); // Hack, if no route matches here, router does not dispatch at all -router.get('/(.*)', function* () { +router.get('/(.*)', function* errorMiddleware() { this.throw(404); }); diff --git a/api/lib/auth.js b/api/lib/auth.js index f915feb..405b4ed 100644 --- a/api/lib/auth.js +++ b/api/lib/auth.js @@ -7,7 +7,7 @@ const debug = debugname('hostr-api:auth'); const badLoginMsg = '{"error": {"message": "Incorrect login details.", "code": 607}}'; -module.exports = function* (next) { +export default function* (next) { const Users = this.db.Users; const Files = this.db.Files; const Logins = this.db.Logins; @@ -20,7 +20,6 @@ module.exports = function* (next) { debug('Token found'); user = yield Users.findOne({'_id': objectID(userToken)}); } else { - const authUser = auth(this); this.assert(authUser, 401, badLoginMsg); const remoteIp = this.req.headers['x-real-ip'] || this.req.connection.remoteAddress; @@ -39,7 +38,7 @@ module.exports = function* (next) { this.assert(!user.activationCode, 401, '{"error": {"message": "Account has not been activated.", "code": 603}}'); const uploadedTotal = yield Files.count({owner: user._id, status: {'$ne': 'deleted'}}); - const uploadedToday = yield Files.count({owner: user._id, 'time_added': {'$gt': Math.ceil(Date.now()/1000)-86400}}); + const uploadedToday = yield Files.count({owner: user._id, 'time_added': {'$gt': Math.ceil(Date.now() / 1000) - 86400}}); const normalisedUser = { 'id': user._id, @@ -48,10 +47,10 @@ module.exports = function* (next) { 'file_count': uploadedTotal, 'max_filesize': user.type === 'Pro' ? 524288000 : 20971520, 'plan': user.type || 'Free', - 'uploads_today': uploadedToday + 'uploads_today': uploadedToday, }; this.response.set('Daily-Uploads-Remaining', user.type === 'Pro' ? 'unlimited' : 15 - uploadedToday); this.user = normalisedUser; debug('Authenticated user: ' + this.user.email); yield next; -}; +} diff --git a/api/routes/file.js b/api/routes/file.js index e3ef326..74a04ef 100644 --- a/api/routes/file.js +++ b/api/routes/file.js @@ -60,7 +60,7 @@ export function* post(next) { const fileId = yield hostrId(Files); // Fire an event to let the frontend map the GUID it sent to the real ID. Allows immediate linking to the file - let acceptedEvent = `{"type": "file-accepted", "data": {"id": "${fileId}", "guid": "${tempGuid}", "href": "${fileHost}/${fileId}"}}`; + const acceptedEvent = `{"type": "file-accepted", "data": {"id": "${fileId}", "guid": "${tempGuid}", "href": "${fileHost}/${fileId}"}}`; this.redis.publish('/user/' + this.user.id, acceptedEvent); this.statsd.incr('file.upload.accepted', 1); @@ -82,20 +82,20 @@ export function* post(next) { upload.pipe(s3Upload(key)); const thumbsPromises = [ - new Promise((resolve, reject) => { + new Promise((resolve) => { const small = gm(upload).resize(150, 150, '>').stream(); small.pipe(fs.createWriteStream(path.join(storePath, fileId[0], '150', fileId + '_' + upload.filename))); small.pipe(s3Upload(path.join('150', fileId + '_' + upload.filename))).on('finish', resolve); }), - new Promise((resolve, reject) => { + new Promise((resolve) => { const medium = gm(upload).resize(970, '>').stream(); medium.pipe(fs.createWriteStream(path.join(storePath, fileId[0], '970', fileId + '_' + upload.filename))); medium.pipe(s3Upload(path.join('970', fileId + '_' + upload.filename))).on('finish', resolve); - }) + }), ]; - let dimensionsPromise = new Promise((resolve, reject) => { + const dimensionsPromise = new Promise((resolve, reject) => { gm(upload).size((err, size) => { if (err) { reject(err); @@ -142,7 +142,7 @@ export function* post(next) { status: 'active', 'last_accessed': null, s3: false, - type: sniff(upload.filename) + type: sniff(upload.filename), }; yield Files.insertOne(dbFile); @@ -175,7 +175,7 @@ export function* post(next) { if (process.env.VIRUSTOTAL) { // Check in the background - process.nextTick(function*() { + process.nextTick(function* malwareScan() { debug('Malware Scan'); const { positive, result } = yield malware(dbFile); if (positive) { @@ -202,20 +202,20 @@ export function* list() { let limit = 20; if (this.request.query.perpage === '0') { limit = false; - } else if(this.request.query.perpage > 0) { - limit = parseInt(this.request.query.perpage / 1); + } else if (this.request.query.perpage > 0) { + limit = parseInt(this.request.query.perpage / 1, 10); } let skip = 0; if (this.request.query.page) { - skip = parseInt(this.request.query.page - 1) * limit; + skip = parseInt(this.request.query.page - 1, 10) * limit; } const queryOptions = { limit: limit, skip: skip, sort: [['time_added', 'desc']], hint: { - owner: 1, status: 1, 'time_added': -1 - } + owner: 1, status: 1, 'time_added': -1, + }, }; const userFiles = yield Files.find({owner: this.user.id, status: status}, queryOptions).toArray(); @@ -266,7 +266,7 @@ export function* events() { pubsub.on('message', (channel, message) => { this.websocket.send(message); }); - this.websocket.on('close', function() { + this.websocket.on('close', () => { pubsub.quit(); }); } diff --git a/api/routes/user.js b/api/routes/user.js index 5142439..39c21c7 100644 --- a/api/routes/user.js +++ b/api/routes/user.js @@ -8,28 +8,28 @@ const debug = debugname('hostr-api:user'); const redisUrl = process.env.REDIS_URL || process.env.REDISTOGO_URL || 'redis://localhost:6379'; -export function* get (){ +export function* get() { this.body = this.user; } -export function* token(){ +export function* token() { const token = uuid.v4(); // eslint-disable-line no-shadow yield this.redis.set(token, this.user.id, 'EX', 86400); this.body = {token: token}; } -export function* transaction(){ +export function* transaction() { const Transactions = this.db.Transactions; const transactions = yield Transactions.find({'user_id': this.user.id}).toArray(); - this.body = transactions.map(function(transaction) { // eslint-disable-line no-shadow + this.body = transactions.map((transaction) => { // eslint-disable-line no-shadow const type = transaction.paypal ? 'paypal' : 'direct'; return { id: transaction._id, amount: transaction.paypal ? transaction.amount : transaction.amount / 100, date: transaction.date, description: transaction.desc, - type: type + type: type, }; }); } @@ -61,9 +61,9 @@ export function* events() { this.websocket.send(message); }); pubsub.on('ready', () => { - this.websocket.on('message', co.wrap(function* (message) { + this.websocket.on('message', co.wrap(function* wsMessage(message) { let json; - try{ + try { json = JSON.parse(message); } catch(err) { debug('Invalid JSON for socket auth'); diff --git a/app.js b/app.js index 5eadde7..7925bba 100644 --- a/app.js +++ b/app.js @@ -1,9 +1,6 @@ import path from 'path'; import koa from 'koa'; -import mount from 'koa-mount'; -import route from 'koa-route'; import logger from 'koa-logger'; -import Router from 'koa-router'; import serve from 'koa-static'; import favicon from 'koa-favicon'; import compress from 'koa-compress'; @@ -13,7 +10,6 @@ import helmet from 'koa-helmet'; import raven from 'raven'; import mongo from './lib/mongo'; import * as redis from './lib/redis'; -import co from 'co'; import api from './api/app'; import { ws } from './api/app'; import web from './web/app'; @@ -27,11 +23,11 @@ app.keys = [process.env.KEYS || 'INSECURE']; if (process.env.SENTRY_DSN) { const ravenClient = new raven.Client(process.env.SENTRY_DSN); ravenClient.patchGlobal(); - app.use(function* (next) { + app.use(function* ravenMiddleware(next) { this.raven = ravenClient; yield next; }); - app.ws.use(function* (next) { + app.ws.use(function* ravenWsMiddleware(next) { this.raven = ravenClient; yield next; }); @@ -39,9 +35,9 @@ if (process.env.SENTRY_DSN) { app.use(helmet()); -app.use(function* (next){ +app.use(function* errorMiddleware(next) { this.set('Server', 'Nintendo 64'); - if(this.req.headers['x-forwarded-proto'] === 'http'){ + if (this.req.headers['x-forwarded-proto'] === 'http') { return this.redirect('https://' + this.req.headers.host + this.req.url); } try { @@ -70,7 +66,7 @@ app.ws.use(redis.middleware()); app.ws.use(ws.prefix('/api').routes()); if (!module.parent) { - app.listen(process.env.PORT || 4040, function() { + app.listen(process.env.PORT || 4040, function listen() { debug('Koa HTTP server listening on port ' + (process.env.PORT || 4040)); }); } diff --git a/init.js b/init.js deleted file mode 100644 index c7da2cc..0000000 --- a/init.js +++ /dev/null @@ -1,2 +0,0 @@ -import { init as storageInit } from './lib/storage'; -storageInit(); diff --git a/lib/format.js b/lib/format.js index 584b494..9448fd2 100644 --- a/lib/format.js +++ b/lib/format.js @@ -9,20 +9,16 @@ export function formatDate(timestamp) { export function formatSize(size) { if (size >= 1073741824) { - size = Math.round((size / 1073741824) * 10) / 10 + 'GB'; - } else { - if (size >= 1048576) { - size = Math.round((size / 1048576) * 10) / 10 + 'MB'; - } else { - if (size >= 1024) { - size = Math.round((size / 1024) * 10) / 10 + 'KB'; - } else { - size = Math.round(size) + 'B'; - } - } + return Math.round((size / 1073741824) * 10) / 10 + 'GB'; } - return size; -}; + if (size >= 1048576) { + return Math.round((size / 1048576) * 10) / 10 + 'MB'; + } + if (size >= 1024) { + return Math.round((size / 1024) * 10) / 10 + 'KB'; + } + return Math.round(size) + 'B'; +} export function formatFile(file) { const formattedFile = { @@ -36,7 +32,7 @@ export function formatFile(file) { readableSize: formatSize(file.file_size), type: sniff(file.file_name), trashed: (file.status === 'trashed'), - status: file.status + status: file.status, }; if (file.width) { @@ -45,7 +41,7 @@ export function formatFile(file) { const ext = (file.file_name.split('.').pop().toLowerCase() === 'psd' ? '.png' : ''); formattedFile.direct = { '150x': fileHost + '/file/150/' + file._id + '/' + file.file_name + ext, // eslint-disable-line no-underscore-dangle - '970x': fileHost + '/file/970/' + file._id + '/' + file.file_name + ext // eslint-disable-line no-underscore-dangle + '970x': fileHost + '/file/970/' + file._id + '/' + file.file_name + ext, // eslint-disable-line no-underscore-dangle }; } return formattedFile; diff --git a/lib/hostr-file-stream.js b/lib/hostr-file-stream.js index d17a486..5bbfee4 100644 --- a/lib/hostr-file-stream.js +++ b/lib/hostr-file-stream.js @@ -1,5 +1,4 @@ import fs from 'fs'; -import path from 'path'; import createError from 'http-errors'; import { get as getFile } from './s3'; diff --git a/lib/hostr-id.js b/lib/hostr-id.js index be784cb..1d19866 100644 --- a/lib/hostr-id.js +++ b/lib/hostr-id.js @@ -13,14 +13,12 @@ function* checkId(Files, fileId, attempts) { return false; } const file = yield Files.findOne({'_id': fileId}); - if(file === null) { + if (file === null) { return fileId; - } else { - return checkId(randomID(), attempts++); } + return checkId(randomID(), ++attempts); // eslint-disable-line no-param-reassign } export default function* (Files) { - let attempts = 0; - return yield checkId(Files, randomID(), attempts); + return yield checkId(Files, randomID(), 0); } diff --git a/lib/malware.js b/lib/malware.js index 84d87c6..9c7a37e 100644 --- a/lib/malware.js +++ b/lib/malware.js @@ -6,33 +6,29 @@ const extensions = ['EXE', 'PIF', 'APPLICATION', 'GADGET', 'MSI', 'MSP', 'COM', 'JAR', 'BAT', 'CMD', 'VB', 'VBS', 'VBE', 'JS', 'JSE', 'WS', 'WSF', 'WSC', 'WSH', 'PS1', 'PS1XML', 'PS2', 'PS2XML', 'PSC1', 'PSC2', 'MSH', 'MSH1', 'MSH2', 'MSHXML', 'MSH1XML', 'MSH2XML', 'SCF', 'LNK', 'INF', 'REG', 'PDF', 'DOC', 'XLS', 'PPT', 'DOCM', 'DOTM', 'XLSM', 'XLTM', 'XLAM', 'PPTM', 'POTM', 'PPAM', 'PPSM', 'SLDM', - 'RAR', 'TAR', 'ZIP', 'GZ' + 'RAR', 'TAR', 'ZIP', 'GZ', ]; function getExtension(filename) { const i = filename.lastIndexOf('.'); return (i < 0) ? '' : filename.substr(i + 1); -}; +} -export default function (file) { - const deferred = {}; - deferred.promise = new Promise(function(resolve, reject) { - deferred.resolve = resolve; - deferred.reject = reject; +export default function(file) { + return new Promise((resolve, reject) => { + if (extensions.indexOf(getExtension(file.file_name.toUpperCase())) >= 0) { + virustotal.getFileReport(file.md5, (err, res) => { + if (err) { + return reject(err); + } + if (res.scans) { + resolve({positive: res.positives >= 5, result: res}); + } else { + resolve(); + } + }); + } else { + resolve(); + } }); - if (extensions.indexOf(getExtension(file.file_name.toUpperCase())) >= 0) { - virustotal.getFileReport(file.md5, function (err, res) { - if (err) { - return deferred.reject(err); - } - if (res.scans) { - deferred.resolve({positive: res.positives >= 5, result: res}); - } else { - deferred.resolve(); - } - }); - } else { - deferred.resolve(); - } - return deferred.promise; -}; +} diff --git a/lib/mongo.js b/lib/mongo.js index e520b9d..c1dad22 100644 --- a/lib/mongo.js +++ b/lib/mongo.js @@ -5,7 +5,7 @@ const debug = debugname('hostr:mongo'); const uristring = process.env.MONGO_URL || process.env.MONGOLAB_URI || 'mongodb://localhost:27017/hostr'; -let configuredClient = new Promise(function (resolve, reject) { +const configuredClient = new Promise((resolve, reject) => { debug('Connecting to Mongodb'); return MongoClient.connect(uristring).then((client) => { debug('Successfully connected to Mongodb'); @@ -20,19 +20,19 @@ let configuredClient = new Promise(function (resolve, reject) { client.ObjectId = mongodb().ObjectId; return resolve(client); }).catch((e) => { - reject(e) + reject(e); }); }).catch((e) => { debug(e); }); export default function() { - return function* (next) { + return function* dbMiddleware(next) { try { this.db = yield configuredClient; } catch (e) { debug(e); } yield next; - } + }; } diff --git a/lib/redis.js b/lib/redis.js index 902e791..94c7f67 100644 --- a/lib/redis.js +++ b/lib/redis.js @@ -9,17 +9,20 @@ const redisUrl = process.env.REDIS_URL || process.env.REDISTOGO_URL || 'redis:// const connection = new Promise((resolve, reject) => { debug('Connecting to Redis'); - resolve(redis.connect(redisUrl)); + const client = redis.connect(redisUrl); + client.on('error', reject); + resolve(client); }).catch((err) => { debug('Connection error: ' + err); + throw err; }); const redisSession = new Promise((resolve, reject) => { return connection.then((client) => { - client = koaRedis({client: client}); + const sessionClient = koaRedis({client: client}); resolve(session({ key: 'hid', - store: client + store: sessionClient, })); }).catch((err) => { debug('koa-redis error: ' + err); @@ -29,31 +32,29 @@ const redisSession = new Promise((resolve, reject) => { const wrapped = new Promise((resolve, reject) => { return connection.then((client) => { - client = coRedis(client); - client.on('error', (err) => { - debug('Client error: ' + err); - reject(err); - }); - client.on('ready', () => { + const asyncClient = coRedis(client); + asyncClient.on('error', reject); + asyncClient.on('ready', () => { debug('Successfully connected to Redis'); - resolve(client); + resolve(asyncClient); }); }).catch((err) => { debug('co-redis error: ' + err); reject(err); + throw err; }); }); export function sessionStore() { - return function* (next) { + return function* sessionStoreMiddleware(next) { const sess = yield redisSession; yield sess.bind(this)(next); - } + }; } export function middleware() { - return function* (next) { + return function* redisMiddleware(next) { this.redis = yield wrapped; yield next; - } + }; } diff --git a/lib/s3.js b/lib/s3.js index b89353e..724255e 100644 --- a/lib/s3.js +++ b/lib/s3.js @@ -13,7 +13,7 @@ export function get(key) { return s3.getObject({Bucket: bucket, Key: 'hostr_files/' + key}).createReadStream(); } -export function upload(key, body) { +export function upload(key) { debug('Uploading file: %s', 'hostr_files/' + key); return s3Stream.upload({Bucket: bucket, Key: 'hostr_files/' + key}); } diff --git a/lib/storage.js b/lib/storage.js index 9441963..7ad8d35 100644 --- a/lib/storage.js +++ b/lib/storage.js @@ -2,8 +2,8 @@ import fs from 'fs'; import path from 'path'; function range(start, stop) { - var result = []; - for (var idx = start.charCodeAt(0), end = stop.charCodeAt(0); idx <= end; ++idx){ + const result = []; + for (let idx = start.charCodeAt(0), end = stop.charCodeAt(0); idx <= end; ++idx) { result.push(String.fromCharCode(idx)); } return result; diff --git a/lib/type.js b/lib/type.js index 736d0e5..099ea80 100644 --- a/lib/type.js +++ b/lib/type.js @@ -23,7 +23,7 @@ const extensions = { 'gz': 'archive', 'tgz': 'archive', 'bz2': 'archive', - 'rar': 'archive' + 'rar': 'archive', }; export function sniff(filename) { diff --git a/package.json b/package.json index dadf3c8..070b20a 100644 --- a/package.json +++ b/package.json @@ -41,13 +41,10 @@ "koa-compress": "~1.0.8", "koa-csrf": "~2.3.0", "koa-favicon": "~1.2.0", - "koa-file-server": "~2.3.1", "koa-generic-session": "~1.9.0", "koa-helmet": "^0.2.0", "koa-logger": "~1.3.0", - "koa-mount": "~1.3.0", "koa-redis": "~1.0.1", - "koa-route": "~2.4.2", "koa-router": "^5.1.2", "koa-static": "^1.4.9", "koa-statsd": "~0.0.2", @@ -70,7 +67,9 @@ "virustotal.js": "~0.3.1" }, "devDependencies": { + "babel-eslint": "^4.0.10", "eslint": "~1.2.1", + "eslint-config-airbnb": "0.0.8", "istanbul": "~0.3.18", "mocha": "~2.2.5", "nodemon": "~1.4.1", diff --git a/web/app.js b/web/app.js index a5ebc52..3d5aa17 100644 --- a/web/app.js +++ b/web/app.js @@ -4,7 +4,6 @@ import csrf from 'koa-csrf'; import views from 'koa-views'; import stats from 'koa-statsd'; import * as redis from '../lib/redis'; -import co from 'co'; import StatsD from 'statsy'; // waiting for PR to be merged, can remove swig dependency when done import errors from '../lib/koa-error'; @@ -13,29 +12,26 @@ import * as file from './routes/file'; import * as pro from './routes/pro'; import * as user from './routes/user'; -import debugname from 'debug'; -const debug = debugname('hostr-web'); - const router = new Router(); router.use(errors({template: path.join(__dirname, 'public', 'error.html')})); -let statsdOpts = {prefix: 'hostr-web', host: process.env.STATSD_HOST || 'localhost'}; +const statsdOpts = {prefix: 'hostr-web', host: process.env.STATSD_HOST || 'localhost'}; router.use(stats(statsdOpts)); -let statsd = new StatsD(statsdOpts); -router.use(function* (next) { +const statsd = new StatsD(statsdOpts); +router.use(function* statsMiddleware(next) { this.statsd = statsd; yield next; }); router.use(redis.sessionStore()); -router.use(function* (next) { +router.use(function* stateMiddleware(next) { this.state = { session: this.session, apiURL: process.env.API_URL, baseURL: process.env.BASE_URL, - stripePublic: process.env.STRIPE_PUBLIC_KEY + stripePublic: process.env.STRIPE_PUBLIC_KEY, }; yield next; }); @@ -43,7 +39,7 @@ router.use(function* (next) { router.use(csrf()); router.use(views('views', { - default: 'ejs' + default: 'ejs', })); router.get('/', index.main); @@ -76,14 +72,14 @@ router.get('/:id', file.landing); router.get('/file/:id/:name', file.get); router.get('/file/:size/:id/:name', file.get); router.get('/files/:id/:name', file.get); -router.get('/download/:id/:name', function* (id) { +router.get('/download/:id/:name', function* downloadRedirect(id) { this.redirect('/' + id); }); -router.get('/updaters/mac', function* () { +router.get('/updaters/mac', function* macUpdater() { this.redirect('/updaters/mac.xml'); }); -router.get('/updaters/mac/changelog', function* () { +router.get('/updaters/mac/changelog', function* macChangelog() { yield this.render('mac-update-changelog'); }); diff --git a/web/lib/auth.js b/web/lib/auth.js index e9d51b5..0bf3101 100644 --- a/web/lib/auth.js +++ b/web/lib/auth.js @@ -7,14 +7,13 @@ import debugname from 'debug'; const debug = debugname('hostr-web:auth'); import { Mandrill } from 'mandrill-api/mandrill'; const mandrill = new Mandrill(process.env.MANDRILL_KEY); -import mongo from '../../lib/mongo'; export function* authenticate(email, password) { const Users = this.db.Users; const Logins = this.db.Logins; const remoteIp = this.headers['x-real-ip'] || this.ip; - if (!password || password.length < 6){ + if (!password || password.length < 6) { debug('No password, or password too short'); return new Error('Invalid login details'); } @@ -33,11 +32,10 @@ export function* authenticate(email, password) { login.successful = true; yield Logins.updateOne({_id: login._id}, login); return user; - } else { - debug('Password invalid'); - login.successful = false; - yield Logins.updateOne({_id: login._id}, login); } + debug('Password invalid'); + login.successful = false; + yield Logins.updateOne({_id: login._id}, login); } else { debug('Email invalid'); login.successful = false; @@ -58,9 +56,9 @@ export function* setupSession(user) { 'maxFileSize': 20971520, 'joined': user.joined, 'plan': user.type || 'Free', - 'uploadsToday': yield this.db.Files.count({owner: user._id, 'time_added': {'$gt': Math.ceil(Date.now()/1000)-86400}}), + 'uploadsToday': yield this.db.Files.count({owner: user._id, 'time_added': {'$gt': Math.ceil(Date.now() / 1000) - 86400}}), 'token': token, - 'md5': crypto.createHash('md5').update(user.email).digest('hex') + 'md5': crypto.createHash('md5').update(user.email).digest('hex'), }; if (sessionUser.plan === 'Pro') { @@ -71,7 +69,7 @@ export function* setupSession(user) { this.session.user = sessionUser; if (this.request.body.remember && this.request.body.remember === 'on') { const Remember = this.db.Remember; - var rememberToken = uuid(); + const rememberToken = uuid(); Remember.save({_id: rememberToken, 'user_id': user.id, created: new Date().getTime()}); this.cookies.set('r', rememberToken, { maxAge: 1209600000, httpOnly: true}); } @@ -87,12 +85,12 @@ export function* signup(email, password, ip) { throw new Error('Email already in use.'); } const cryptedPassword = yield passwords.crypt(password); - var user = { + const user = { email: email, 'salted_password': cryptedPassword, joined: Math.round(new Date().getTime() / 1000), 'signup_ip': ip, - activationCode: uuid() + activationCode: uuid(), }; Users.insertOne(user); @@ -112,11 +110,11 @@ ${process.env.BASE_URL + '/activate/' + user.activationCode} 'from_name': 'Jonathan from Hostr', to: [{ email: user.email, - type: 'to' + type: 'to', }], 'tags': [ - 'user-activation' - ] + 'user-activation', + ], }}); } @@ -126,11 +124,11 @@ export function* sendResetToken(email) { const Reset = this.db.Reset; const user = yield Users.findOne({email: email}); if (user) { - var token = uuid.v4(); + const token = uuid.v4(); Reset.save({ '_id': user._id, 'token': token, - 'created': Math.round(new Date().getTime() / 1000) + 'created': Math.round(new Date().getTime() / 1000), }); const html = yield render('email/inlined/forgot', {forgotUrl: process.env.BASE_URL + '/forgot/' + token}); const text = `It seems you've forgotten your password :( @@ -144,11 +142,11 @@ Visit ${process.env.BASE_URL + '/forgot/' + token} to set a new one. 'from_name': 'Jonathan from Hostr', to: [{ email: user.email, - type: 'to' + type: 'to', }], 'tags': [ - 'password-reset' - ] + 'password-reset', + ], }}); } else { throw new Error('There was an error looking up your email address.'); diff --git a/web/public/.eslintignore b/web/public/.eslintignore new file mode 100644 index 0000000..7e15a05 --- /dev/null +++ b/web/public/.eslintignore @@ -0,0 +1,3 @@ +jspm_packages/ +config.js +lazy-src.js diff --git a/web/public/src/app.js b/web/public/src/app.js index cb4e78e..8f26c60 100644 --- a/web/public/src/app.js +++ b/web/public/src/app.js @@ -1,11 +1,11 @@ import angular from 'angular'; -import ngRoute from 'angular/route'; -import ngResource from 'angular/resource'; -import ReconnectingWebSocket from 'angular-reconnecting-websocket'; -import ngDimensions from 'angular-strap/dist/modules/dimensions'; -import ngStrapCore from 'angular-strap/dist/modules/compiler'; -import ngTooltip from 'angular-strap/dist/modules/tooltip'; -import ngTooltipTemplate from 'angular-strap/dist/modules/tooltip.tpl'; +import 'angular/route'; +import 'angular/resource'; +import 'angular-reconnecting-websocket'; +import 'angular-strap/dist/modules/dimensions'; +import 'angular-strap/dist/modules/compiler'; +import 'angular-strap/dist/modules/tooltip'; +import 'angular-strap/dist/modules/tooltip.tpl'; import { FilesController, FileController, AccountController, ProController, BillingController } from './app/controllers'; import { appHeader, appFooter, menuDropdown, searchShortcut, stripeSubscribe } from './app/directives'; @@ -15,12 +15,12 @@ import { fileSize, direct } from './app/filters'; import { FileService, UserService, EventService, TransactionService, SettingService } from './app/services'; // Declare app level module which depends on filters, and services -var app = angular.module('hostr', [ +const app = angular.module('hostr', [ 'ngRoute', 'ngResource', 'reconnectingWebSocket', 'mgcrea.ngStrap.core', - 'mgcrea.ngStrap.tooltip' + 'mgcrea.ngStrap.tooltip', ]); app.factory('FileService', ['$resource', '$cacheFactory', FileService.factory]); @@ -40,21 +40,20 @@ app.directive('lazySrc', ['$window', '$document', lazySrc]); app.directive('searchShortcut', ['$document', searchShortcut]); app.directive('stripeSubscribe', ['$http', stripeSubscribe]); -app.config(['$routeProvider', '$locationProvider', '$httpProvider', '$tooltipProvider', function($routeProvider, $locationProvider, $httpProvider, $tooltipProvider) { - +app.config(['$routeProvider', '$locationProvider', '$httpProvider', ($routeProvider, $locationProvider, $httpProvider) => { if (typeof window.user !== 'undefined') { $httpProvider.defaults.headers.common.Authorization = ':' + window.user.token; } $locationProvider.html5Mode(true); - $httpProvider.interceptors.push(['$q', function($q) { + $httpProvider.interceptors.push(['$q', ($q) => { return { - responseError: function(rejection) { + responseError: (rejection) => { if (rejection.status === 401) { window.location = '/logout'; } return $q.reject(rejection); - } + }, }; }]); @@ -63,66 +62,65 @@ app.config(['$routeProvider', '$locationProvider', '$httpProvider', '$tooltipPro controller: FilesController, title: ' - Files', resolve: { - files: ['FileService', function(Files) { + files: ['FileService', (Files) => { return Files.query(); - }] - } + }], + }, }) .when('/apps', { templateUrl: '/build/partials/apps.html', - title: ' - Apps for Mac and Windows' + title: ' - Apps for Mac and Windows', }) .when('/pro', { templateUrl: '/build/partials/pro.html', controller: ProController, - title: ' - Pro' + title: ' - Pro', }) .when('/account', { templateUrl: '/build/partials/account.html', controller: AccountController, - title: ' - Account' + title: ' - Account', }) .when('/billing', { templateUrl: '/build/partials/billing.html', controller: BillingController, - title: ' - Billing' + title: ' - Billing', }) .when('/terms', { templateUrl: '/build/partials/terms.html', - title: ' - Terms of Service' + title: ' - Terms of Service', }) .when('/privacy', { templateUrl: '/build/partials/privacy.html', - title: ' - Privacy Policy' + title: ' - Privacy Policy', }) .when('/:id', { templateUrl: '/build/partials/file.html', controller: FileController, resolve: { - file: ['$route', 'FileService', function($route, Files) { + file: ['$route', 'FileService', ($route, Files) => { return Files.get({id: $route.current.params.id}); - }] - } + }], + }, }); }]); -app.run(['$location', '$rootScope', function($location, $rootScope) { - - $rootScope.$on('$routeChangeStart', function(e, curr) { - if (curr.$$route && curr.$$route.resolve) { +app.run(['$location', '$rootScope', ($location, $rootScope) => { + $rootScope.$on('$routeChangeStart', (e, curr) => { + if (curr.$$route && curr.$$route.resolve) { // Show a loading message until promises are resolved - $rootScope.loadingView = true; - } - }); - $rootScope.$on('$routeChangeSuccess', function (event, current) { + $rootScope.loadingView = true; + } + }); + $rootScope.$on('$routeChangeSuccess', (event, current) => { $rootScope.navError = false; $rootScope.pageTitle = current.$$route.title; }); - $rootScope.$on('$routeChangeError', function () { + $rootScope.$on('$routeChangeError', () => { $rootScope.loadingView = false; $rootScope.navError = true; }); - $rootScope.$on('$locationChangeStart', function(event, newUrl) { + $rootScope.$on('$locationChangeStart', (event, newUrl) => { if (window.ga) { window.ga('send', 'pageview', newUrl); } diff --git a/web/public/src/app/controllers.js b/web/public/src/app/controllers.js index eeb8cb3..c9465ba 100644 --- a/web/public/src/app/controllers.js +++ b/web/public/src/app/controllers.js @@ -1,17 +1,17 @@ export class FilesController { constructor($scope, UserService, EventService, files) { $scope.$root.user = UserService.get(); - files.$promise.then(function() { + files.$promise.then(() => { $scope.$root.loadingView = false; }); $scope.header = 'full'; if (!$scope.$root.files) { $scope.$root.files = files; } - $scope.remove = function(file) { - $scope.$root.files.some(function(existingFile, index) { + $scope.remove = (file) => { + $scope.$root.files.some((existingFile, index) => { if (file.id === existingFile.id) { - file.$remove(function() { + file.$remove(() => { $scope.$root.showDropdown = false; $scope.$root.files.splice(index, 1); }); @@ -25,8 +25,8 @@ export class FilesController { FilesController.$inject = ['$scope', 'UserService', 'EventService', 'files']; export class FileController { - constructor ($scope, $rootScope, $routeParams, ReconnectingWebSocket, file) { - file.$promise.then(function() { + constructor($scope, $rootScope, $routeParams, ReconnectingWebSocket, file) { + file.$promise.then(() => { $scope.$root.loadingView = false; $scope.header = 'small'; $scope.file = file; @@ -34,25 +34,25 @@ export class FileController { $rootScope.pageTitle = ' - ' + file.name; if (file.status === 'uploading') { file.percent = 0; - var ws = new ReconnectingWebSocket(window.settings.apiURL.replace(/^http/, 'ws') + '/file/' + file.id); - ws.onmessage = function (msg) { - var evt = JSON.parse(msg.data); + const ws = new ReconnectingWebSocket(window.settings.apiURL.replace(/^http/, 'ws') + '/file/' + file.id); + ws.onmessage = (msg) => { + const evt = JSON.parse(msg.data); $rootScope.$broadcast(evt.type, evt.data); }; - ws.onopen = function() { + ws.onopen = () => { ws.send(JSON.stringify({authorization: window.user.token})); }; - $rootScope.$on('file-progress', function(evt, data) { + $rootScope.$on('file-progress', (evt, data) => { $scope.file.percent = data.complete; }); - $rootScope.$on('file-added', function(evt, data) { + $rootScope.$on('file-added', (evt, data) => { $scope.file = data; }); - $rootScope.$on('file-accepted', function(evt, data) { + $rootScope.$on('file-accepted', (evt, data) => { $scope.file = data; }); } - }, function() { + }, () => { $rootScope.navError = true; $scope.$root.loadingView = false; }); @@ -61,15 +61,15 @@ export class FileController { FileController.$inject = ['$scope', '$rootScope', '$routeParams', 'WebSocket', 'file']; export class ProController { - constructor ($scope, $http, UserService) { + constructor($scope, $http, UserService) { $scope.$root.loadingView = false; $scope.user = UserService.get(); $scope.header = 'full'; - $scope.cancel = function() { - $http.post('/pro/cancel').success(function() { + $scope.cancel = () => { + $http.post('/pro/cancel').success(() => { window.location.reload(true); - }).error(function(data) { - console.log(new Error(data)); + }).error((data) => { + console.error(new Error(data)); }); }; } @@ -77,17 +77,17 @@ export class ProController { ProController.$inject = ['$scope', '$http', 'UserService']; export class AccountController { - constructor ($scope, UserService, SettingService) { + constructor($scope, UserService, SettingService) { $scope.$root.loadingView = false; $scope.$root.user = UserService.get(); - $scope.submit = function(form) { + $scope.submit = (form) => { $scope.updated = false; $scope.error = false; - SettingService.update(form).then(function() { + SettingService.update(form).then(() => { $scope.updated = true; delete $scope.user.new_password; delete $scope.user.current_password; - }, function(response) { + }, (response) => { $scope.error = response.data.error.message; }); }; @@ -96,7 +96,7 @@ export class AccountController { AccountController.$inject = ['$scope', 'UserService', 'SettingService']; export class BillingController { - constructor ($scope, UserService, TransactionService) { + constructor($scope, UserService, TransactionService) { $scope.$root.loadingView = false; $scope.$root.user = UserService.get(); $scope.transactions = TransactionService.query(); diff --git a/web/public/src/app/directives.js b/web/public/src/app/directives.js index 5ad1439..8ac64ce 100644 --- a/web/public/src/app/directives.js +++ b/web/public/src/app/directives.js @@ -9,7 +9,7 @@ export function appHeader() { scope.userMD5 = window.user.md5; scope.email = window.user.email; scope.pro = (window.user.type === 'Pro'); - } + }, }; } @@ -23,19 +23,19 @@ export function appFooter() { scope.userMD5 = window.user.md5; scope.email = window.user.email; scope.pro = (window.user.type === 'Pro'); - } + }, }; } export function menuDropdown() { - return function($scope, element) { + return ($scope, element) => { $scope.$root.overlayClick = function overlayClick() { $scope.$root.showDropdown = false; $('.dropdown').hide(); }; - var activeDropdown = $(element).find('.dropdown'); - element.on('click', function(e) { + const activeDropdown = $(element).find('.dropdown'); + element.on('click', (e) => { if (activeDropdown.not(':visible').length > 0) { $('.dropdown').hide(); $scope.$root.showDropdown = true; @@ -50,10 +50,10 @@ export function menuDropdown() { } -export function searchShortcut ($document) { - return function($scope, element) { - $document.bind('keypress', function(event) { - if(event.which === 47) { +export function searchShortcut($document) { + return ($scope, element) => { + $document.bind('keypress', (event) => { + if (event.which === 47) { if (['INPUT', 'TEXTAREA'].indexOf(document.activeElement.tagName) < 0) { element[0].focus(); event.preventDefault(); @@ -68,21 +68,23 @@ export function stripeSubscribe($http) { const handler = window.StripeCheckout.configure({ key: window.settings.stripePublic, image: '/images/stripe-128.png', - token: function(token) { - $http.post('/pro/create', {stripeToken: token}) - .success(function(data) { + token: (token) => { + $http.post('/pro/create', { + stripeToken: token, + }) + .success((data) => { if (data.status === 'active') { window.user.plan = 'Pro'; window.location.reload(true); } }) - .error(function() { - alert('Error upgrading your account'); + .error(() => { + console.error('Error upgrading your account'); }); - } + }, }); - return function(scope, element) { - element.on('click', function() { + return (scope, element) => { + element.on('click', () => { // Open Checkout with further options handler.open({ name: 'Hostr', @@ -91,7 +93,7 @@ export function stripeSubscribe($http) { amount: 600, currency: 'USD', panelLabel: 'Subscribe {{amount}}', - billingAddress: false + billingAddress: false, }); }); }; diff --git a/web/public/src/app/directives/dropzone.js b/web/public/src/app/directives/dropzone.js index b2ce1e0..8de387a 100644 --- a/web/public/src/app/directives/dropzone.js +++ b/web/public/src/app/directives/dropzone.js @@ -11,56 +11,56 @@ function guid() { } export default function dropzone(FileService, $cacheFactory) { - var dropOverlay = document.getElementById('filedrop-overlay'); - var dropzoneEl; - var errorTimeout; - return function($scope) { - $scope.$on('$viewContentLoaded', function() { + const dropOverlay = document.getElementById('filedrop-overlay'); + let dropzoneEl; + let errorTimeout; + return ($scope) => { + $scope.$on('$viewContentLoaded', () => { if (!dropzoneEl) { $scope.$root.uploadingFiles = []; - var clickable = [].slice.call(document.querySelectorAll('.choose-file')); + const clickable = [].slice.call(document.querySelectorAll('.choose-file')); dropzoneEl = new Dropzone(document.body, { - url: window.settings.apiURL + '/file', - maxFilesize: window.user.maxFileSize / 1024 / 1024, - maxThumbnailFilesize: 5, - thumbnailWidth: 150, - thumbnailHeight: 98, - parallelUploads: 1, - uploadMultiple: false, - clickable: clickable.length ? clickable : false, - autoDiscover: false, - headers: {'Authorization': ':' + window.user.token}, - previewsContainer: false + url: window.settings.apiURL + '/file', + maxFilesize: window.user.maxFileSize / 1024 / 1024, + maxThumbnailFilesize: 5, + thumbnailWidth: 150, + thumbnailHeight: 98, + parallelUploads: 1, + uploadMultiple: false, + clickable: clickable.length ? clickable : false, + autoDiscover: false, + headers: {'Authorization': ':' + window.user.token}, + previewsContainer: false, }); - dropzoneEl.on('thumbnail', function(file, thumbnail){ + dropzoneEl.on('thumbnail', (file, thumbnail) => { file.thumbnail = thumbnail; $scope.$apply(); }); - dropzoneEl.on('addedfile', function(file){ - var id = guid(); + dropzoneEl.on('addedfile', (file) => { + const id = guid(); file.guid = id; $scope.$root.uploadingFiles.push(file); $scope.$apply(); }); - dropzoneEl.on('sending', function(file, xhr) { + dropzoneEl.on('sending', (file, xhr) => { xhr.setRequestHeader('hostr-guid', file.guid); }); - dropzoneEl.on('uploadprogress', function(file, progress) { + dropzoneEl.on('uploadprogress', (file, progress) => { $scope.$root.progress = { name: file.name, percent: progress, - status: 'Uploading' + status: 'Uploading', }; if (progress === 100) { $scope.$root.progress.status = 'Processing'; } $scope.$apply(); }); - dropzoneEl.on('complete', function(file){ + dropzoneEl.on('complete', (file) => { delete $scope.$root.progress; $scope.$apply(); - $scope.$root.uploadingFiles.some(function(uploadingFile, index) { + $scope.$root.uploadingFiles.some((uploadingFile, index) => { if (uploadingFile.guid === file.guid) { $scope.$root.uploadingFiles.splice(index, 1); $scope.$apply(); @@ -69,11 +69,10 @@ export default function dropzone(FileService, $cacheFactory) { return false; }); }); - dropzoneEl.on('error', function(evt, error){ + dropzoneEl.on('error', (evt, error) => { if (error.error) { $scope.$root.uploadError = 'Error uploading file: ' + evt.name + '. ' + error.error.message; - } - else if (evt.name) { + } else if (evt.name) { $scope.$root.uploadError = 'Error uploading file: ' + evt.name + '. ' + error; } else { if (error[0] !== '<') { @@ -82,33 +81,33 @@ export default function dropzone(FileService, $cacheFactory) { } $scope.$apply(); clearTimeout(errorTimeout); - errorTimeout = setTimeout(function() { + errorTimeout = setTimeout(() => { $scope.$root.uploadError = ''; $scope.$apply(); }, 5000); }); - var addFile = function(newFile) { - if (!$scope.$root.files.some(function (file) { - return file.id === newFile.id; - })) { - var cache = $cacheFactory.get('files-cache'); + const addFile = (newFile) => { + if (!$scope.$root.files.some((file) => { + return file.id === newFile.id; + })) { + const cache = $cacheFactory.get('files-cache'); cache.removeAll(); - var file = new FileService(newFile); + const file = new FileService(newFile); $scope.$root.files.unshift(file); $scope.$root.user.uploads_today++; $scope.$apply(); } }; - dropzoneEl.on('success', function(file, response){ + dropzoneEl.on('success', (file, response) => { addFile(response); }); - $scope.$on('file-added', function(event, data){ + $scope.$on('file-added', (event, data) => { addFile(data); }); - $scope.$on('file-accepted', function(event, data){ - $scope.$root.uploadingFiles.some(function(file) { + $scope.$on('file-accepted', (event, data) => { + $scope.$root.uploadingFiles.some((file) => { if (file.guid === data.guid) { file.id = data.id; file.href = data.href; @@ -117,33 +116,33 @@ export default function dropzone(FileService, $cacheFactory) { } }); }); - $scope.$on('file-deleted', function(evt, data) { - $scope.$root.files.forEach(function(file, index) { - if(data.id === file.id) { + $scope.$on('file-deleted', (evt, data) => { + $scope.$root.files.forEach((file, index) => { + if (data.id === file.id) { delete $scope.$root.files[index]; $scope.$digest(); } }); }); - document.body.addEventListener('dragenter', function(){ + document.body.addEventListener('dragenter', () => { dropOverlay.style.display = 'block'; }); - dropOverlay.addEventListener('dragleave', function(event){ + dropOverlay.addEventListener('dragleave', (event) => { if (event.target.outerText !== 'Drop files to upload' || event.x === 0) { dropOverlay.style.display = 'none'; } }); - dropOverlay.addEventListener('drop', function(){ + dropOverlay.addEventListener('drop', () => { dropOverlay.style.display = 'none'; }); } else { - var clicker = [].slice.call(document.querySelectorAll('.choose-file')); + const clicker = [].slice.call(document.querySelectorAll('.choose-file')); if (clicker) { - clicker.forEach(function(el) { - el.addEventListener('click', function() { + clicker.forEach((el) => { + el.addEventListener('click', () => { return dropzoneEl.hiddenFileInput.click(); }); }); diff --git a/web/public/src/app/filters.js b/web/public/src/app/filters.js index f781431..1820285 100644 --- a/web/public/src/app/filters.js +++ b/web/public/src/app/filters.js @@ -1,33 +1,29 @@ export function fileSize() { - return function(input) { + return (input) => { if (input >= 1073741824) { - input = Math.round((input / 1073741824) * 10) / 10 + 'GB'; - } else { - if (input >= 1048576) { - input = Math.round((input / 1048576) * 10) / 10 + 'MB'; - } else { - if (input >= 1024) { - input = Math.round((input / 1024) * 10) / 10 + 'KB'; - } else { - input = Math.round(input) + 'B'; - } - } + return Math.round((input / 1073741824) * 10) / 10 + 'GB'; } - return input; + if (input >= 1048576) { + return Math.round((input / 1048576) * 10) / 10 + 'MB'; + } + if (input >= 1024) { + return Math.round((input / 1024) * 10) / 10 + 'KB'; + } + return Math.round(input) + 'B'; }; } export function direct() { - return function(file) { - if(file.name) { + return (file) => { + if (file.name) { if (file.direct && file.name.split('.').pop().toLowerCase() === 'psd') { return file.direct['970x'].replace('/970/', '/').slice(0, -4); - } else if (file.direct && file.direct['970x']) { - return file.direct['970x'].replace('/970/', '/'); - } else { - return file.href.replace('hostr.co/', 'hostr.co/file/') + '/' + file.name; } + if (file.direct && file.direct['970x']) { + return file.direct['970x'].replace('/970/', '/'); + } + return file.href.replace('hostr.co/', 'hostr.co/file/') + '/' + file.name; } }; } diff --git a/web/public/src/app/services.js b/web/public/src/app/services.js index 38cee6b..ff8da5d 100644 --- a/web/public/src/app/services.js +++ b/web/public/src/app/services.js @@ -1,72 +1,92 @@ export class FileService { - constructor($resource, $cacheFactory) { - var cache = $cacheFactory('files-cache'); - return $resource(window.settings.apiURL + '/file/:id', {id: '@id'}, { - query: {method: 'GET', isArray: true, cache: cache, - params: {perpage: 0, all: true} + constructor($resource, $cacheFactory) { + const cache = $cacheFactory('files-cache'); + return $resource(window.settings.apiURL + '/file/:id', { + id: '@id', + }, { + query: { + method: 'GET', + isArray: true, + cache: cache, + params: { + perpage: 0, + all: true, + }, + }, + delete: { + method: 'DELETE', + isArray: true, + cache: cache, }, - delete: {method: 'DELETE', isArray: true, cache: cache} }); - } + } - static factory($resource, $cacheFactory) { + static factory($resource, $cacheFactory) { return new FileService($resource, $cacheFactory); } } export class UserService { - constructor($resource) { - return $resource(window.settings.apiURL + '/user'); - } + constructor($resource) { + return $resource(window.settings.apiURL + '/user'); + } - static factory($resource) { + static factory($resource) { return new UserService($resource); } } export class EventService { - constructor($rootScope, ReconnectingWebSocket) { - if (window.user && WebSocket) { - let ws = new ReconnectingWebSocket('wss' + window.settings.apiURL.replace('https', '').replace('http', '') + '/user'); - ws.onmessage = function (msg) { - var evt = JSON.parse(msg.data); + constructor($rootScope, ReconnectingWebSocket) { + if (window.user && WebSocket) { + const ws = new ReconnectingWebSocket('wss' + window.settings.apiURL.replace('https', '').replace('http', '') + '/user'); + ws.onmessage = (msg) => { + const evt = JSON.parse(msg.data); $rootScope.$broadcast(evt.type, evt.data); }; - ws.onopen = function() { - ws.send(JSON.stringify({authorization: window.user.token})); + ws.onopen = () => { + ws.send(JSON.stringify({ + authorization: window.user.token, + })); }; } return true; - } + } - static factory($rootScope, ReconnectingWebSocket) { - return new EventService($rootScope, ReconnectingWebSocket); - } + static factory($rootScope, ReconnectingWebSocket) { + return new EventService($rootScope, ReconnectingWebSocket); + } } export class TransactionService { - constructor ($resource, $cacheFactory) { - var cache = $cacheFactory('transaction-cache'); - return $resource(window.settings.apiURL + '/user/transaction/:id', {id: '@id'}, { - query: {method: 'GET', isArray: true, cache: cache} + constructor($resource, $cacheFactory) { + const cache = $cacheFactory('transaction-cache'); + return $resource(window.settings.apiURL + '/user/transaction/:id', { + id: '@id', + }, { + query: { + method: 'GET', + isArray: true, + cache: cache, + }, }); } - static factory($resource, $cacheFactory) { + static factory($resource, $cacheFactory) { return new TransactionService($resource, $cacheFactory); } } export class SettingService { - constructor ($http) { - var service = {}; - service.update = function(data) { + constructor($http) { + const service = {}; + service.update = (data) => { return $http.post(window.settings.apiURL + '/user/settings', data); }; return service; } - static factory($http) { + static factory($http) { return new SettingService($http); } } diff --git a/web/routes/file.js b/web/routes/file.js index f402558..9657d2c 100644 --- a/web/routes/file.js +++ b/web/routes/file.js @@ -1,39 +1,35 @@ -import fs from 'fs'; import path from 'path'; import mime from 'mime-types'; import hostrFileStream from '../../lib/hostr-file-stream'; import { formatFile } from '../../lib/format'; -import debugname from 'debug'; -const debug = debugname('hostr-web:file'); - const storePath = process.env.STORE_PATH || path.join(process.env.HOME, '.hostr', 'uploads'); -const userAgentCheck = function(userAgent) { - if (!userAgent){ +function userAgentCheck(userAgent) { + if (!userAgent) { return false; } return userAgent.match(/^(wget|curl|vagrant)/i); -}; +} -const hotlinkCheck = function(file, userAgent, referrer) { - return !userAgentCheck(userAgent) && !file.width && (!referrer || !(referrer.match(/^https:\/\/hostr.co/) || referrer.match(/^http:\/\/localhost:4040/))) -}; +function hotlinkCheck(file, userAgent, referrer) { + return !userAgentCheck(userAgent) && !file.width && (!referrer || !(referrer.match(/^https:\/\/hostr.co/) || referrer.match(/^http:\/\/localhost:4040/))); +} export function* get() { const file = yield this.db.Files.findOne({_id: this.params.id, 'file_name': this.params.name, 'status': 'active'}); this.assert(file, 404); - if (hotlinkCheck(file, this.headers['user-agent'], this.headers['referer'])) { + if (hotlinkCheck(file, this.headers['user-agent'], this.headers.referer)) { return this.redirect('/' + file._id); } - if (!file.width && this.request.query.warning != 'on') { + if (!file.width && this.request.query.warning !== 'on') { return this.redirect('/' + file._id); } if (file.malware) { - let alert = this.request.query.alert; + const alert = this.request.query.alert; if (!alert || !alert.match(/i want to download malware/i)) { return this.redirect('/' + file._id); } @@ -70,9 +66,9 @@ export function* get() { if (!this.params.size || (this.params.size && this.params.size > 150)) { this.db.Files.updateOne( - {_id: file._id}, - {'$set': {'last_accessed': Math.ceil(Date.now()/1000)}, '$inc': {downloads: 1}}, - {w:0} + {'_id': file._id}, + {'$set': {'last_accessed': Math.ceil(Date.now() / 1000)}, '$inc': {downloads: 1}}, + {'w': 0} ); } @@ -86,7 +82,7 @@ export function* resized() { export function* landing() { const file = yield this.db.Files.findOne({_id: this.params.id, status: 'active'}); this.assert(file, 404); - if(userAgentCheck(this.headers['user-agent'])) { + if (userAgentCheck(this.headers['user-agent'])) { this.params.name = file.file_name; return yield get.call(this); } diff --git a/web/routes/index.js b/web/routes/index.js index fc49641..3079cc4 100644 --- a/web/routes/index.js +++ b/web/routes/index.js @@ -32,24 +32,24 @@ export function* staticPage(next) { this.session.user.token = token; yield this.render('index', {user: this.session.user}); } else { - switch(this.originalUrl) { - case '/terms': - yield this.render('terms'); - break; - case '/privacy': - yield this.render('privacy'); - break; - case '/pricing': - yield this.render('pricing'); - break; - case '/apps': - yield this.render('apps'); - break; - case '/stats': - yield this.render('index', {user: {}}); - break; - default: - yield next; + switch (this.originalUrl) { + case '/terms': + yield this.render('terms'); + break; + case '/privacy': + yield this.render('privacy'); + break; + case '/pricing': + yield this.render('pricing'); + break; + case '/apps': + yield this.render('apps'); + break; + case '/stats': + yield this.render('index', {user: {}}); + break; + default: + yield next; } } } diff --git a/web/routes/pro.js b/web/routes/pro.js index 61da1d9..391b79d 100644 --- a/web/routes/pro.js +++ b/web/routes/pro.js @@ -17,7 +17,7 @@ export function* create() { const createCustomer = { card: stripeToken.id, plan: 'usd_monthly', - email: this.session.email + email: this.session.email, }; const customer = yield stripe.customers.create(createCustomer); @@ -32,7 +32,7 @@ export function* create() { 'user_id': this.session.user.id, amount: customer.subscription.plan.amount, desc: customer.subscription.plan.name, - date: new Date(customer.subscription.plan.created * 1000) + date: new Date(customer.subscription.plan.created * 1000), }; yield Transactions.insertOne(transaction); @@ -40,8 +40,8 @@ export function* create() { this.session.user.plan = 'Pro'; this.body = {status: 'active'}; - let html = yield render('email/inlined/pro'); - let text = `Hey, thanks for upgrading to Hostr Pro! + const html = yield render('email/inlined/pro'); + const text = `Hey, thanks for upgrading to Hostr Pro! You've signed up for Hostr Pro Monthly at $6/Month. @@ -56,11 +56,11 @@ export function* create() { 'from_name': fromName, to: [{ email: this.session.user.email, - type: 'to' + type: 'to', }], 'tags': [ - 'pro-upgrade' - ] + 'pro-upgrade', + ], }}); } diff --git a/web/routes/user.js b/web/routes/user.js index bae1f1c..7b3abae 100644 --- a/web/routes/user.js +++ b/web/routes/user.js @@ -10,16 +10,15 @@ export function* signin() { this.statsd.incr('auth.attempt', 1); this.assertCSRF(this.request.body); const user = yield authenticate.call(this, this.request.body.email, this.request.body.password); - if(!user) { + if (!user) { this.statsd.incr('auth.failure', 1); return yield this.render('signin', {error: 'Invalid login details', csrf: this.csrf}); } else if (user.activationCode) { return yield this.render('signin', {error: 'Your account hasn\'t been activated yet. Check your for an activation email.', csrf: this.csrf}); - } else { - this.statsd.incr('auth.success', 1); - yield setupSession.call(this, user); - this.redirect('/'); } + this.statsd.incr('auth.success', 1); + yield setupSession.call(this, user); + this.redirect('/'); } @@ -60,7 +59,7 @@ export function* forgot() { } this.assertCSRF(this.request.body); const tokenUser = yield validateResetToken.call(this, token); - var userId = tokenUser._id; + const userId = tokenUser._id; yield updatePassword.call(this, userId, this.request.body.password); yield Reset.deleteOne({_id: userId}); const user = yield Users.findOne({_id: userId}); @@ -72,13 +71,12 @@ export function* forgot() { if (!tokenUser) { this.statsd.incr('auth.reset.fail', 1); return yield this.render('forgot', {error: 'Invalid password reset token. It might be expired, or has already been used.', token: null, csrf: this.csrf}); - } else { - return yield this.render('forgot', {token: token, csrf: this.csrf}); } + return yield this.render('forgot', {token: token, csrf: this.csrf}); } else if (this.request.body.email) { this.assertCSRF(this.request.body); try { - var email = this.request.body.email; + const email = this.request.body.email; yield sendResetToken.call(this, email); this.statsd.incr('auth.reset.request', 1); return yield this.render('forgot', {message: 'We\'ve sent an email with a link to reset your password. Be sure to check your spam folder if you it doesn\'t appear within a few minutes', token: null, csrf: this.csrf});