diff --git a/android/android.js b/android/android.js index 712bc034..9d113776 100644 --- a/android/android.js +++ b/android/android.js @@ -1,29 +1,9 @@ /* global window, navigator */ - -window.LIMITS = { - ANON: { - MAX_FILE_SIZE: 1024 * 1024 * 1024 * 2, - MAX_DOWNLOADS: 20, - MAX_EXPIRE_SECONDS: 86400 - }, - MAX_FILE_SIZE: 1024 * 1024 * 1024 * 2, - MAX_DOWNLOADS: 200, - MAX_EXPIRE_SECONDS: 604800, - MAX_FILES_PER_ARCHIVE: 64, - MAX_ARCHIVES_PER_USER: 16 -}; - -window.DEFAULTS = { - DOWNLOAD_COUNTS: [1, 2, 3, 4, 5, 20, 50, 100, 200], - EXPIRE_TIMES_SECONDS: [300, 3600, 86400, 604800], - EXPIRE_SECONDS: 3600 -}; - import choo from 'choo'; import html from 'choo/html'; import Raven from 'raven-js'; -import { setApiUrlPrefix } from '../app/api'; +import { setApiUrlPrefix, getConstants } from '../app/api'; import metrics from '../app/metrics'; //import assets from '../common/assets'; import Archive from '../app/archive'; @@ -78,14 +58,17 @@ function body(main) { } (async function start() { const translate = await getTranslator('en-US'); + const { LIMITS, DEFAULTS } = await getConstants(); app.use((state, emitter) => { + state.LIMITS = LIMITS; + state.DEFAULTS = DEFAULTS; state.translate = translate; state.capabilities = { account: true }; //TODO - state.archive = new Archive(); + state.archive = new Archive([], DEFAULTS.EXPIRE_SECONDS); state.storage = storage; - state.user = new User(storage); + state.user = new User(storage, LIMITS); state.raven = Raven; window.finishLogin = async function(accountInfo) { diff --git a/android/user.js b/android/user.js index 822a39ee..2b2a543d 100644 --- a/android/user.js +++ b/android/user.js @@ -3,8 +3,8 @@ import User from '../app/user'; import { deriveFileListKey } from '../app/fxa'; export default class AndroidUser extends User { - constructor(storage) { - super(storage); + constructor(storage, limits) { + super(storage, limits); } async login() { diff --git a/app/api.js b/app/api.js index 98bc7bdb..45fc00ef 100644 --- a/app/api.js +++ b/app/api.js @@ -401,3 +401,14 @@ export function sendMetrics(blob) { console.error(e); } } + +export async function getConstants() { + const response = await fetch(getApiUrl('/config')); + + if (response.ok) { + const obj = await response.json(); + return obj; + } + + throw new Error(response.status); +} diff --git a/app/archive.js b/app/archive.js index 572bb071..45517754 100644 --- a/app/archive.js +++ b/app/archive.js @@ -1,4 +1,3 @@ -/* global LIMITS DEFAULTS */ import { blobStream, concatStream } from './streams'; function isDupe(newFile, array) { @@ -15,9 +14,10 @@ function isDupe(newFile, array) { } export default class Archive { - constructor(files = []) { + constructor(files = [], defaultTimeLimit = 86400) { this.files = Array.from(files); - this.timeLimit = DEFAULTS.EXPIRE_SECONDS; + this.defaultTimeLimit = defaultTimeLimit; + this.timeLimit = defaultTimeLimit; this.dlimit = 1; this.password = null; } @@ -52,8 +52,8 @@ export default class Archive { return concatStream(this.files.map(file => blobStream(file))); } - addFiles(files, maxSize) { - if (this.files.length + files.length > LIMITS.MAX_FILES_PER_ARCHIVE) { + addFiles(files, maxSize, maxFiles) { + if (this.files.length + files.length > maxFiles) { throw new Error('tooManyFiles'); } const newFiles = files.filter( @@ -77,7 +77,7 @@ export default class Archive { clear() { this.files = []; this.dlimit = 1; - this.timeLimit = DEFAULTS.EXPIRE_SECONDS; + this.timeLimit = this.defaultTimeLimit; this.password = null; } } diff --git a/app/controller.js b/app/controller.js index 95fa9ad3..ac12da29 100644 --- a/app/controller.js +++ b/app/controller.js @@ -1,4 +1,3 @@ -/* global LIMITS */ import FileSender from './fileSender'; import FileReceiver from './fileReceiver'; import { copyToClipboard, delay, openLinksInNewTab, percent } from './utils'; @@ -87,15 +86,19 @@ export default function(state, emitter) { } const maxSize = state.user.maxSize; try { - state.archive.addFiles(files, maxSize); + state.archive.addFiles( + files, + maxSize, + state.LIMITS.MAX_FILES_PER_ARCHIVE + ); } catch (e) { - if (e.message === 'fileTooBig' && maxSize < LIMITS.MAX_FILE_SIZE) { + if (e.message === 'fileTooBig' && maxSize < state.LIMITS.MAX_FILE_SIZE) { return emitter.emit('signup-cta', 'size'); } state.modal = okDialog( state.translate(e.message, { size: bytes(maxSize), - count: LIMITS.MAX_FILES_PER_ARCHIVE + count: state.LIMITS.MAX_FILES_PER_ARCHIVE }) ); } @@ -119,10 +122,10 @@ export default function(state, emitter) { }); emitter.on('upload', async () => { - if (state.storage.files.length >= LIMITS.MAX_ARCHIVES_PER_USER) { + if (state.storage.files.length >= state.LIMITS.MAX_ARCHIVES_PER_USER) { state.modal = okDialog( state.translate('tooManyArchives', { - count: LIMITS.MAX_ARCHIVES_PER_USER + count: state.LIMITS.MAX_ARCHIVES_PER_USER }) ); return render(); diff --git a/app/main.js b/app/main.js index c4dd79ae..b64a5955 100644 --- a/app/main.js +++ b/app/main.js @@ -1,4 +1,4 @@ -/* global LOCALE */ +/* global DEFAULTS LIMITS LOCALE */ import 'core-js'; import 'fast-text-encoding'; // MS Edge support import 'fluent-intl-polyfill'; @@ -41,12 +41,14 @@ if (process.env.NODE_ENV === 'production') { const translate = await getTranslator(LOCALE); window.initialState = { - archive: new Archive(), + LIMITS, + DEFAULTS, + archive: new Archive([], DEFAULTS.EXPIRE_SECONDS), capabilities, translate, storage, raven: Raven, - user: new User(storage), + user: new User(storage, LIMITS, window.AUTH_CONFIG), transfer: null, fileInfo: null }; diff --git a/app/ui/archiveTile.js b/app/ui/archiveTile.js index abf3af7e..b840db94 100644 --- a/app/ui/archiveTile.js +++ b/app/ui/archiveTile.js @@ -1,4 +1,4 @@ -/* global Android LIMITS */ +/* global Android */ const html = require('choo/html'); const raw = require('choo/html/raw'); @@ -390,7 +390,7 @@ module.exports.empty = function(state, emit) { : html`

