Merge branch 'time' into vnext

This commit is contained in:
Emily 2018-08-09 15:58:23 -07:00
commit 37c2926252
23 changed files with 229 additions and 71 deletions

View File

@ -136,7 +136,14 @@ function listenForResponse(ws, canceller) {
}); });
} }
async function upload(stream, metadata, verifierB64, onprogress, canceller) { async function upload(
stream,
metadata,
verifierB64,
timeLimit,
onprogress,
canceller
) {
const host = window.location.hostname; const host = window.location.hostname;
const port = window.location.port; const port = window.location.port;
const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:'; const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
@ -151,7 +158,8 @@ async function upload(stream, metadata, verifierB64, onprogress, canceller) {
const metadataHeader = arrayToB64(new Uint8Array(metadata)); const metadataHeader = arrayToB64(new Uint8Array(metadata));
const fileMeta = { const fileMeta = {
fileMetadata: metadataHeader, fileMetadata: metadataHeader,
authorization: `send-v1 ${verifierB64}` authorization: `send-v1 ${verifierB64}`,
timeLimit
}; };
const responsePromise = listenForResponse(ws, canceller); const responsePromise = listenForResponse(ws, canceller);
@ -188,7 +196,13 @@ async function upload(stream, metadata, verifierB64, onprogress, canceller) {
} }
} }
export function uploadWs(encrypted, metadata, verifierB64, onprogress) { export function uploadWs(
encrypted,
metadata,
verifierB64,
onprogress,
timeLimit
) {
const canceller = { cancelled: false }; const canceller = { cancelled: false };
return { return {
@ -196,7 +210,15 @@ export function uploadWs(encrypted, metadata, verifierB64, onprogress) {
canceller.error = new Error(0); canceller.error = new Error(0);
canceller.cancelled = true; canceller.cancelled = true;
}, },
result: upload(encrypted, metadata, verifierB64, onprogress, canceller)
result: upload(
encrypted,
metadata,
verifierB64,
timeLimit,
onprogress,
canceller
)
}; };
} }

View File

@ -293,3 +293,9 @@ a {
margin: 15px; margin: 15px;
} }
} }
@media (max-device-width: 700px), (max-width: 700px) {
.stripedBox {
margin-top: 72px;
}
}

View File

