diff --git a/frontend/src/download.js b/frontend/src/download.js index 633128e0..5258dda2 100644 --- a/frontend/src/download.js +++ b/frontend/src/download.js @@ -1,20 +1,53 @@ const FileReceiver = require('./fileReceiver'); -const { notify } = require('./utils'); +const { notify, findMetric } = require('./utils'); const $ = require('jquery'); require('jquery-circle-progress'); const Raven = window.Raven; + +if (!localStorage.hasOwnProperty('totalDownloads')) { + localStorage.setItem('totalDownloads', 0); +} + $(document).ready(function() { //link back to homepage $('.send-new').attr('href', window.location.origin); - $('.send-new').click(function() { - window.analytics - .sendEvent('recipient', 'restarted', { - cd2: 'completed' - }); - }) + + if (location.pathname.toString().includes('download')) { + $('.send-new').click(function(target) { + target.preventDefault(); + window.analytics + .sendEvent('recipient', 'restarted', { + cd2: 'completed' + }) + .then(() => { + location.href = target.currentTarget.href; + }); + }) + + + $('.legal-links a, .social-links a, #dl-firefox').click(function(target) { + target.preventDefault(); + let metric = findMetric(target.currentTarget.href); + // record exited event by recipient + window.analytics + .sendEvent('recipient', 'exited', { + cd3: metric + }) + .then(() => { + location.href = target.currentTarget.href; + }); + }) + + $('#expired-send-new').click(function() { + localStorage.setItem('referrer', 'errored-download'); + }) + + } const filename = $('#dl-filename').html(); + const bytelength = $('#dl-bytelength').html(); + const timeToExpiry = $('#dl-ttl').html(); //initiate progress bar $('#dl-progress').circleProgress({ @@ -26,9 +59,41 @@ $(document).ready(function() { }); $('#download-btn').click(download); function download() { + let totalDownloads = localStorage.getItem('totalDownloads'); + localStorage.setItem('totalDownloads', Number(totalDownloads) + 1); + const fileReceiver = new FileReceiver(); + let unexpiredFiles = 0; + + for (let i = 0; i < localStorage.length; i++) { + const id = localStorage.key(i); + if (id != 'totalUploads' && + id != 'totalDownloads' && + id != 'referrer') { + unexpiredFiles += 1; + } + } + + let totalUploads = 0; + if (localStorage.hasOwnProperty('totalUploads')) { + totalUploads = localStorage.getItem('totalUploads'); + } fileReceiver.on('progress', progress => { + + window.onunload = function() { + localStorage.setItem('referrer', 'cancelled-download'); + // record download-stopped (cancelled by tab close or reload) + window.analytics + .sendEvent('recipient', 'download-stopped', { + cm1: bytelength, + cm5: totalUploads, + cm6: unexpiredFiles, + cm7: localStorage.getItem('totalDownloads'), + cd2: 'cancelled' + }) + } + $('#download-page-one').attr('hidden', true); $('#download-progress').removeAttr('hidden'); const percent = progress[0] / progress[1]; @@ -57,15 +122,18 @@ $(document).ready(function() { notify(translated[0]); $('.title').html(translated[1]); }); + window.onunload = null; } }); + let downloadEnd; fileReceiver.on('decrypting', isStillDecrypting => { // The file is being decrypted if (isStillDecrypting) { console.log('Decrypting'); } else { console.log('Done decrypting'); + downloadEnd = new Date().getTime(); } }); @@ -78,9 +146,32 @@ $(document).ready(function() { } }); + const startTime = new Date().getTime(); + + // record download-started by recipient + window.analytics + .sendEvent('recipient', 'download-started', { + cm1: bytelength, + cm4: timeToExpiry, + cm5: totalUploads, + cm6: unexpiredFiles, + cm7: localStorage.getItem('totalDownloads') + }); + fileReceiver .download() - .catch(() => { + .catch(err => { + // record download-stopped (errored) by recipient + window.analytics + .sendEvent('recipient', 'download-stopped', { + cm1: bytelength, + cm5: totalUploads, + cm6: unexpiredFiles, + cm7: localStorage.getItem('totalDownloads'), + cd2: 'errored', + cd6: err + }); + document.l10n.formatValue('expiredPageHeader') .then(translated => { $('.title').text(translated); @@ -91,6 +182,24 @@ $(document).ready(function() { return; }) .then(([decrypted, fname]) => { + const endTime = new Date().getTime(); + const totalTime = endTime - startTime; + const downloadTime = endTime - downloadEnd; + const downloadSpeed = bytelength / (downloadTime / 1000); + + localStorage.setItem('referrer', 'completed-download'); + // record download-stopped (completed) by recipient + window.analytics + .sendEvent('recipient', 'download-stopped', { + cm1: bytelength, + cm2: totalTime, + cm3: downloadSpeed, + cm5: totalUploads, + cm6: unexpiredFiles, + cm7: localStorage.getItem('totalDownloads'), + cd2: 'completed' + }); + const dataView = new DataView(decrypted); const blob = new Blob([dataView]); const downloadUrl = URL.createObjectURL(blob); diff --git a/frontend/src/upload.js b/frontend/src/upload.js index bcdb4271..ed7f9297 100644 --- a/frontend/src/upload.js +++ b/frontend/src/upload.js @@ -1,98 +1,142 @@ const FileSender = require('./fileSender'); -const { notify, gcmCompliant } = require('./utils'); +const { notify, gcmCompliant, findMetric } = require('./utils'); const $ = require('jquery'); require('jquery-circle-progress'); const Raven = window.Raven; +if (!localStorage.hasOwnProperty('totalUploads')) { + localStorage.setItem('totalUploads', 0); +} + +if (localStorage.hasOwnProperty('referrer')) { + window.referrer = localStorage.getItem('referrer'); + localStorage.removeItem('referrer'); +} else { + window.referrer = 'external'; +} + $(document).ready(function() { - gcmCompliant() - .catch(err => { - $('#page-one').attr('hidden', true); - $('#unsupported-browser').removeAttr('hidden'); + if (!location.pathname.toString().includes('download')) { + gcmCompliant() + .catch(err => { + $('#page-one').attr('hidden', true); + $('#unsupported-browser').removeAttr('hidden'); + // record unsupported event + window.analytics + .sendEvent('sender', 'unsupported', { + cd6: err + }); + }); + + $('#file-upload').change(onUpload); + + $('.legal-links a, .social-links a, #dl-firefox').click(function(target) { + target.preventDefault(); + let metric = findMetric(target.currentTarget.href); + // record exited event by recipient window.analytics - .sendEvent('sender', 'unsupported', { - cd7: err + .sendEvent('sender', 'exited', { + cd3: metric + }) + .then(() => { + location.href = target.currentTarget.href; }); - }); + }) - $('#file-upload').change(onUpload); + $('#send-new-completed').click(function(target) { + target.preventDefault(); + // record restarted event + window.analytics + .sendEvent('sender', 'restarted', { + cd2: 'completed' + }) + .then(() => { + localStorage.setItem('referrer', 'completed-upload'); + location.href = target.currentTarget.href; + }); + }) - $('#send-new-completed').click(function() { - window.analytics - .sendEvent('sender', 'restarted', { - cd2: 'completed' - }); - }) + $('#send-new-error').click(function(target) { + target.preventDefault(); + // record restarted event + window.analytics + .sendEvent('sender', 'restarted', { + cd2: 'errored' + }) + .then(() => { + localStorage.setItem('referrer', 'errored-upload'); + location.href = target.currentTarget.href; + }); + }) - $('#send-new-error').click(function() { - window.analytics - .sendEvent('sender', 'restarted', { - cd2: 'errored' - }); - }) + $('body').on('dragover', allowDrop).on('drop', onUpload); + // reset copy button + const $copyBtn = $('#copy-btn'); + $copyBtn.attr('disabled', false); + $('#link').attr('disabled', false); + $copyBtn.attr('data-l10n-id', 'copyUrlFormButton'); - $('body').on('dragover', allowDrop).on('drop', onUpload); - // reset copy button - const $copyBtn = $('#copy-btn'); - $copyBtn.attr('disabled', false); - $('#link').attr('disabled', false); - $copyBtn.attr('data-l10n-id', 'copyUrlFormButton'); - - if (localStorage.length === 0) { - toggleHeader(); - } else { - for (let i = 0; i < localStorage.length; i++) { - const id = localStorage.key(i); - //check if file exists before adding to list - checkExistence(id, true); + if (localStorage.length === 0) { + toggleHeader(); + } else { + for (let i = 0; i < localStorage.length; i++) { + const id = localStorage.key(i); + //check if file exists before adding to list + checkExistence(id, true); + } } + + // copy link to clipboard + $copyBtn.click(() => { + // record copied event from success screen + window.analytics + .sendEvent('sender', 'copied', { + cd4: 'success-screen' + }); + const aux = document.createElement('input'); + aux.setAttribute('value', $('#link').attr('value')); + document.body.appendChild(aux); + aux.select(); + document.execCommand('copy'); + document.body.removeChild(aux); + //disable button for 3s + $copyBtn.attr('disabled', true); + $('#link').attr('disabled', true); + $copyBtn.html(''); + window.setTimeout(() => { + $copyBtn.attr('disabled', false); + $('#link').attr('disabled', false); + $copyBtn.attr('data-l10n-id', 'copyUrlFormButton'); + }, 3000); + }); + + $('.upload-window').on('dragover', () => { + $('.upload-window').addClass('ondrag'); + }); + $('.upload-window').on('dragleave', () => { + $('.upload-window').removeClass('ondrag'); + }); + //initiate progress bar + $('#ul-progress').circleProgress({ + value: 0.0, + startAngle: -Math.PI / 2, + fill: '#3B9DFF', + size: 158, + animation: { duration: 300 } + }); } - // copy link to clipboard - $copyBtn.click(() => { - window.analytics - .sendEvent('sender', 'copied', { - cd6: 'success-screen' - }); - const aux = document.createElement('input'); - aux.setAttribute('value', $('#link').attr('value')); - document.body.appendChild(aux); - aux.select(); - document.execCommand('copy'); - document.body.removeChild(aux); - //disable button for 3s - $copyBtn.attr('disabled', true); - $('#link').attr('disabled', true); - $copyBtn.html(''); - window.setTimeout(() => { - $copyBtn.attr('disabled', false); - $('#link').attr('disabled', false); - $copyBtn.attr('data-l10n-id', 'copyUrlFormButton'); - }, 3000); - }); - - $('.upload-window').on('dragover', () => { - $('.upload-window').addClass('ondrag'); - }); - $('.upload-window').on('dragleave', () => { - $('.upload-window').removeClass('ondrag'); - }); - //initiate progress bar - $('#ul-progress').circleProgress({ - value: 0.0, - startAngle: -Math.PI / 2, - fill: '#3B9DFF', - size: 158, - animation: { duration: 300 } - }); - //link back to homepage $('.send-new').attr('href', window.location); // on file upload by browse or drag & drop function onUpload(event) { event.preventDefault(); + + let totalUploads = localStorage.getItem('totalUploads'); + localStorage.setItem('totalUploads', Number(totalUploads) + 1); + let file = ''; if (event.type === 'drop') { if (event.originalEvent.dataTransfer.files.length > 1 || event.originalEvent.dataTransfer.files[0].size === 0){ @@ -123,6 +167,18 @@ $(document).ready(function() { .then(str => { notify(str); }); + localStorage.setItem('referrer', 'cancelled-upload'); + + // record upload-stopped (cancelled) by sender + window.analytics + .sendEvent('sender', 'upload-stopped', { + cm1: file.size, + cm5: localStorage.getItem('totalUploads'), + cm6: unexpiredFiles, + cm7: totalDownloads, + cd1: event.type === 'drop' ? 'drop' : 'click', + cd2: 'cancelled' + }); }); fileSender.on('progress', progress => { @@ -165,27 +221,82 @@ $(document).ready(function() { } }); + let uploadStart; fileSender.on('encrypting', isStillEncrypting => { // The file is being encrypted if (isStillEncrypting) { console.log('Encrypting'); } else { console.log('Finished encrypting'); + uploadStart = new Date().getTime(); } }); - let t = ''; + + let t; + const startTime = new Date().getTime(); + + let unexpiredFiles = 0; + + for (let i = 0; i < localStorage.length; i++) { + const id = localStorage.key(i); + if (id != 'totalUploads' && + id != 'totalDownloads' && + id != 'referrer') { + unexpiredFiles += 1; + } + } + + let totalDownloads = 0; + if (localStorage.hasOwnProperty('totalDownloads')) { + totalDownloads = localStorage.getItem('totalDownloads'); + } + + // record upload-started event by sender + window.analytics + .sendEvent('sender', 'upload-started', { + cm1: file.size, + cm5: localStorage.getItem('totalUploads'), + cm6: unexpiredFiles, + cm7: totalDownloads, + cd1: event.type === 'drop' ? 'drop' : 'click', + cd5: window.referrer + }); + fileSender .upload() .then(info => { + const endTime = new Date().getTime(); + const totalTime = endTime - startTime; + const uploadTime = endTime - uploadStart; + const uploadSpeed = file.size / (uploadTime / 1000); + + // record upload-stopped (completed) by sender + window.analytics + .sendEvent('sender', 'upload-stopped', { + cm1: file.size, + cm2: totalTime, + cm3: uploadSpeed, + cm5: localStorage.getItem('totalUploads'), + cm6: unexpiredFiles, + cm7: totalDownloads, + cd1: event.type === 'drop' ? 'drop' : 'click', + cd2: 'completed' + }); + const fileData = { name: file.name, + size: file.size, fileId: info.fileId, url: info.url, secretKey: info.secretKey, deleteToken: info.deleteToken, creationDate: new Date(), - expiry: expiration + expiry: expiration, + totalTime: totalTime, + typeOfUpload: event.type === 'drop' ? 'drop' : 'click', + uploadSpeed: uploadSpeed }; + localStorage.setItem(info.fileId, JSON.stringify(fileData)); $('#upload-filename').attr('data-l10n-id', 'uploadSuccessConfirmHeader'); t = window.setTimeout(() => { @@ -208,6 +319,18 @@ $(document).ready(function() { $('#upload-progress').attr('hidden', true); $('#upload-error').removeAttr('hidden'); window.clearTimeout(t); + + // record upload-stopped (errored) by sender + window.analytics + .sendEvent('sender', 'upload-stopped', { + cm1: file.size, + cm5: localStorage.getItem('totalUploads'), + cm6: unexpiredFiles, + cm7: totalDownloads, + cd1: event.type === 'drop' ? 'drop' : 'click', + cd2: 'errored', + cd6: err + }); }); } @@ -224,7 +347,11 @@ $(document).ready(function() { populateFileList(localStorage.getItem(id)); } } else if (xhr.status === 404) { - localStorage.removeItem(id); + if (id != 'totalUploads' && + id != 'totalDownloads' && + id != 'referrer') { + localStorage.removeItem(id); + } } } }; @@ -282,9 +409,10 @@ $(document).ready(function() { //copy link to clipboard when icon clicked $copyIcon.click(function() { + // record copied event from upload list window.analytics .sendEvent('sender', 'copied', { - cd6: 'upload-list' + cd4: 'upload-list' }); const aux = document.createElement('input'); aux.setAttribute('value', url); @@ -362,7 +490,6 @@ $(document).ready(function() { popupNvmSpan ]); - // add data cells to table row row.appendChild(name); $(link).append($copyIcon); @@ -374,18 +501,67 @@ $(document).ready(function() { row.appendChild(del); $('tbody').append(row); //add row to table + let unexpiredFiles = 0; + + for (let i = 0; i < localStorage.length; i++) { + const id = localStorage.key(i); + if (id != 'totalUploads' && + id != 'totalDownloads' && + id != 'referrer') { + unexpiredFiles += 1; + } + } + + let totalDownloads = 0; + if (localStorage.hasOwnProperty('totalDownloads')) { + totalDownloads = localStorage.getItem('totalDownloads'); + } + // delete file $popupText.find('.del-file').click(e => { FileSender.delete(file.fileId, file.deleteToken).then(() => { $(e.target).parents('tr').remove(); - localStorage.removeItem(file.fileId); + const timeToExpiry = 86400000 - (new Date().getTime() - file.creationDate.getTime()); + // record upload-deleted from file list + window.analytics + .sendEvent('sender', 'upload-deleted', { + cm1: file.size, + cm2: file.totalTime, + cm3: file.uploadSpeed, + cm4: timeToExpiry, + cm5: localStorage.getItem('totalUploads'), + cm6: unexpiredFiles, + cm7: totalDownloads, + cd1: file.typeOfUpload, + cd4: 'upload-list' + }) + .then(() => { + localStorage.removeItem(file.fileId); + }) toggleHeader(); }); }); + document.getElementById('delete-file').onclick = () => { FileSender.delete(file.fileId, file.deleteToken).then(() => { - localStorage.removeItem(file.fileId); - location.reload(); + const timeToExpiry = 86400000 - (new Date().getTime() - file.creationDate.getTime()); + // record upload-deleted from success screen + window.analytics + .sendEvent('sender', 'upload-deleted', { + cm1: file.size, + cm2: file.totalTime, + cm3: file.uploadSpeed, + cm4: timeToExpiry, + cm5: localStorage.getItem('totalUploads'), + cm6: unexpiredFiles, + cm7: totalDownloads, + cd1: file.typeOfUpload, + cd4: 'success-screen' + }) + .then(() => { + localStorage.removeItem(file.fileId); + location.reload(); + }) }); }; // show popup diff --git a/frontend/src/utils.js b/frontend/src/utils.js index aff37488..473ac045 100644 --- a/frontend/src/utils.js +++ b/frontend/src/utils.js @@ -69,9 +69,35 @@ function gcmCompliant() { } } +function findMetric(href) { + switch(href) { + case 'https://www.mozilla.org/': + return 'mozilla'; + case 'https://www.mozilla.org/about/legal': + return 'legal'; + case 'https://testpilot.firefox.com/about': + return 'about'; + case 'https://testpilot.firefox.com/privacy': + return 'privacy'; + case 'https://testpilot.firefox.com/terms': + return 'terms'; + case 'https://www.mozilla.org/en-US/privacy/websites/#cookies': + return 'cookies'; + case 'https://github.com/mozilla/send': + return 'github'; + case 'https://twitter.com/FxTestPilot': + return 'twitter'; + case 'https://www.mozilla.org/firefox/new/?scene=2': + return 'download-firefox'; + default: + return 'other'; + } +} + module.exports = { arrayToHex, hexToArray, notify, - gcmCompliant + gcmCompliant, + findMetric }; diff --git a/package-lock.json b/package-lock.json index c662769a..c9675f42 100644 --- a/package-lock.json +++ b/package-lock.json @@ -208,9 +208,21 @@ "dev": true }, "aws-sdk": { - "version": "2.77.0", - "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.77.0.tgz", - "integrity": "sha1-gJCQu4dNj0//ysUxZilYdjjnhlw=" + "version": "2.87.0", + "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.87.0.tgz", + "integrity": "sha1-lW+Ey48yah0j/ioJ1JVlbhobato=", + "dependencies": { + "buffer": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz", + "integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=" + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + } + } }, "babel-code-frame": { "version": "6.22.0", @@ -384,7 +396,8 @@ "buffer": { "version": "5.0.6", "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.0.6.tgz", - "integrity": "sha1-LqZp9+7Atu2gWwj4tf9mGyhXNYg=" + "integrity": "sha1-LqZp9+7Atu2gWwj4tf9mGyhXNYg=", + "dev": true }, "buffer-xor": { "version": "1.0.3", @@ -526,12 +539,24 @@ "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", "dev": true }, + "color-convert": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.0.tgz", + "integrity": "sha1-Gsz5fdc5uYO/mU1W/sj5WFNkG3o=", + "dev": true + }, "color-diff": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/color-diff/-/color-diff-0.1.7.tgz", "integrity": "sha1-bbeM2UgqjkWdQIIer0tQMoPcuOI=", "dev": true }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, "colorguard": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/colorguard/-/colorguard-1.2.0.tgz", @@ -1067,11 +1092,17 @@ "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" }, "eslint": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-4.1.0.tgz", - "integrity": "sha1-u7VaKCIO4Itp2pVU1FprLr/X2RM=", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-4.2.0.tgz", + "integrity": "sha1-orMYQRGxmOAunH88ymJaXgHFaz0=", "dev": true, "dependencies": { + "ajv": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.2.2.tgz", + "integrity": "sha1-R8aNaehvXZUxA7AHSpQw3GPaXjk=", + "dev": true + }, "concat-stream": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.0.tgz", @@ -1103,9 +1134,9 @@ "dev": true }, "readable-stream": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.2.tgz", - "integrity": "sha1-WgTfBeT1f+Pw3Gj90R3FyXx+b00=", + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz", + "integrity": "sha512-m+qzzcn7KUxEmd1gMbchF+Y2eIUbieUaxkWtptyHywrX0rE8QEYqPC07Vuy4Wm32/xE16NcdBctb8S0Xe/5IeQ==", "dev": true }, "string_decoder": { @@ -1123,9 +1154,9 @@ "dev": true }, "eslint-plugin-node": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-node/-/eslint-plugin-node-5.0.0.tgz", - "integrity": "sha512-9xERRx9V/8ciUHlTDlz9S4JiTL6Dc5oO+jKTy2mvQpxjhycpYZXzTT1t90IXjf+nAYw6/8sDnZfkeixJHxromA==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-node/-/eslint-plugin-node-5.1.1.tgz", + "integrity": "sha512-3xdoEbPyyQNyGhhqttjgSO3cU/non8QDBJF8ttGaHM2h8CaY5zFIngtqW6ZbLEIvhpoFPDVwiQg61b8zanx5zQ==", "dev": true }, "eslint-plugin-security": { @@ -1198,8 +1229,7 @@ "events": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", - "integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ=", - "dev": true + "integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ=" }, "evp_bytestokey": { "version": "1.0.0", @@ -1265,6 +1295,12 @@ "integrity": "sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE=", "dev": true }, + "fast-deep-equal": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.0.0.tgz", + "integrity": "sha1-liVqO8l1WV6zbYLpkp0GDYk0Of8=", + "dev": true + }, "fast-levenshtein": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", @@ -2179,18 +2215,6 @@ "integrity": "sha1-szmUr0V6gRVwDUEPMXczy+egkEs=", "dev": true }, - "generate-function": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.0.0.tgz", - "integrity": "sha1-aFj+fAlpt9TpCTM3ZHrHn2DfvnQ=", - "dev": true - }, - "generate-object-property": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/generate-object-property/-/generate-object-property-1.2.0.tgz", - "integrity": "sha1-nA4cQDCM6AT0eDYYuTf6iPmdUNA=", - "dev": true - }, "get-stdin": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-5.0.1.tgz", @@ -2349,12 +2373,6 @@ "resolved": "https://registry.npmjs.org/hsts/-/hsts-2.0.0.tgz", "integrity": "sha1-pSI0xgcN7PIUsra3C7FE0H5Hdsc=" }, - "html-tags": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/html-tags/-/html-tags-1.2.0.tgz", - "integrity": "sha1-x43mW1Zjqll5id0rerSSANfk25g=", - "dev": true - }, "htmlescape": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/htmlescape/-/htmlescape-1.1.1.tgz", @@ -2561,12 +2579,6 @@ "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", "dev": true }, - "is-my-json-valid": { - "version": "2.16.0", - "resolved": "https://registry.npmjs.org/is-my-json-valid/-/is-my-json-valid-2.16.0.tgz", - "integrity": "sha1-8Hndm/2uZe4gOKrorLyGqxCeNpM=", - "dev": true - }, "is-number": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-2.1.0.tgz", @@ -2615,12 +2627,6 @@ "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=", "dev": true }, - "is-property": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz", - "integrity": "sha1-V/4cTkhHTt1lsJkR8msc1Ald2oQ=", - "dev": true - }, "is-regex": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz", @@ -2725,6 +2731,12 @@ "integrity": "sha1-KqEH8UKvQSHRRWWdRPUIMJYeaZo=", "dev": true }, + "json-schema-traverse": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", + "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=", + "dev": true + }, "json-stable-stringify": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-0.0.1.tgz", @@ -2798,12 +2810,6 @@ "integrity": "sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA=", "dev": true }, - "jsonpointer": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-4.0.1.tgz", - "integrity": "sha1-T9kss04OnbPInIYi7PUfm5eMbLk=", - "dev": true - }, "JSONStream": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.1.tgz", @@ -3551,9 +3557,9 @@ "dev": true }, "prettier": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.4.4.tgz", - "integrity": "sha512-GuuPazIvjW1DG26yLQgO+nagmRF/h9M4RaCtZWqu/eFW7csdZkQEwPJUeXX10d+LzmCnR9DuIZndqIOn3p2YoA==", + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.5.3.tgz", + "integrity": "sha1-WdrcaDNF7GuI+IuU7Urn4do5S/4=", "dev": true }, "process": { @@ -3698,9 +3704,9 @@ } }, "raven-js": { - "version": "3.16.0", - "resolved": "https://registry.npmjs.org/raven-js/-/raven-js-3.16.0.tgz", - "integrity": "sha1-p5naT90ExjlD9n3rk9qg7P4QHqs=" + "version": "3.17.0", + "resolved": "https://registry.npmjs.org/raven-js/-/raven-js-3.17.0.tgz", + "integrity": "sha1-d5RXrHkQUSw8LMm7bQqe61mpaew=" }, "raw-body": { "version": "2.2.0", @@ -4063,9 +4069,9 @@ "dev": true }, "sinon": { - "version": "2.3.5", - "resolved": "https://registry.npmjs.org/sinon/-/sinon-2.3.5.tgz", - "integrity": "sha1-mi/A/41SbacW8wlTqixl1RiRf2w=", + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/sinon/-/sinon-2.3.8.tgz", + "integrity": "sha1-Md4G/tj7o6Zx5XbdltClhjeW8lw=", "dev": true, "dependencies": { "path-to-regexp": { @@ -4348,17 +4354,29 @@ } }, "stylelint": { - "version": "7.11.1", - "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-7.11.1.tgz", - "integrity": "sha1-yBbGWLr32eXRZ9gic/6tN8l65J0=", + "version": "7.13.0", + "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-7.13.0.tgz", + "integrity": "sha1-ER+Xttpy53XICADWu29fhpmXeF0=", "dev": true, "dependencies": { + "ansi-styles": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.1.0.tgz", + "integrity": "sha1-CcIC1ckX7CMYjKpcnLkXnNlUd1A=", + "dev": true + }, "balanced-match": { "version": "0.4.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.tgz", "integrity": "sha1-yz8+PHMtwPAe5wtAPzAuYddwmDg=", "dev": true }, + "chalk": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.0.1.tgz", + "integrity": "sha512-Mp+FXEI+FrwY/XYV45b2YD3E8i3HwnEAoFcM0qlZzq/RZ9RwWitt2Y/c7cqRAz70U7hfekqx6qNYthuKFO6K0g==", + "dev": true + }, "glob": { "version": "7.1.2", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", @@ -4371,11 +4389,29 @@ "integrity": "sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=", "dev": true }, + "has-flag": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", + "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=", + "dev": true + }, + "html-tags": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/html-tags/-/html-tags-2.0.0.tgz", + "integrity": "sha1-ELMKOGCF9Dzt41PMj6fLDe7qZos=", + "dev": true + }, "resolve-from": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=", "dev": true + }, + "supports-color": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.2.0.tgz", + "integrity": "sha512-Ts0Mu/A1S1aZxEJNG88I4Oc9rcZSBFNac5e27yh4j2mqbhZSSzR1Ah79EYwSn9Zuh7lrlGD2cVGzw1RKGzyLSg==", + "dev": true } } }, @@ -4467,9 +4503,9 @@ "dev": true }, "testpilot-ga": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/testpilot-ga/-/testpilot-ga-0.2.1.tgz", - "integrity": "sha512-vuV8ukXMVy4g2ZEuNHo9y92MXoZrjfPdJqCrFUR1CXk45oJ25Tm2SjUStH3ZeNZRv4O+8Hs0AzsXCaH4/J9mUg==" + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/testpilot-ga/-/testpilot-ga-0.3.0.tgz", + "integrity": "sha512-z4PJbw3KK0R0iflA+u/3BhWZrtsLHLu+0rMviMd8H1wp8qPV0rggNBjsKckBJCcXq4uEjXETGZzApHH7Tovpzw==" }, "text-encoding": { "version": "0.6.4", diff --git a/package.json b/package.json index bd580709..503a815d 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "version": "0.1.2", "author": "Mozilla (https://mozilla.org)", "dependencies": { - "aws-sdk": "^2.62.0", + "aws-sdk": "^2.87.0", "body-parser": "^1.17.2", "bytes": "^2.5.0", "connect-busboy": "0.0.2", @@ -18,26 +18,26 @@ "l20n": "^5.0.0", "mozlog": "^2.1.1", "raven": "^2.1.0", - "raven-js": "^3.16.0", + "raven-js": "^3.17.0", "redis": "^2.7.1", "selenium-webdriver": "^3.4.0", "supertest": "^3.0.0", - "testpilot-ga": "^0.2.1", + "testpilot-ga": "^0.3.0", "uglify-es": "3.0.19" }, "devDependencies": { "browserify": "^14.4.0", - "eslint": "^4.0.0", + "eslint": "^4.2.0", "eslint-plugin-mocha": "^4.11.0", - "eslint-plugin-node": "^5.0.0", + "eslint-plugin-node": "^5.1.1", "eslint-plugin-security": "^1.4.0", "git-rev-sync": "^1.9.1", "mocha": "^3.4.2", "npm-run-all": "^4.0.2", - "prettier": "^1.4.4", + "prettier": "^1.5.3", "proxyquire": "^1.8.0", - "sinon": "^2.3.5", - "stylelint": "^7.11.0", + "sinon": "^2.3.8", + "stylelint": "^7.13.0", "stylelint-config-standard": "^16.0.0", "watchify": "^3.9.0" }, diff --git a/server/server.js b/server/server.js index 24e1d418..59a8d65d 100644 --- a/server/server.js +++ b/server/server.js @@ -107,12 +107,18 @@ app.get('/download/:id', (req, res) => { storage .length(id) .then(contentLength => { - res.render('download', { - filename: decodeURIComponent(filename), - filesize: bytes(contentLength), - trackerId: conf.analytics_id, - dsn: conf.sentry_id - }); + storage + .ttl(id) + .then(timeToExpiry => { + res.render('download', { + filename: decodeURIComponent(filename), + filesize: bytes(contentLength), + sizeInBytes: contentLength, + timeToExpiry: timeToExpiry, + trackerId: conf.analytics_id, + dsn: conf.sentry_id + }); + }) }) .catch(() => { res.render('download'); diff --git a/server/storage.js b/server/storage.js index 68375e47..40e4a2ed 100644 --- a/server/storage.js +++ b/server/storage.js @@ -23,6 +23,7 @@ if (conf.s3_bucket) { module.exports = { filename: filename, exists: exists, + ttl: ttl, length: awsLength, get: awsGet, set: awsSet, @@ -39,6 +40,7 @@ if (conf.s3_bucket) { module.exports = { filename: filename, exists: exists, + ttl: ttl, length: localLength, get: localGet, set: localSet, @@ -73,6 +75,18 @@ function metadata(id) { }); } +function ttl(id) { + return new Promise((resolve, reject) => { + redis_client.ttl(id, (err, reply) => { + if (!err) { + resolve(reply * 1000); + } else { + reject(err); + } + }) + }) +} + function filename(id) { return new Promise((resolve, reject) => { redis_client.hget(id, 'filename', (err, reply) => { diff --git a/views/download.handlebars b/views/download.handlebars index 94cfeef8..4efea3ae 100644 --- a/views/download.handlebars +++ b/views/download.handlebars @@ -7,6 +7,8 @@ data-l10n-args='{"filename": "{{filename}}"}'> + {{sizeInBytes}} + {{timeToExpiry}}
@@ -41,6 +43,6 @@ - + {{/if}} diff --git a/views/layouts/main.handlebars b/views/layouts/main.handlebars index 285ad3c7..9e1ccd14 100644 --- a/views/layouts/main.handlebars +++ b/views/layouts/main.handlebars @@ -22,8 +22,8 @@