${state.translate('signInSizeBump', { - size: bytes(LIMITS.MAX_FILE_SIZE, 0) + size: bytes(state.LIMITS.MAX_FILE_SIZE, 0) })}

`; diff --git a/app/ui/expiryOptions.js b/app/ui/expiryOptions.js index a34bac0c..fd73cc37 100644 --- a/app/ui/expiryOptions.js +++ b/app/ui/expiryOptions.js @@ -1,4 +1,3 @@ -/* globals DEFAULTS */ const html = require('choo/html'); const raw = require('choo/html/raw'); const { secondsToL10nId } = require('../utils'); @@ -21,7 +20,7 @@ module.exports = function(state, emit) { return el; } - const counts = DEFAULTS.DOWNLOAD_COUNTS.filter( + const counts = state.DEFAULTS.DOWNLOAD_COUNTS.filter( i => state.capabilities.account || i <= state.user.maxDownloads ); @@ -45,7 +44,7 @@ module.exports = function(state, emit) { dlCountSelect ); - const expires = DEFAULTS.EXPIRE_TIMES_SECONDS.filter( + const expires = state.DEFAULTS.EXPIRE_TIMES_SECONDS.filter( i => state.capabilities.account || i <= state.user.maxExpireSeconds ); diff --git a/app/ui/signupDialog.js b/app/ui/signupDialog.js index 56832d7a..3febc16c 100644 --- a/app/ui/signupDialog.js +++ b/app/ui/signupDialog.js @@ -1,12 +1,10 @@ -/* global LIMITS */ const html = require('choo/html'); const { bytes, platform } = require('../utils'); const { canceledSignup, submittedSignup } = require('../metrics'); -const DAYS = Math.floor(LIMITS.MAX_EXPIRE_SECONDS / 86400); - module.exports = function(trigger) { return function(state, emit, close) { + const DAYS = Math.floor(state.LIMITS.MAX_EXPIRE_SECONDS / 86400); const hidden = platform() === 'android' ? 'hidden' : ''; let submitting = false; return html` @@ -14,7 +12,7 @@ module.exports = function(trigger) {

${state.translate('accountBenefitTitle')}