@ -1,4 +1,5 @@
/* global MAXFILESIZE */ /* global MAXFILESIZE */
/* global DEFAULT_EXPIRE_SECONDS */
import FileSender from './fileSender'; import FileSender from './fileSender';
import FileReceiver from './fileReceiver'; import FileReceiver from './fileReceiver';
import { copyToClipboard, delay, openLinksInNewTab, percent } from './utils'; import { copyToClipboard, delay, openLinksInNewTab, percent } from './utils';
@ -110,7 +111,9 @@ export default function(state, emitter) {
emitter.on('upload', async ({ type, dlCount, password }) => { emitter.on('upload', async ({ type, dlCount, password }) => {
if (!state.archive) return; if (!state.archive) return;
const size = state.archive.size; const size = state.archive.size;
const sender = new FileSender(state.archive); if (!state.timeLimit) state.timeLimit = DEFAULT_EXPIRE_SECONDS;
const sender = new FileSender(state.archive, state.timeLimit);
sender.on('progress', updateProgress); sender.on('progress', updateProgress);
sender.on('encrypting', render); sender.on('encrypting', render);
sender.on('complete', render); sender.on('complete', render);
@ -157,7 +160,7 @@ export default function(state, emitter) {
} }
} finally { } finally {
openLinksInNewTab(links, false); openLinksInNewTab(links, false);
state.files = []; state.archive = null;
state.password = ''; state.password = '';
state.uploading = false; state.uploading = false;
state.transfer = null; state.transfer = null;

View File

@ -1,4 +1,4 @@
/* global EXPIRE_SECONDS */ /* global DEFAULT_EXPIRE_SECONDS */
import Nanobus from 'nanobus'; import Nanobus from 'nanobus';
import OwnedFile from './ownedFile'; import OwnedFile from './ownedFile';
import Keychain from './keychain'; import Keychain from './keychain';
@ -7,8 +7,9 @@ import { uploadWs } from './api';
import { encryptedSize } from './ece'; import { encryptedSize } from './ece';
export default class FileSender extends Nanobus { export default class FileSender extends Nanobus {
constructor(file) { constructor(file, timeLimit) {
super('FileSender'); super('FileSender');
this.timeLimit = timeLimit || DEFAULT_EXPIRE_SECONDS;
this.file = file; this.file = file;
this.keychain = new Keychain(); this.keychain = new Keychain();
this.reset(); this.reset();
@ -70,10 +71,16 @@ export default class FileSender extends Nanobus {
const metadata = await this.keychain.encryptMetadata(this.file); const metadata = await this.keychain.encryptMetadata(this.file);
const authKeyB64 = await this.keychain.authKeyB64(); const authKeyB64 = await this.keychain.authKeyB64();
this.uploadRequest = uploadWs(encStream, metadata, authKeyB64, p => { this.uploadRequest = uploadWs(
this.progress = [p, totalSize]; encStream,
this.emit('progress'); metadata,
}); authKeyB64,
p => {
this.progress = [p, totalSize];
this.emit('progress');
},
this.timeLimit
);
if (this.cancelled) { if (this.cancelled) {
throw new Error(0); throw new Error(0);
@ -97,10 +104,11 @@ export default class FileSender extends Nanobus {
time: time, time: time,
speed: this.file.size / (time / 1000), speed: this.file.size / (time / 1000),
createdAt: Date.now(), createdAt: Date.now(),
expiresAt: Date.now() + EXPIRE_SECONDS * 1000, expiresAt: Date.now() + this.timeLimit * 1000,
secretKey: secretKey, secretKey: secretKey,
nonce: this.keychain.nonce, nonce: this.keychain.nonce,
ownerToken: result.ownerToken ownerToken: result.ownerToken,
timeLimit: this.timeLimit
}); });
return ownedFile; return ownedFile;

View File

@ -19,6 +19,7 @@ export default class OwnedFile {
this.dtotal = obj.dtotal || 0; this.dtotal = obj.dtotal || 0;
this.keychain = new Keychain(obj.secretKey, obj.nonce); this.keychain = new Keychain(obj.secretKey, obj.nonce);
this._hasPassword = !!obj.hasPassword; this._hasPassword = !!obj.hasPassword;
this.timeLimit = obj.timeLimit;
} }
async setPassword(password) { async setPassword(password) {
@ -80,7 +81,8 @@ export default class OwnedFile {
ownerToken: this.ownerToken, ownerToken: this.ownerToken,
dlimit: this.dlimit, dlimit: this.dlimit,
dtotal: this.dtotal, dtotal: this.dtotal,
hasPassword: this.hasPassword hasPassword: this.hasPassword,
timeLimit: this.timeLimit
}; };
} }
} }

View File

@ -1,10 +1,10 @@
/* global EXPIRE_SECONDS */
const html = require('choo/html'); const html = require('choo/html');
const raw = require('choo/html/raw'); const raw = require('choo/html/raw');
const assets = require('../../../common/assets'); const assets = require('../../../common/assets');
const notFound = require('../notFound'); const notFound = require('../notFound');
const deletePopup = require('../../templates/popup'); const deletePopup = require('../../templates/popup');
const uploadedFileList = require('../../templates/uploadedFileList'); const uploadedFileList = require('../../templates/uploadedFileList');
const timeLimitText = require('../../templates/timeLimitText');
const { allowedCopy, delay, fadeOut } = require('../../utils'); const { allowedCopy, delay, fadeOut } = require('../../utils');
module.exports = function(state, emit) { module.exports = function(state, emit) {
@ -18,7 +18,6 @@ module.exports = function(state, emit) {
: 'passwordReminder--hidden'; : 'passwordReminder--hidden';
return html` return html`
<div class="page effect--fadeIn" id="shareWrapper"> <div class="page effect--fadeIn" id="shareWrapper">
<a href="/" class="goBackButton"> <a href="/" class="goBackButton">
<img src="${assets.get('back-arrow.svg')}"/> <img src="${assets.get('back-arrow.svg')}"/>
@ -98,13 +97,14 @@ module.exports = function(state, emit) {
}; };
function expireInfo(file, translate) { function expireInfo(file, translate) {
const hours = Math.floor(EXPIRE_SECONDS / 60 / 60); const el = html`<div class="shareTitle">
const el = html`<div class="shareTitle">${raw( ${raw(
translate('expireInfo', { translate('expireInfo', {
downloadCount: translate('downloadCount', { num: file.dlimit }), downloadCount: translate('downloadCount', { num: file.dlimit }),
timespan: translate('timespanHours', { num: hours }) timespan: timeLimitText(translate, file.timeLimit)
}) })
)}</div>`; )}
</div>`;
return el; return el;
} }

View File

@ -34,6 +34,7 @@ module.exports = function(state, emit) {
${title(state)} ${title(state)}
<label class="uploadArea" <label class="uploadArea"
ondragover=${dragover} ondragover=${dragover}
ondragleave=${dragleave}> ondragleave=${dragleave}>

View File

@ -1,12 +1,13 @@
const html = require('choo/html'); const html = require('choo/html');
const raw = require('choo/html/raw'); const raw = require('choo/html/raw');
const selectbox = require('../selectbox'); const selectbox = require('../selectbox');
const timeLimitText = require('../timeLimitText');
module.exports = function(state) { module.exports = function(state) {
const el = html`<div> ${raw( const el = html`<div> ${raw(
state.translate('frontPageExpireInfo', { state.translate('frontPageExpireInfo', {
downloadCount: '<select id=dlCount></select>', downloadCount: '<select id=dlCount></select>',
timespan: state.translate('timespanHours', { num: 24 }) //'<select id=timespan></select>' timespan: '<select id=timespan></select>'
}) })
)} )}
</div>`; </div>`;
@ -24,13 +25,18 @@ module.exports = function(state) {
dlCountSelect dlCountSelect
); );
/*
const timeSelect = el.querySelector('#timespan'); const timeSelect = el.querySelector('#timespan');
el.replaceChild( el.replaceChild(
selectbox(1, [1, 2, 3, 4, 5], num => num, () => {}), selectbox(
state.timeLimit || 86400,
[300, 3600, 86400, 604800, 1209600],
num => timeLimitText(state.translate, num),
value => {
state.timeLimit = value;
}
),
timeSelect timeSelect
); );
*/
return el; return el;
}; };

View File

@ -51,6 +51,14 @@ module.exports = function(file, state) {
function timeLeft(milliseconds, state) { function timeLeft(milliseconds, state) {
const minutes = Math.floor(milliseconds / 1000 / 60); const minutes = Math.floor(milliseconds / 1000 / 60);
const hours = Math.floor(minutes / 60); const hours = Math.floor(minutes / 60);
const days = Math.floor(hours / 24);
if (days >= 1) {
return state.translate('expiresDaysHoursMinutes', {
days,
hours: hours % 24,
minutes: minutes % 60
});
}
if (hours >= 1) { if (hours >= 1) {
return state.translate('expiresHoursMinutes', { return state.translate('expiresHoursMinutes', {
hours, hours,

View File

@ -8,6 +8,7 @@
width: 22px; width: 22px;
height: 32px; height: 32px;
overflow: hidden; overflow: hidden;
user-select: none;
} }
.fileIcon__lock { .fileIcon__lock {

View File

@ -56,7 +56,7 @@
text-decoration: underline; text-decoration: underline;
} }
@media (max-device-width: 750px), (max-width: 750px) { @media (max-device-width: 700px), (max-width: 700px) {
.signupPromo { .signupPromo {
flex-direction: row; flex-direction: row;
align-items: center; align-items: center;

View File

@ -0,0 +1,14 @@
module.exports = function(translate, seconds) {
const displayText = {
300: translate('timespanMinutes', { num: 5 }),
3600: translate('timespanHours', { num: 1 }),
86400: translate('timespanHours', { num: 24 }),
604800: translate('timespanWeeks', { num: 1 }),
1209600: translate('timespanWeeks', { num: 2 })
};
if (displayText[seconds]) {
return displayText[seconds];
}
return seconds;
};

6
package-lock.json generated
View File

@ -926,9 +926,9 @@
} }
}, },
"aws-sdk": { "aws-sdk": {
"version": "2.285.1", "version": "2.288.0",
"resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.285.1.tgz", "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.288.0.tgz",
"integrity": "sha512-lkroCYcnb7UWR/jbaW6wyjAeGROrsBFWyqUukQjICuCV4a0Mapnjsxefl2A/z+0SX3gnBN7owUb/60UjQSHpzA==", "integrity": "sha512-kV0yLAP3DPfUpDfFOCvOn4s/69XqVH7D53Vnfgf6mgKO5bp1HVFpWfTJ/OZeiwRy7hfIi0xTiSEmPXaMG0ACkg==",
"requires": { "requires": {
"buffer": "4.9.1", "buffer": "4.9.1",
"events": "1.1.1", "events": "1.1.1",

View File

@ -117,7 +117,7 @@
"webpack-unassert-loader": "^1.2.0" "webpack-unassert-loader": "^1.2.0"
}, },
"dependencies": { "dependencies": {
"aws-sdk": "^2.285.1", "aws-sdk": "^2.288.0",
"babel-polyfill": "^6.26.0", "babel-polyfill": "^6.26.0",
"choo": "^6.12.1", "choo": "^6.12.1",
"cldr-core": "^33.0.0", "cldr-core": "^33.0.0",

View File

@ -122,6 +122,8 @@ reportIPInfringement = Report IP Infringement
javascriptRequired = Firefox Send requires JavaScript javascriptRequired = Firefox Send requires JavaScript
whyJavascript = Why does Firefox Send require JavaScript? whyJavascript = Why does Firefox Send require JavaScript?
enableJavascript = Please enable JavaScript and try again. enableJavascript = Please enable JavaScript and try again.
# A short representation of a countdown timer containing the number of days, hours, and minutes remaining as digits, example "2d 11h 56m"
expiresDaysHoursMinutes = { $days }d { $hours }h { $minutes }m
# A short representation of a countdown timer containing the number of hours and minutes remaining as digits, example "13h 47m" # A short representation of a countdown timer containing the number of hours and minutes remaining as digits, example "13h 47m"
expiresHoursMinutes = { $hours }h { $minutes }m expiresHoursMinutes = { $hours }h { $minutes }m
# A short representation of a countdown timer containing the number of minutes remaining as digits, example "56m" # A short representation of a countdown timer containing the number of minutes remaining as digits, example "56m"

View File

@ -9,6 +9,31 @@ const conf = convict({
default: '', default: '',
env: 'S3_BUCKET' env: 'S3_BUCKET'
}, },
num_of_prefixes: {
format: Number,
default: 5,
env: 'NUM_OF_PREFIXES'
},
expire_prefixes: {
format: Array,
default: ['5minutes', '1hour', '1day', '1week', '2weeks'],
env: 'EXPIRE_PREFIXES'
},
expire_times_seconds: {
format: Array,
default: [300, 3600, 86400, 604800, 1209600],
env: 'EXPIRE_TIMES_SECONDS'
},
default_expire_seconds: {
format: Number,
default: 86400,
env: 'DEFAULT_EXPIRE_SECONDS'
},
max_expire_seconds: {
format: Number,
default: 1209600,
env: 'MAX_EXPIRE_SECONDS'
},
redis_host: { redis_host: {
format: String, format: String,
default: 'localhost', default: 'localhost',
@ -55,11 +80,6 @@ const conf = convict({
default: 1024 * 1024 * 1024 * 3, default: 1024 * 1024 * 1024 * 3,
env: 'MAX_FILE_SIZE' env: 'MAX_FILE_SIZE'
}, },
expire_seconds: {
format: Number,
default: 86400,
env: 'EXPIRE_SECONDS'
},
l10n_dev: { l10n_dev: {
format: Boolean, format: Boolean,
default: false, default: false,

View File

@ -13,7 +13,8 @@ module.exports = async function(req, res) {
'Content-Length': contentLength, 'Content-Length': contentLength,
'WWW-Authenticate': `send-v1 ${req.nonce}` 'WWW-Authenticate': `send-v1 ${req.nonce}`
}); });
const file_stream = storage.get(id);
const file_stream = await storage.get(id);
let cancelled = false; let cancelled = false;
req.on('close', () => { req.on('close', () => {

View File

@ -35,7 +35,7 @@ if (isIE && !isUnsupportedPage) {
window.location.replace('/unsupported/ie'); window.location.replace('/unsupported/ie');
} }
var MAXFILESIZE = ${config.max_file_size}; var MAXFILESIZE = ${config.max_file_size};
var EXPIRE_SECONDS = ${config.expire_seconds}; var DEFAULT_EXPIRE_SECONDS = ${config.default_expire_seconds};
${ga} ${ga}
${sentry} ${sentry}
`; `;

View File

@ -24,7 +24,9 @@ module.exports = async function(req, res) {
try { try {
const limiter = new Limiter(config.max_file_size); const limiter = new Limiter(config.max_file_size);
const fileStream = req.pipe(limiter); const fileStream = req.pipe(limiter);
await storage.set(newId, fileStream, meta); //this hasn't been updated to expiration time setting yet
//if you want to fallback to this code add this
await storage.set(newId, fileStream, meta, config.default_expire_seconds);
const protocol = config.env === 'production' ? 'https' : req.protocol; const protocol = config.env === 'production' ? 'https' : req.protocol;
const url = `${protocol}://${req.get('host')}/download/${newId}/`; const url = `${protocol}://${req.get('host')}/download/${newId}/`;
res.set('WWW-Authenticate', `send-v1 ${meta.nonce}`); res.set('WWW-Authenticate', `send-v1 ${meta.nonce}`);

View File

@ -23,10 +23,16 @@ module.exports = async function(ws, req) {
const owner = crypto.randomBytes(10).toString('hex'); const owner = crypto.randomBytes(10).toString('hex');
const fileInfo = JSON.parse(message); const fileInfo = JSON.parse(message);
const timeLimit = fileInfo.timeLimit;
const metadata = fileInfo.fileMetadata; const metadata = fileInfo.fileMetadata;
const auth = fileInfo.authorization; const auth = fileInfo.authorization;
if (!metadata || !auth) { if (
!metadata ||
!auth ||
timeLimit <= 0 ||
timeLimit > config.max_expire_seconds
) {
ws.send( ws.send(
JSON.stringify({ JSON.stringify({
error: 400 error: 400
@ -50,7 +56,7 @@ module.exports = async function(ws, req) {
fileStream = wsStream(ws, { binary: true }) fileStream = wsStream(ws, { binary: true })
.pipe(limiter) .pipe(limiter)
.pipe(parser); .pipe(parser);
await storage.set(newId, fileStream, meta); await storage.set(newId, fileStream, meta, timeLimit);
if (ws.readyState === 1) { if (ws.readyState === 1) {
// if the socket is closed by a cancelled upload the stream // if the socket is closed by a cancelled upload the stream

View File

@ -7,8 +7,9 @@ class DB {
constructor(config) { constructor(config) {
const Storage = config.s3_bucket ? require('./s3') : require('./fs'); const Storage = config.s3_bucket ? require('./s3') : require('./fs');
this.log = mozlog('send.storage'); this.log = mozlog('send.storage');
this.expireSeconds = config.expire_seconds;
this.storage = new Storage(config, this.log); this.storage = new Storage(config, this.log);
this.redis = createRedisClient(config); this.redis = createRedisClient(config);
this.redis.on('error', err => { this.redis.on('error', err => {
this.log.error('Redis:', err); this.log.error('Redis:', err);
@ -20,27 +21,45 @@ class DB {
return Math.ceil(result) * 1000; return Math.ceil(result) * 1000;
} }
length(id) { async getPrefixedId(id) {
return this.storage.length(id); const prefix = await this.redis.hgetAsync(id, 'prefix');
return `${prefix}-${id}`;
} }
get(id) { async length(id) {
return this.storage.getStream(id); const filePath = await this.getPrefixedId(id);
return this.storage.length(filePath);
} }
async set(id, file, meta) { async get(id) {
await this.storage.set(id, file); const filePath = await this.getPrefixedId(id);
return this.storage.getStream(filePath);
}
async set(id, file, meta, expireSeconds = config.default_expire_seconds) {
const expireTimes = config.expire_times_seconds;
let i;
for (i = 0; i < expireTimes.length - 1; i++) {
if (expireSeconds <= expireTimes[i]) {
break;
}
}
const prefix = config.expire_prefixes[i];
const filePath = `${prefix}-${id}`;
await this.storage.set(filePath, file);
this.redis.hset(id, 'prefix', prefix);
this.redis.hmset(id, meta); this.redis.hmset(id, meta);
this.redis.expire(id, this.expireSeconds); this.redis.expire(id, expireSeconds);
} }
setField(id, key, value) { setField(id, key, value) {
this.redis.hset(id, key, value); this.redis.hset(id, key, value);
} }
del(id) { async del(id) {
const filePath = await this.getPrefixedId(id);
this.storage.del(filePath);
this.redis.del(id); this.redis.del(id);
return this.storage.del(id);
} }
async ping() { async ping() {

View File

@ -20,14 +20,18 @@ class MockStorage {
} }
} }
const expire_seconds = 10; const config = {
s3_bucket: 'foo',
default_expire_seconds: 20,
num_of_prefixes: 3,
expire_prefixes: ['ten', 'twenty', 'thirty'],
expire_times_seconds: [10, 20, 30],
env: 'development',
redis_host: 'localhost'
};
const storage = proxyquire('../../server/storage', { const storage = proxyquire('../../server/storage', {
'../config': { '../config': config,
expire_seconds,
s3_bucket: 'foo',
env: 'development',
redis_host: 'localhost'
},
'../log': () => {}, '../log': () => {},
'./s3': MockStorage './s3': MockStorage
}); });
@ -35,10 +39,11 @@ const storage = proxyquire('../../server/storage', {
describe('Storage', function() { describe('Storage', function() {
describe('ttl', function() { describe('ttl', function() {
it('returns milliseconds remaining', async function() { it('returns milliseconds remaining', async function() {
await storage.set('x', null, { foo: 'bar' }); const time = 40;
await storage.set('x', null, { foo: 'bar' }, time);
const ms = await storage.ttl('x'); const ms = await storage.ttl('x');
await storage.del('x'); await storage.del('x');
assert.equal(ms, expire_seconds * 1000); assert.equal(ms, time * 1000);
}); });
}); });
@ -50,33 +55,51 @@ describe('Storage', function() {
}); });
describe('get', function() { describe('get', function() {
it('returns a stream', function() { it('returns a stream', async function() {
const s = storage.get('x'); const s = await storage.get('x');
assert.equal(s, stream); assert.equal(s, stream);
}); });
}); });
describe('set', function() { describe('set', function() {
it('sets expiration to config.expire_seconds', async function() { it('sets expiration to expire time', async function() {
await storage.set('x', null, { foo: 'bar' }); const seconds = 100;
await storage.set('x', null, { foo: 'bar' }, seconds);
const s = await storage.redis.ttlAsync('x'); const s = await storage.redis.ttlAsync('x');
await storage.del('x'); await storage.del('x');
assert.equal(Math.ceil(s), expire_seconds); assert.equal(Math.ceil(s), seconds);
});
it('adds right prefix based on expire time', async function() {
await storage.set('x', null, { foo: 'bar' }, 10);
const path_x = await storage.getPrefixedId('x');
assert.equal(path_x, 'ten-x');
await storage.del('x');
await storage.set('y', null, { foo: 'bar' }, 11);
const path_y = await storage.getPrefixedId('y');
assert.equal(path_y, 'twenty-y');
await storage.del('y');
await storage.set('z', null, { foo: 'bar' }, 33);
const path_z = await storage.getPrefixedId('z');
assert.equal(path_z, 'thirty-z');
await storage.del('z');
}); });
it('sets metadata', async function() { it('sets metadata', async function() {
const m = { foo: 'bar' }; const m = { foo: 'bar' };
await storage.set('x', null, m); await storage.set('x', null, m);
const meta = await storage.redis.hgetallAsync('x'); const meta = await storage.redis.hgetallAsync('x');
delete meta.prefix;
await storage.del('x'); await storage.del('x');
assert.deepEqual(meta, m); assert.deepEqual(meta, m);
}); });
//it('throws when storage fails');
}); });
describe('setField', function() { describe('setField', function() {
it('works', async function() { it('works', async function() {
await storage.set('x', null);
storage.setField('x', 'y', 'z'); storage.setField('x', 'y', 'z');
const z = await storage.redis.hgetAsync('x', 'y'); const z = await storage.redis.hgetAsync('x', 'y');
assert.equal(z, 'z'); assert.equal(z, 'z');

View File

@ -1,3 +1,4 @@
/* global DEFAULT_EXPIRE_SECONDS */
import assert from 'assert'; import assert from 'assert';
import Archive from '../../../app/archive'; import Archive from '../../../app/archive';
import * as api from '../../../app/api'; import * as api from '../../../app/api';
@ -18,7 +19,13 @@ describe('API', function() {
const meta = await keychain.encryptMetadata(metadata); const meta = await keychain.encryptMetadata(metadata);
const verifierB64 = await keychain.authKeyB64(); const verifierB64 = await keychain.authKeyB64();
const p = function() {}; const p = function() {};
const up = api.uploadWs(enc, meta, verifierB64, p); const up = api.uploadWs(
enc,
meta,
verifierB64,
p,
DEFAULT_EXPIRE_SECONDS
);
const result = await up.result; const result = await up.result;
assert.ok(result.url); assert.ok(result.url);
@ -32,7 +39,14 @@ describe('API', function() {
const meta = await keychain.encryptMetadata(metadata); const meta = await keychain.encryptMetadata(metadata);
const verifierB64 = await keychain.authKeyB64(); const verifierB64 = await keychain.authKeyB64();
const p = function() {}; const p = function() {};
const up = api.uploadWs(enc, meta, verifierB64, p); const up = api.uploadWs(
enc,
meta,
verifierB64,
p,
DEFAULT_EXPIRE_SECONDS
);
up.cancel(); up.cancel();
try { try {
await up.result; await up.result;