'use strict'; exports = module.exports = { list, add, get, update, remove, getIcon }; const assert = require('assert'), database = require('./database.js'), BoxError = require('./boxerror.js'), uuid = require('uuid'), safe = require('safetydance'), superagent = require('superagent'), jsdom = require('jsdom'), debug = require('debug')('box:applinks'); const APPLINKS_FIELDS= [ 'id', 'accessRestrictionJson', 'creationTime', 'updateTime', 'ts', 'label', 'tagsJson', 'icon', 'upstreamUri' ].join(','); function postProcess(result) { assert.strictEqual(typeof result, 'object'); assert(result.tagsJson === null || typeof result.tagsJson === 'string'); result.tags = safe.JSON.parse(result.tagsJson) || []; delete result.tagsJson; assert(result.accessRestrictionJson === null || typeof result.accessRestrictionJson === 'string'); result.accessRestriction = safe.JSON.parse(result.accessRestrictionJson); if (result.accessRestriction && !result.accessRestriction.users) result.accessRestriction.users = []; delete result.accessRestrictionJson; result.icon = result.icon ? result.icon : null; } async function list() { debug('list'); const results = await database.query(`SELECT ${APPLINKS_FIELDS} FROM applinks ORDER BY upstreamUri`); results.forEach(postProcess); return results; } async function amendIconAndLabel(applink) { assert.strictEqual(typeof applink, 'object'); const [error, response] = await safe(superagent.get(applink.upstreamUri)); if (error || !response.text) throw new BoxError(BoxError.BAD_FIELD, 'cannot fetch applink icon'); const dom = new jsdom.JSDOM(response.text); if (!applink.icon) { if (dom.window.document.querySelector('link[rel="icon"]')) { const favicon = dom.window.document.querySelector('link[rel="icon"]').href ; const [error, response] = await safe(superagent.get(favicon)); if (error) throw new BoxError(BoxError.BAD_FIELD, 'cannot fetch applink icon'); if (response.ok && response.headers['content-type'] === 'image/png') applink.icon = response.body; else debug(`Failed to fetch icon ${response.status}`); } else { debug(`Cannot fetch icon for ${applink.upstreamUri}`); } } if (!applink.lable) { if (dom.window.document.title) applink.label = dom.window.document.title; } } async function add(applink) { assert.strictEqual(typeof applink, 'object'); assert.strictEqual(typeof applink.upstreamUri, 'string'); debug(`add: ${applink.upstreamUri}`, applink); await amendIconAndLabel(applink); const data = { id: uuid.v4(), accessRestrictionJson: applink.accessRestriction ? JSON.stringify(applink.accessRestriction) : null, label: applink.label || '', tagsJson: applink.tags ? JSON.stringify(applink.tags) : null, icon: applink.icon || null, upstreamUri: applink.upstreamUri }; const query = 'INSERT INTO applinks (id, accessRestrictionJson, label, tagsJson, icon, upstreamUri) VALUES (?, ?, ?, ?, ?, ?)'; const args = [ data.id, data.accessRestrictionJson, data.label, data.tagsJson, data.icon, data.upstreamUri ]; const [error] = await safe(database.query(query, args)); if (error) throw error; return data.id; } async function get(applinkId) { assert.strictEqual(typeof applinkId, 'string'); const result = await database.query(`SELECT ${APPLINKS_FIELDS} FROM applinks WHERE id = ?`, [ applinkId ]); if (result.length === 0) throw new BoxError(BoxError.NOT_FOUND, 'Applink not found'); postProcess(result[0]); return result[0]; } async function update(applinkId, applink) { assert.strictEqual(typeof applinkId, 'string'); assert.strictEqual(typeof applink, 'object'); assert.strictEqual(typeof applink.upstreamUri, 'string'); debug(`update: ${applinkId} ${applink.upstreamUri}`, applink); await amendIconAndLabel(applink); const query = 'UPDATE applinks SET label=?, icon=?, upstreamUri=? WHERE id = ?'; const args = [ applink.label, applink.icon, applink.upstreamUri, applinkId ]; const result = await database.query(query, args); if (result.affectedRows !== 1) throw new BoxError(BoxError.NOT_FOUND, 'Applink not found'); } async function remove(applinkId) { assert.strictEqual(typeof applinkId, 'string'); debug(`remove: ${applinkId}`); const result = await database.query(`DELETE FROM applinks WHERE id = ?`, [ applinkId ]); if (result.affectedRows !== 1) throw new BoxError(BoxError.NOT_FOUND, 'Applink not found'); } async function getIcon(applinkId) { assert.strictEqual(typeof applinkId, 'string'); const applink = await get(applinkId); return applink.icon; }