added /config endpoint, use fewer globals (#1172)
* added /config endpoint, use fewer globals * fixed integration tests
This commit is contained in:
parent
8df400a676
commit
1c44d1d0f9
|
@ -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) {
|
||||
|
|
|
@ -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() {
|
||||
|
|
11
app/api.js
11
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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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
|
||||
};
|
||||
|
|
|
@ -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`
|
||||
<p class="center font-medium text-xs text-grey-dark mt-4 mb-2">
|
||||
${state.translate('signInSizeBump', {
|
||||
size: bytes(LIMITS.MAX_FILE_SIZE, 0)
|
||||
size: bytes(state.LIMITS.MAX_FILE_SIZE, 0)
|
||||
})}
|
||||
</p>
|
||||
`;
|
||||
|
|
|
@ -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
|
||||
);
|
||||
|
||||
|
|
|
@ -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) {
|
|||
<h2 class="font-bold">${state.translate('accountBenefitTitle')}</h3>
|
||||
<ul class="my-2 leading-normal list-reset text-lg mb-8 mt-4">
|
||||
<li>${state.translate('accountBenefitLargeFiles', {
|
||||
size: bytes(LIMITS.MAX_FILE_SIZE)
|
||||
size: bytes(state.LIMITS.MAX_FILE_SIZE)
|
||||
})}</li>
|
||||
<li>${state.translate('accountBenefitExpiry')}</li>
|
||||
<li>${state.translate('accountBenefitExpiryTwo', { count: DAYS })}</li>
|
||||
|
|
29
app/user.js
29
app/user.js
|
@ -1,4 +1,3 @@
|
|||
/* global LIMITS AUTH_CONFIG */
|
||||
import assets from '../common/assets';
|
||||
import { getFileList, setFileList } from './api';
|
||||
import { encryptStream, decryptStream } from './ece';
|
||||
|
@ -21,7 +20,9 @@ async function hashId(id) {
|
|||
}
|
||||
|
||||
export default class User {
|
||||
constructor(storage) {
|
||||
constructor(storage, limits, authConfig) {
|
||||
this.authConfig = authConfig;
|
||||
this.limits = limits;
|
||||
this.storage = storage;
|
||||
this.data = storage.user || {};
|
||||
}
|
||||
|
@ -68,17 +69,21 @@ export default class User {
|
|||
}
|
||||
|
||||
get maxSize() {
|
||||
return this.loggedIn ? LIMITS.MAX_FILE_SIZE : LIMITS.ANON.MAX_FILE_SIZE;
|
||||
return this.loggedIn
|
||||
? this.limits.MAX_FILE_SIZE
|
||||
: this.limits.ANON.MAX_FILE_SIZE;
|
||||
}
|
||||
|
||||
get maxExpireSeconds() {
|
||||
return this.loggedIn
|
||||
? LIMITS.MAX_EXPIRE_SECONDS
|
||||
: LIMITS.ANON.MAX_EXPIRE_SECONDS;
|
||||
? this.limits.MAX_EXPIRE_SECONDS
|
||||
: this.limits.ANON.MAX_EXPIRE_SECONDS;
|
||||
}
|
||||
|
||||
get maxDownloads() {
|
||||
return this.loggedIn ? LIMITS.MAX_DOWNLOADS : LIMITS.ANON.MAX_DOWNLOADS;
|
||||
return this.loggedIn
|
||||
? this.limits.MAX_DOWNLOADS
|
||||
: this.limits.ANON.MAX_DOWNLOADS;
|
||||
}
|
||||
|
||||
async metricId() {
|
||||
|
@ -95,11 +100,11 @@ export default class User {
|
|||
const keys_jwk = await prepareScopedBundleKey(this.storage);
|
||||
const code_challenge = await preparePkce(this.storage);
|
||||
const options = {
|
||||
client_id: AUTH_CONFIG.client_id,
|
||||
client_id: this.authConfig.client_id,
|
||||
code_challenge,
|
||||
code_challenge_method: 'S256',
|
||||
response_type: 'code',
|
||||
scope: `profile ${AUTH_CONFIG.key_scope}`,
|
||||
scope: `profile ${this.authConfig.key_scope}`,
|
||||
state,
|
||||
keys_jwk
|
||||
};
|
||||
|
@ -108,7 +113,7 @@ export default class User {
|
|||
}
|
||||
const params = new URLSearchParams(options);
|
||||
location.assign(
|
||||
`${AUTH_CONFIG.authorization_endpoint}?${params.toString()}`
|
||||
`${this.authConfig.authorization_endpoint}?${params.toString()}`
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -118,19 +123,19 @@ export default class User {
|
|||
if (state !== localState) {
|
||||
throw new Error('state mismatch');
|
||||
}
|
||||
const tokenResponse = await fetch(AUTH_CONFIG.token_endpoint, {
|
||||
const tokenResponse = await fetch(this.authConfig.token_endpoint, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
code,
|
||||
client_id: AUTH_CONFIG.client_id,
|
||||
client_id: this.authConfig.client_id,
|
||||
code_verifier: this.storage.get('pkceVerifier')
|
||||
})
|
||||
});
|
||||
const auth = await tokenResponse.json();
|
||||
const infoResponse = await fetch(AUTH_CONFIG.userinfo_endpoint, {
|
||||
const infoResponse = await fetch(this.authConfig.userinfo_endpoint, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
Authorization: `Bearer ${auth.access_token}`
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
const config = require('./config');
|
||||
|
||||
module.exports = {
|
||||
LIMITS: {
|
||||
ANON: {
|
||||
MAX_FILE_SIZE: config.anon_max_file_size,
|
||||
MAX_DOWNLOADS: config.anon_max_downloads,
|
||||
MAX_EXPIRE_SECONDS: config.anon_max_expire_seconds
|
||||
},
|
||||
MAX_FILE_SIZE: config.max_file_size,
|
||||
MAX_DOWNLOADS: config.max_downloads,
|
||||
MAX_EXPIRE_SECONDS: config.max_expire_seconds,
|
||||
MAX_FILES_PER_ARCHIVE: config.max_files_per_archive,
|
||||
MAX_ARCHIVES_PER_USER: config.max_archives_per_user
|
||||
},
|
||||
DEFAULTS: {
|
||||
DOWNLOAD_COUNTS: config.download_counts,
|
||||
EXPIRE_TIMES_SECONDS: config.expire_times_seconds,
|
||||
EXPIRE_SECONDS: config.default_expire_seconds
|
||||
}
|
||||
};
|
|
@ -1,6 +1,7 @@
|
|||
const html = require('choo/html');
|
||||
const raw = require('choo/html/raw');
|
||||
const config = require('./config');
|
||||
const clientConstants = require('./clientConstants');
|
||||
|
||||
let sentry = '';
|
||||
if (config.sentry_id) {
|
||||
|
@ -44,23 +45,8 @@ module.exports = function(state) {
|
|||
window.location.assign('/unsupported/outdated');
|
||||
}
|
||||
|
||||
var LIMITS = {
|
||||
ANON: {
|
||||
MAX_FILE_SIZE: ${config.anon_max_file_size},
|
||||
MAX_DOWNLOADS: ${config.anon_max_downloads},
|
||||
MAX_EXPIRE_SECONDS: ${config.anon_max_expire_seconds},
|
||||
},
|
||||
MAX_FILE_SIZE: ${config.max_file_size},
|
||||
MAX_DOWNLOADS: ${config.max_downloads},
|
||||
MAX_EXPIRE_SECONDS: ${config.max_expire_seconds},
|
||||
MAX_FILES_PER_ARCHIVE: ${config.max_files_per_archive},
|
||||
MAX_ARCHIVES_PER_USER: ${config.max_archives_per_user}
|
||||
};
|
||||
var DEFAULTS = {
|
||||
DOWNLOAD_COUNTS: ${JSON.stringify(config.download_counts)},
|
||||
EXPIRE_TIMES_SECONDS: ${JSON.stringify(config.expire_times_seconds)},
|
||||
EXPIRE_SECONDS: ${config.default_expire_seconds}
|
||||
};
|
||||
var LIMITS = ${JSON.stringify(clientConstants.LIMITS)};
|
||||
var DEFAULTS = ${JSON.stringify(clientConstants.DEFAULTS)};
|
||||
const LOCALE = '${state.locale}';
|
||||
const downloadMetadata = ${
|
||||
state.downloadMetadata ? raw(JSON.stringify(state.downloadMetadata)) : '{}'
|
||||
|
|
|
@ -8,6 +8,7 @@ const auth = require('../middleware/auth');
|
|||
const language = require('../middleware/language');
|
||||
const pages = require('./pages');
|
||||
const filelist = require('./filelist');
|
||||
const clientConstants = require('../clientConstants');
|
||||
|
||||
const IS_DEV = config.env === 'development';
|
||||
const ID_REGEX = '([0-9a-fA-F]{10})';
|
||||
|
@ -70,6 +71,9 @@ module.exports = function(app) {
|
|||
app.use(bodyParser.json());
|
||||
app.use(bodyParser.text());
|
||||
app.get('/', language, pages.index);
|
||||
app.get('/config', function(req, res) {
|
||||
res.json(clientConstants);
|
||||
});
|
||||
app.get('/error', language, pages.blank);
|
||||
app.get('/oauth', language, pages.blank);
|
||||
app.get('/legal', language, pages.legal);
|
||||
|
|
|
@ -52,7 +52,7 @@ describe('Firefox Send', function() {
|
|||
browser.back();
|
||||
browser.waitForExist('send-archive');
|
||||
assert.equal(
|
||||
browser.getText('send-archive > div').substring(0, 24),
|
||||
browser.getText('send-archive > div:first-of-type').substring(0, 24),
|
||||
'Expires after 1 download'
|
||||
);
|
||||
});
|
||||
|
|
|
@ -17,7 +17,7 @@ class Page {
|
|||
waitForPageToLoad() {
|
||||
browser.waitUntil(function() {
|
||||
return browser.execute(function() {
|
||||
return typeof window.appState !== 'undefined';
|
||||
return typeof window.app !== 'undefined';
|
||||
});
|
||||
}, 3000);
|
||||
browser.pause(100);
|
||||
|
|
Loading…
Reference in New Issue