'use strict'; const nprogress = require('nprogress'); const cookies = require('js-cookie'); const request = require('superagent'); const config = require('./config.js'); const events = require('./events.js'); class Api extends events.EventTarget { constructor() { super(); this.user = null; this.userName = null; this.userPassword = null; this.cache = {}; this.allRanks = [ 'anonymous', 'restricted', 'regular', 'power', 'moderator', 'administrator', 'nobody', ]; this.rankNames = new Map([ ['anonymous', 'Anonymous'], ['restricted', 'Restricted user'], ['regular', 'Regular user'], ['power', 'Power user'], ['moderator', 'Moderator'], ['administrator', 'Administrator'], ['nobody', 'Nobody'], ]); } get(url, options) { if (url in this.cache) { return new Promise((resolve, reject) => { resolve(this.cache[url]); }); } return this._process(url, request.get, {}, {}, options) .then(response => { this.cache[url] = response; return Promise.resolve(response); }); } post(url, data, files, options) { this.cache = {}; return this._process(url, request.post, data, files, options); } put(url, data, files, options) { this.cache = {}; return this._process(url, request.put, data, files, options); } delete(url, data, options) { this.cache = {}; return this._process(url, request.delete, data, {}, options); } _process(url, requestFactory, data, files, options) { options = options || {}; const [fullUrl, query] = this._getFullUrl(url); let abortFunction = null; let promise = new Promise((resolve, reject) => { let req = requestFactory(fullUrl); req.set('Accept', 'application/json'); if (query) { req.query(query); } if (data) { req.attach('metadata', new Blob([JSON.stringify(data)])); } if (files) { for (let key of Object.keys(files)) { req.attach(key, files[key] || new Blob()); } } try { if (this.userName && this.userPassword) { req.auth( this.userName, encodeURIComponent(this.userPassword) .replace(/%([0-9A-F]{2})/g, (match, p1) => { return String.fromCharCode('0x' + p1); })); } } catch (e) { reject({ title: 'Authentication error', description: 'Malformed credentials'}); } if (!options.noProgress) { nprogress.start(); } abortFunction = () => { req.abort(); // does *NOT* call the callback passed in .end() nprogress.done(); reject({ title: 'Cancelled', description: 'The request was aborted due to user cancel.'}); }; req.end((error, response) => { nprogress.done(); if (error) { reject(response && response.body ? response.body : { title: 'Networking error', description: error.message}); } else { resolve(response.body); } }); }); promise.abort = () => abortFunction(); return promise; } hasPrivilege(lookup) { let minViableRank = null; for (let privilege of Object.keys(config.privileges)) { if (!privilege.startsWith(lookup)) { continue; } const rankName = config.privileges[privilege]; const rankIndex = this.allRanks.indexOf(rankName); if (minViableRank === null || rankIndex < minViableRank) { minViableRank = rankIndex; } } if (minViableRank === null) { throw `Bad privilege name: ${lookup}`; } let myRank = this.user !== null ? this.allRanks.indexOf(this.user.rank) : 0; return myRank >= minViableRank; } loginFromCookies() { return new Promise((resolve, reject) => { const auth = cookies.getJSON('auth'); if (auth && auth.user && auth.password) { this.login(auth.user, auth.password, true) .then(resolve) .catch(errorMessage => { reject(errorMessage); }); } else { resolve(); } }); } login(userName, userPassword, doRemember) { this.cache = {}; return new Promise((resolve, reject) => { this.userName = userName; this.userPassword = userPassword; this.get('/user/' + userName + '?bump-login=true') .then(response => { const options = {}; if (doRemember) { options.expires = 365; } cookies.set( 'auth', {'user': userName, 'password': userPassword}, options); this.user = response; resolve(); this.dispatchEvent(new CustomEvent('login')); }, response => { reject(response.description || response || 'Unknown error'); this.logout(); }); }); } logout() { this.user = null; this.userName = null; this.userPassword = null; this.dispatchEvent(new CustomEvent('logout')); } forget() { cookies.remove('auth'); } isLoggedIn(user) { if (user) { return this.userName !== null && this.userName.toLowerCase() === user.name.toLowerCase(); } else { return this.userName !== null; } } _getFullUrl(url) { const fullUrl = (config.apiUrl + '/' + url).replace(/([^:])\/+/g, '$1/'); const matches = fullUrl.match(/^([^?]*)\??(.*)$/); const baseUrl = matches[1]; const request = matches[2]; return [baseUrl, request]; } } module.exports = new Api();