Merge branch 'apply-mozilla-patches' into 'master'
Apply Mozilla patches See merge request timvisee/send!3
This commit is contained in:
commit
ba33e022b0
|
@ -16,13 +16,12 @@ RUN set -x \
|
|||
--home /app \
|
||||
--uid 10001 \
|
||||
app
|
||||
RUN npm i -g npm
|
||||
COPY --chown=app:app . /app
|
||||
USER app
|
||||
WORKDIR /app
|
||||
RUN set -x \
|
||||
# Build
|
||||
&& npm ci \
|
||||
&& PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true npm ci \
|
||||
&& npm run build
|
||||
|
||||
|
||||
|
|
|
@ -61,7 +61,10 @@ async function fetchWithAuth(url, params, keychain) {
|
|||
const result = {};
|
||||
params = params || {};
|
||||
const h = await keychain.authHeader();
|
||||
params.headers = new Headers({ Authorization: h });
|
||||
params.headers = new Headers({
|
||||
Authorization: h,
|
||||
'Content-Type': 'application/json'
|
||||
});
|
||||
const response = await fetch(url, params);
|
||||
result.response = response;
|
||||
result.ok = response.ok;
|
||||
|
|
|
@ -77,6 +77,7 @@ async function polyfillStreams() {
|
|||
|
||||
export default async function getCapabilities() {
|
||||
const browser = browserName();
|
||||
const isMobile = /mobi|android/i.test(navigator.userAgent);
|
||||
const serviceWorker = 'serviceWorker' in navigator && browser !== 'edge';
|
||||
let crypto = await checkCrypto();
|
||||
const nativeStreams = checkStreams();
|
||||
|
@ -91,14 +92,15 @@ export default async function getCapabilities() {
|
|||
account = false;
|
||||
}
|
||||
const share =
|
||||
typeof navigator.share === 'function' && locale().startsWith('en'); // en until strings merge
|
||||
isMobile &&
|
||||
typeof navigator.share === 'function' &&
|
||||
locale().startsWith('en'); // en until strings merge
|
||||
|
||||
const standalone =
|
||||
window.matchMedia('(display-mode: standalone)').matches ||
|
||||
navigator.standalone;
|
||||
|
||||
const mobileFirefox =
|
||||
browser === 'firefox' && /mobile/i.test(navigator.userAgent);
|
||||
const mobileFirefox = browser === 'firefox' && isMobile;
|
||||
|
||||
return {
|
||||
account,
|
||||
|
|
|
@ -49,8 +49,8 @@ export default function(state, emitter) {
|
|||
state.user.login(email);
|
||||
});
|
||||
|
||||
emitter.on('logout', () => {
|
||||
state.user.logout();
|
||||
emitter.on('logout', async () => {
|
||||
await state.user.logout();
|
||||
metrics.loggedOut({ trigger: 'button' });
|
||||
emitter.emit('pushState', '/');
|
||||
});
|
||||
|
@ -178,6 +178,12 @@ export default function(state, emitter) {
|
|||
//cancelled. do nothing
|
||||
metrics.cancelledUpload(archive, err.duration);
|
||||
render();
|
||||
} else if (err.message === '401') {
|
||||
const refreshed = await state.user.refresh();
|
||||
if (refreshed) {
|
||||
return emitter.emit('upload');
|
||||
}
|
||||
emitter.emit('pushState', '/error');
|
||||
} else {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(err);
|
||||
|
@ -229,6 +235,9 @@ export default function(state, emitter) {
|
|||
if (!file.requiresPassword) {
|
||||
return emitter.emit('pushState', '/404');
|
||||
}
|
||||
} else {
|
||||
console.error(e);
|
||||
return emitter.emit('pushState', '/error');
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import Nanobus from 'nanobus';
|
||||
import Keychain from './keychain';
|
||||
import { delay, bytes, streamToArrayBuffer } from './utils';
|
||||
import { downloadFile, metadata, getApiUrl } from './api';
|
||||
import { downloadFile, metadata, getApiUrl, reportLink } from './api';
|
||||
import { blobStream } from './streams';
|
||||
import Zip from './zip';
|
||||
|
||||
|
@ -53,6 +53,10 @@ export default class FileReceiver extends Nanobus {
|
|||
this.state = 'ready';
|
||||
}
|
||||
|
||||
async reportLink(reason) {
|
||||
await reportLink(this.fileInfo.id, this.keychain, reason);
|
||||
}
|
||||
|
||||
sendMessageToSw(msg) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const channel = new MessageChannel();
|
||||
|
|
|
@ -283,7 +283,7 @@ select {
|
|||
@apply m-auto;
|
||||
@apply py-8;
|
||||
|
||||
min-height: 36rem;
|
||||
min-height: 42rem;
|
||||
max-height: 42rem;
|
||||
width: calc(100% - 3rem);
|
||||
}
|
||||
|
|
|
@ -13,7 +13,10 @@ module.exports = function(app = choo({ hash: true })) {
|
|||
app.route('/oauth', function(state, emit) {
|
||||
emit('authenticate', state.query.code, state.query.state);
|
||||
});
|
||||
app.route('/login', body(require('./ui/home')));
|
||||
app.route('/login', function(state, emit) {
|
||||
emit('replaceState', '/');
|
||||
setTimeout(() => emit('render'));
|
||||
});
|
||||
app.route('*', body(require('./ui/notFound')));
|
||||
return app;
|
||||
};
|
||||
|
|
|
@ -9,7 +9,7 @@ import contentDisposition from 'content-disposition';
|
|||
let noSave = false;
|
||||
const map = new Map();
|
||||
const IMAGES = /.*\.(png|svg|jpg)$/;
|
||||
const VERSIONED_ASSET = /\.[A-Fa-f0-9]{8}\.(js|css|png|svg|jpg)$/;
|
||||
const VERSIONED_ASSET = /\.[A-Fa-f0-9]{8}\.(js|css|png|svg|jpg)(#\w+)?$/;
|
||||
const DOWNLOAD_URL = /\/api\/download\/([A-Fa-f0-9]{4,})/;
|
||||
const FONT = /\.woff2?$/;
|
||||
|
||||
|
|
|
@ -54,12 +54,17 @@ class Account extends Component {
|
|||
createElement() {
|
||||
if (!this.enabled) {
|
||||
return html`
|
||||
<div></div>
|
||||
<send-account></send-account>
|
||||
`;
|
||||
}
|
||||
const user = this.state.user;
|
||||
const translate = this.state.translate;
|
||||
this.setLocal();
|
||||
if (user.loginRequired && !this.local.loggedIn) {
|
||||
return html`
|
||||
<send-account></send-account>
|
||||
`;
|
||||
}
|
||||
if (!this.local.loggedIn) {
|
||||
return html`
|
||||
<send-account>
|
||||
|
|
|
@ -30,6 +30,12 @@ function password(state) {
|
|||
|
||||
return html`
|
||||
<div class="mb-2 px-1">
|
||||
<input
|
||||
id="autocomplete-decoy"
|
||||
class="hidden"
|
||||
type="password"
|
||||
value="lol"
|
||||
/>
|
||||
<div class="checkbox inline-block mr-3">
|
||||
<input
|
||||
id="add-password"
|
||||
|
|
|
@ -55,9 +55,13 @@ module.exports = function(state, emit) {
|
|||
let content = '';
|
||||
if (!state.fileInfo) {
|
||||
state.fileInfo = createFileInfo(state);
|
||||
if (!state.fileInfo.nonce) {
|
||||
if (downloadMetadata.status === 404) {
|
||||
return notFound(state);
|
||||
}
|
||||
if (!state.fileInfo.nonce) {
|
||||
// coming from something like the browser back button
|
||||
return location.reload();
|
||||
}
|
||||
}
|
||||
|
||||
if (!state.transfer && !state.fileInfo.requiresPassword) {
|
||||
|
|
|
@ -2,6 +2,7 @@ const html = require('choo/html');
|
|||
const assets = require('../../common/assets');
|
||||
|
||||
module.exports = function(state) {
|
||||
const btnText = state.user.loggedIn ? 'okButton' : 'sendYourFilesLink';
|
||||
return html`
|
||||
<div
|
||||
id="download-complete"
|
||||
|
@ -10,13 +11,18 @@ module.exports = function(state) {
|
|||
<h1 class="text-center text-3xl font-bold my-2">
|
||||
${state.translate('downloadFinish')}
|
||||
</h1>
|
||||
<img src="${assets.get('completed.svg')}" class="my-12 h-48" />
|
||||
<p class="text-grey-80 leading-normal dark:text-grey-40">
|
||||
<img src="${assets.get('completed.svg')}" class="my-8 h-48" />
|
||||
<p
|
||||
class="text-grey-80 leading-normal dark:text-grey-40 ${state.user
|
||||
.loggedIn
|
||||
? 'hidden'
|
||||
: ''}"
|
||||
>
|
||||
${state.translate('trySendDescription')}
|
||||
</p>
|
||||
<p class="my-5">
|
||||
<a href="/" class="btn rounded-lg flex items-center mt-4" role="button"
|
||||
>${state.translate('sendYourFilesLink')}</a
|
||||
>${state.translate(btnText)}</a
|
||||
>
|
||||
</p>
|
||||
</div>
|
||||
|
|
|
@ -21,6 +21,12 @@ module.exports = function(state, emit) {
|
|||
onsubmit="${checkPassword}"
|
||||
data-no-csrf
|
||||
>
|
||||
<input
|
||||
id="autocomplete-decoy"
|
||||
class="hidden"
|
||||
type="password"
|
||||
value="lol"
|
||||
/>
|
||||
<input
|
||||
id="password-input"
|
||||
class="w-full border-l border-t border-b rounded-l-lg rounded-r-none ${invalid
|
||||
|
@ -63,8 +69,13 @@ module.exports = function(state, emit) {
|
|||
const input = document.getElementById('password-input');
|
||||
const btn = document.getElementById('password-btn');
|
||||
label.classList.add('invisible');
|
||||
input.classList.remove('border-red');
|
||||
btn.classList.remove('bg-red', 'hover:bg-red', 'focus:bg-red');
|
||||
input.classList.remove('border-red', 'dark:border-red-40');
|
||||
btn.classList.remove(
|
||||
'bg-red',
|
||||
'hover:bg-red',
|
||||
'focus:bg-red',
|
||||
'dark:bg-red-40'
|
||||
);
|
||||
}
|
||||
|
||||
function checkPassword(event) {
|
||||
|
|
|
@ -3,6 +3,7 @@ const assets = require('../../common/assets');
|
|||
const modal = require('./modal');
|
||||
|
||||
module.exports = function(state, emit) {
|
||||
const btnText = state.user.loggedIn ? 'okButton' : 'sendYourFilesLink';
|
||||
return html`
|
||||
<main class="main">
|
||||
${state.modal && modal(state, emit)}
|
||||
|
@ -13,12 +14,17 @@ module.exports = function(state, emit) {
|
|||
${state.translate('errorPageHeader')}
|
||||
</h1>
|
||||
<img class="my-12 h-48" src="${assets.get('error.svg')}" />
|
||||
<p class="max-w-md text-center text-grey-80 leading-normal">
|
||||
<p
|
||||
class="max-w-md text-center text-grey-80 leading-normal dark:text-grey-40 ${state
|
||||
.user.loggedIn
|
||||
? 'hidden'
|
||||
: ''}"
|
||||
>
|
||||
${state.translate('trySendDescription')}
|
||||
</p>
|
||||
<p class="my-5">
|
||||
<a href="/" class="btn rounded-lg flex items-center" role="button"
|
||||
>${state.translate('sendYourFilesLink')}</a
|
||||
>${state.translate(btnText)}</a
|
||||
>
|
||||
</p>
|
||||
</section>
|
||||
|
|
|
@ -33,7 +33,7 @@ class Header extends Component {
|
|||
alt="${this.state.translate('title')}"
|
||||
src="${assets.get('icon.svg')}"
|
||||
/>
|
||||
<svg class="w-48 md:w-64">
|
||||
<svg viewBox="66 0 340 64" class="w-48 md:w-64">
|
||||
<use xlink:href="${assets.get('wordmark.svg')}#logo" />
|
||||
</svg>
|
||||
</a>
|
||||
|
|
|
@ -2,6 +2,7 @@ const html = require('choo/html');
|
|||
const modal = require('./modal');
|
||||
|
||||
module.exports = function(state, emit) {
|
||||
state.modal = null;
|
||||
return html`
|
||||
<main class="main">
|
||||
${state.modal && modal(state, emit)}
|
||||
|
|
|
@ -6,7 +6,7 @@ module.exports = function(state, emit) {
|
|||
class="absolute inset-0 flex items-center justify-center overflow-hidden z-40 bg-white md:rounded-xl md:my-8 dark:bg-grey-90"
|
||||
>
|
||||
<div
|
||||
class="h-full w-full max-h-screen absolute top-0 flex items-center justify-center"
|
||||
class="h-full w-full max-h-screen absolute top-0 flex justify-center md:items-center"
|
||||
>
|
||||
<div class="w-full">
|
||||
${state.modal(state, emit, close)}
|
||||
|
|
|
@ -19,9 +19,9 @@ module.exports = function(state, emit) {
|
|||
<form class="md:w-128" onsubmit=${submit}>
|
||||
<fieldset class="border rounded p-4 my-4" onchange=${optionChanged}>
|
||||
<div class="flex items-center mb-2">
|
||||
<img class="mr-3 flex-shrink-0" src="${assets.get(
|
||||
'blue_file.svg'
|
||||
)}"/>
|
||||
<svg class="h-8 w-6 mr-3 flex-shrink-0 text-white dark:text-grey-90">
|
||||
<use xlink:href="${assets.get('blue_file.svg')}#icon"/>
|
||||
</svg>
|
||||
<p class="flex-grow">
|
||||
<h1 class="text-base font-medium word-break-all">${
|
||||
archive.name
|
||||
|
@ -55,6 +55,11 @@ module.exports = function(state, emit) {
|
|||
value="${state.translate('copyLinkButton')}"
|
||||
title="${state.translate('copyLinkButton')}"
|
||||
type="submit" />
|
||||
<p
|
||||
class="text-grey-80 leading-normal dark:text-grey-40 font-semibold text-center md:my-8 md:text-left"
|
||||
>
|
||||
${state.translate('downloadConfirmDescription')}
|
||||
</p>
|
||||
</form>
|
||||
</div>
|
||||
`;
|
||||
|
@ -64,6 +69,7 @@ module.exports = function(state, emit) {
|
|||
const choice = event.target.value;
|
||||
const button = event.currentTarget.nextElementSibling;
|
||||
let title = button.title;
|
||||
console.error(choice, title);
|
||||
switch (choice) {
|
||||
case 'copy':
|
||||
title = state.translate('copyLinkButton');
|
||||
|
|
|
@ -3,6 +3,7 @@ const assets = require('../../common/assets');
|
|||
const modal = require('./modal');
|
||||
|
||||
module.exports = function(state, emit) {
|
||||
const btnText = state.user.loggedIn ? 'okButton' : 'sendYourFilesLink';
|
||||
return html`
|
||||
<main class="main">
|
||||
${state.modal && modal(state, emit)}
|
||||
|
@ -13,12 +14,17 @@ module.exports = function(state, emit) {
|
|||
${state.translate('expiredTitle')}
|
||||
</h1>
|
||||
<img src="${assets.get('notFound.svg')}" class="my-12" />
|
||||
<p class="max-w-md text-center text-grey-80 leading-normal">
|
||||
<p
|
||||
class="max-w-md text-center text-grey-80 leading-normal dark:text-grey-40 ${state
|
||||
.user.loggedIn
|
||||
? 'hidden'
|
||||
: ''}"
|
||||
>
|
||||
${state.translate('trySendDescription')}
|
||||
</p>
|
||||
<p class="my-5">
|
||||
<a href="/" class="btn rounded-lg flex items-center" role="button"
|
||||
>${state.translate('sendYourFilesLink')}</a
|
||||
>${state.translate(btnText)}</a
|
||||
>
|
||||
</p>
|
||||
</section>
|
||||
|
|
|
@ -1,22 +1,19 @@
|
|||
const html = require('choo/html');
|
||||
const assets = require('../../common/assets');
|
||||
const { bytes, platform } = require('../utils');
|
||||
const { bytes } = require('../utils');
|
||||
const { canceledSignup, submittedSignup } = require('../metrics');
|
||||
|
||||
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`
|
||||
<send-signup-dialog
|
||||
class="flex flex-col lg:flex-row justify-center px-8 md:px-24 w-full h-full"
|
||||
class="flex flex-col justify-center my-16 md:my-0 px-8 md:px-24 w-full h-full"
|
||||
>
|
||||
<img src="${assets.get('master-logo.svg')}" class="h-16 mt-1 mb-4" />
|
||||
<section
|
||||
class="flex flex-col flex-shrink-0 self-center lg:mx-6 lg:max-w-xs"
|
||||
>
|
||||
<h1 class="text-3xl font-bold text-center lg:text-left">
|
||||
<section class="flex flex-col flex-shrink-0 self-center">
|
||||
<h1 class="text-3xl font-bold text-center">
|
||||
${state.translate('accountBenefitTitle')}
|
||||
</h1>
|
||||
<ul
|
||||
|
@ -32,17 +29,14 @@ module.exports = function(trigger) {
|
|||
${state.translate('accountBenefitTimeLimit', { count: DAYS })}
|
||||
</li>
|
||||
<li>${state.translate('accountBenefitSync')}</li>
|
||||
<li>${state.translate('accountBenefitMoz')}</li>
|
||||
</ul>
|
||||
</section>
|
||||
<section
|
||||
class="flex flex-col flex-grow m-4 md:self-center md:w-128 lg:max-w-xs"
|
||||
>
|
||||
<section class="flex flex-col flex-grow m-4 md:self-center md:w-128">
|
||||
<form onsubmit=${submitEmail} data-no-csrf>
|
||||
<input
|
||||
id="email-input"
|
||||
type="email"
|
||||
class="${hidden} border rounded-lg w-full px-2 py-1 h-12 mb-3 text-lg text-grey-70 leading-loose dark:bg-grey-80 dark:text-white"
|
||||
class="hidden border rounded-lg w-full px-2 py-1 h-12 mb-3 text-lg text-grey-70 leading-loose dark:bg-grey-80 dark:text-white"
|
||||
placeholder=${state.translate('emailPlaceholder')}
|
||||
/>
|
||||
<input
|
||||
|
@ -53,13 +47,17 @@ module.exports = function(trigger) {
|
|||
type="submit"
|
||||
/>
|
||||
</form>
|
||||
<button
|
||||
class="my-3 link-blue font-medium"
|
||||
title="${state.translate('deletePopupCancel')}"
|
||||
onclick=${cancel}
|
||||
>
|
||||
${state.translate('deletePopupCancel')}
|
||||
</button>
|
||||
${state.user.loginRequired
|
||||
? ''
|
||||
: html`
|
||||
<button
|
||||
class="my-3 link-blue font-medium"
|
||||
title="${state.translate('deletePopupCancel')}"
|
||||
onclick=${cancel}
|
||||
>
|
||||
${state.translate('deletePopupCancel')}
|
||||
</button>
|
||||
`}
|
||||
</section>
|
||||
</send-signup-dialog>
|
||||
`;
|
||||
|
|
79
app/user.js
79
app/user.js
|
@ -76,6 +76,10 @@ export default class User {
|
|||
return this.info.access_token;
|
||||
}
|
||||
|
||||
get refreshToken() {
|
||||
return this.info.refresh_token;
|
||||
}
|
||||
|
||||
get maxSize() {
|
||||
return this.loggedIn
|
||||
? this.limits.MAX_FILE_SIZE
|
||||
|
@ -135,6 +139,7 @@ export default class User {
|
|||
const code_challenge = await preparePkce(this.storage);
|
||||
const options = {
|
||||
action: 'email',
|
||||
access_type: 'offline',
|
||||
client_id: this.authConfig.client_id,
|
||||
code_challenge,
|
||||
code_challenge_method: 'S256',
|
||||
|
@ -192,12 +197,69 @@ export default class User {
|
|||
});
|
||||
const userInfo = await infoResponse.json();
|
||||
userInfo.access_token = auth.access_token;
|
||||
userInfo.refresh_token = auth.refresh_token;
|
||||
userInfo.fileListKey = await getFileListKey(this.storage, auth.keys_jwe);
|
||||
this.info = userInfo;
|
||||
this.storage.remove('pkceVerifier');
|
||||
}
|
||||
|
||||
logout() {
|
||||
async refresh() {
|
||||
if (!this.refreshToken) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
const tokenResponse = await fetch(this.authConfig.token_endpoint, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
client_id: this.authConfig.client_id,
|
||||
grant_type: 'refresh_token',
|
||||
refresh_token: this.refreshToken
|
||||
})
|
||||
});
|
||||
if (tokenResponse.ok) {
|
||||
const auth = await tokenResponse.json();
|
||||
const info = { ...this.info, access_token: auth.access_token };
|
||||
this.info = info;
|
||||
return true;
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
await this.logout();
|
||||
return false;
|
||||
}
|
||||
|
||||
async logout() {
|
||||
try {
|
||||
if (this.refreshToken) {
|
||||
await fetch(this.authConfig.revocation_endpoint, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
refresh_token: this.refreshToken
|
||||
})
|
||||
});
|
||||
}
|
||||
if (this.bearerToken) {
|
||||
await fetch(this.authConfig.revocation_endpoint, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
token: this.bearerToken
|
||||
})
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
// oh well, we tried
|
||||
}
|
||||
this.storage.clearLocalFiles();
|
||||
this.info = {};
|
||||
}
|
||||
|
@ -211,6 +273,14 @@ export default class User {
|
|||
const key = b64ToArray(this.info.fileListKey);
|
||||
const sha = await crypto.subtle.digest('SHA-256', key);
|
||||
const kid = arrayToB64(new Uint8Array(sha)).substring(0, 16);
|
||||
const retry = async () => {
|
||||
const refreshed = await this.refresh();
|
||||
if (refreshed) {
|
||||
return await this.syncFileList();
|
||||
} else {
|
||||
return { incoming: true };
|
||||
}
|
||||
};
|
||||
try {
|
||||
const encrypted = await getFileList(this.bearerToken, kid);
|
||||
const decrypted = await streamToArrayBuffer(
|
||||
|
@ -219,8 +289,7 @@ export default class User {
|
|||
list = JSON.parse(textDecoder.decode(decrypted));
|
||||
} catch (e) {
|
||||
if (e.message === '401') {
|
||||
this.logout();
|
||||
return { incoming: true };
|
||||
return retry(e);
|
||||
}
|
||||
}
|
||||
changes = await this.storage.merge(list);
|
||||
|
@ -236,7 +305,9 @@ export default class User {
|
|||
);
|
||||
await setFileList(this.bearerToken, kid, encrypted);
|
||||
} catch (e) {
|
||||
//
|
||||
if (e.message === '401') {
|
||||
return retry(e);
|
||||
}
|
||||
}
|
||||
return changes;
|
||||
}
|
||||
|
|
|
@ -142,12 +142,16 @@ function openLinksInNewTab(links, should = true) {
|
|||
|
||||
function browserName() {
|
||||
try {
|
||||
// order of these matters
|
||||
if (/firefox/i.test(navigator.userAgent)) {
|
||||
return 'firefox';
|
||||
}
|
||||
if (/edge/i.test(navigator.userAgent)) {
|
||||
return 'edge';
|
||||
}
|
||||
if (/edg/i.test(navigator.userAgent)) {
|
||||
return 'edgium';
|
||||
}
|
||||
if (/trident/i.test(navigator.userAgent)) {
|
||||
return 'ie';
|
||||
}
|
||||
|
|
|
@ -1,61 +1,6 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
version="1.1"
|
||||
id="svg7"
|
||||
sodipodi:docname="wordmark.svg"
|
||||
inkscape:version="0.92.5 (2060ec1f9f, 2020-04-08)">
|
||||
<metadata
|
||||
id="metadata13">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<defs
|
||||
id="defs11" />
|
||||
<sodipodi:namedview
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="1874"
|
||||
inkscape:window-height="1016"
|
||||
id="namedview9"
|
||||
showgrid="false"
|
||||
inkscape:zoom="3.337544"
|
||||
inkscape:cx="82.487885"
|
||||
inkscape:cy="47.814478"
|
||||
inkscape:window-x="1966"
|
||||
inkscape:window-y="27"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg7" />
|
||||
<symbol
|
||||
id="logo"
|
||||
viewBox="66 0 340 64">
|
||||
<path
|
||||
d="m 105.17,33.27 c -1.04895,-0.638175 -2.18377,-1.123082 -3.37,-1.44 -1.25,-0.34 -2.46,-0.63 -3.63,-0.88 l -3.08,-0.7 C 94.22073,30.069182 93.37751,29.78027 92.58,29.39 91.90449,29.074134 91.31719,28.596738 90.87,28 c -0.43741,-0.644047 -0.65489,-1.412243 -0.62,-2.19 -0.0406,-1.405196 0.53693,-2.75754 1.58,-3.7 1.06,-1 2.73,-1.44 5,-1.44 1.76437,-0.07198 3.51559,0.332147 5.07,1.17 1.35935,0.80694 2.51833,1.911219 3.39,3.23 l 2.79,-2.18 c -1.26761,-1.5933 -2.84201,-2.916072 -4.63,-3.89 -2.04373,-1.017745 -4.30804,-1.512526 -6.59,-1.44 -1.40785,-0.02195 -2.80876,0.201387 -4.14,0.66 -1.16063,0.399115 -2.24085,1.001871 -3.19,1.78 -0.8713,0.712445 -1.5718,1.611145 -2.05,2.63 -0.4819,1.011666 -0.72807,2.119452 -0.72,3.24 -0.05,1.231532 0.24064,2.452997 0.84,3.53 0.55827,0.895068 1.31002,1.653654 2.2,2.22 0.94422,0.612326 1.97599,1.077636 3.06,1.38 1.13,0.32 2.29,0.6 3.47,0.84 l 3.26,0.74 c 0.96945,0.22193 1.90929,0.557589 2.8,1 0.77256,0.367753 1.45522,0.900225 2,1.56 0.51019,0.701297 0.77072,1.553301 0.74,2.42 0.0438,1.566414 -0.62122,3.069031 -1.81,4.09 -1.52512,1.147855 -3.41702,1.699065 -5.32,1.55 -4.03416,0.15747 -7.83041,-1.90763 -9.89,-5.38 l -3,2.34 c 1.3876,1.880136 3.1735,3.430427 5.23,4.54 2.3855,1.197767 5.03194,1.782045 7.7,1.7 1.49114,0.02151 2.97422,-0.222285 4.38,-0.72 1.21788,-0.44929 2.33816,-1.128248 3.3,-2 0.88604,-0.797749 1.60053,-1.767412 2.1,-2.85 0.48895,-1.06318 0.74142,-2.219779 0.74,-3.39 0.0397,-1.336553 -0.30755,-2.656119 -1,-3.8 -0.62101,-0.95962 -1.44763,-1.769154 -2.42,-2.37 z m 27.51,-4.72 c -1.0207,-1.016684 -2.23916,-1.813109 -3.58,-2.34 -1.42831,-0.567565 -2.95311,-0.852828 -4.49,-0.84 -1.58532,-0.01887 -3.15769,0.287432 -4.62,0.9 -1.3691,0.572827 -2.61257,1.408599 -3.66,2.46 -2.1451,2.217513 -3.33989,5.184759 -3.33,8.27 -0.0138,1.54162 0.26439,3.071916 0.82,4.51 0.5255,1.363982 1.32922,2.603618 2.36,3.64 1.06096,1.043663 2.31862,1.866239 3.7,2.42 1.53222,0.610739 3.17082,0.909903 4.82,0.88 2.13421,0.08534 4.25095,-0.416179 6.12,-1.45 1.69947,-1.049265 3.13073,-2.480527 4.18,-4.18 l -2.88,-1.69 c -1.41279,2.768876 -4.32635,4.443291 -7.43,4.27 -1.09666,0.02103 -2.18793,-0.158593 -3.22,-0.53 -0.93382,-0.341463 -1.79784,-0.849713 -2.55,-1.5 -0.72694,-0.645531 -1.33013,-1.418157 -1.78,-2.28 -0.47812,-0.903522 -0.77374,-1.892313 -0.87,-2.91 h 19.59 v -1.52 c 0.0166,-1.555338 -0.27566,-3.098506 -0.86,-4.54 -0.54053,-1.333176 -1.33916,-2.54641 -2.35,-3.57 z m -16.28,6.67 c 0.18109,-0.958759 0.51895,-1.881119 1,-2.73 0.47186,-0.820757 1.07675,-1.557447 1.79,-2.18 0.72195,-0.61779 1.5482,-1.102022 2.44,-1.43 0.95944,-0.356614 1.97651,-0.532906 3,-0.52 4.04346,-0.224227 7.5255,2.82256 7.84,6.86 z M 158.82,28 c -0.83726,-0.883328 -1.8626,-1.566885 -3,-2 -1.25447,-0.462049 -2.58329,-0.689169 -3.92,-0.67 -1.60057,-0.03131 -3.18086,0.362037 -4.58,1.14 -1.28188,0.720594 -2.36173,1.752297 -3.14,3 v -3.65 h -3.29 V 48 h 3.37 V 35.67 c -0.0102,-1.001391 0.16968,-1.995625 0.53,-2.93 0.3373,-0.856524 0.84023,-1.638106 1.48,-2.3 0.62704,-0.649648 1.38331,-1.160636 2.22,-1.5 0.87089,-0.363534 1.8063,-0.547214 2.75,-0.54 1.87023,-0.128793 3.70135,0.578019 5,1.93 1.22147,1.441484 1.85048,3.292756 1.76,5.18 V 48 h 3.41 V 35.34 c 0.0211,-1.424123 -0.20214,-2.84132 -0.66,-4.19 -0.40985,-1.176324 -1.06809,-2.250653 -1.93,-3.15 z m 27,-12.42 v 14.1 c -0.43264,-0.685249 -0.96517,-1.302051 -1.58,-1.83 -0.60967,-0.53196 -1.28117,-0.98858 -2,-1.36 -0.73088,-0.369676 -1.5029,-0.651634 -2.3,-0.84 -0.78611,-0.187908 -1.59174,-0.281898 -2.4,-0.28 -1.50724,-0.0078 -3.00162,0.277523 -4.4,0.84 -1.34071,0.551089 -2.56038,1.35967 -3.59,2.38 -1.03697,1.047216 -1.85907,2.287165 -2.42,3.65 -1.17023,2.996466 -1.17023,6.323534 0,9.32 0.55964,1.361695 1.37424,2.603955 2.4,3.66 1.02081,1.031107 2.2428,1.841226 3.59,2.38 1.40561,0.561607 2.90636,0.846817 4.42,0.84 0.80981,-0.0026 1.6161,-0.106786 2.4,-0.31 0.79636,-0.199929 1.56783,-0.488392 2.3,-0.86 0.72123,-0.371416 1.39312,-0.831661 2,-1.37 0.61025,-0.540083 1.14205,-1.162767 1.58,-1.85 v 4 h 3.33 V 15.59 Z m -0.37,24.58 c -1.76276,4.229524 -6.6195,6.23041 -10.85,4.47 v 0 c -0.97862,-0.401365 -1.86378,-1.000551 -2.6,-1.76 -0.7522,-0.76312 -1.34086,-1.671634 -1.73,-2.67 -0.41974,-1.066531 -0.63023,-2.203893 -0.62,-3.35 -0.0103,-1.129892 0.20027,-2.250911 0.62,-3.3 0.39328,-0.993283 0.98151,-1.897738 1.73,-2.66 0.74207,-0.76001 1.62521,-1.368023 2.6,-1.79 2.07874,-0.890012 4.43126,-0.890012 6.51,0 0.98149,0.434716 1.87338,1.048526 2.63,1.81 0.74927,0.763509 1.33458,1.672102 1.72,2.67 0.41464,1.036611 0.62516,2.14355 0.62,3.26 -1.3e-4,1.141508 -0.22084,2.272237 -0.65,3.33 z"
|
||||
id="path2"
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:currentColor"
|
||||
sodipodi:nodetypes="cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc" />
|
||||
</symbol>
|
||||
<use
|
||||
xlink:href="#logo"
|
||||
id="use5" />
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<symbol id="logo">
|
||||
<path d="m 105.17,33.27 c -1.04895,-0.638175 -2.18377,-1.123082 -3.37,-1.44 -1.25,-0.34 -2.46,-0.63 -3.63,-0.88 l -3.08,-0.7 C 94.22073,30.069182 93.37751,29.78027 92.58,29.39 91.90449,29.074134 91.31719,28.596738 90.87,28 c -0.43741,-0.644047 -0.65489,-1.412243 -0.62,-2.19 -0.0406,-1.405196 0.53693,-2.75754 1.58,-3.7 1.06,-1 2.73,-1.44 5,-1.44 1.76437,-0.07198 3.51559,0.332147 5.07,1.17 1.35935,0.80694 2.51833,1.911219 3.39,3.23 l 2.79,-2.18 c -1.26761,-1.5933 -2.84201,-2.916072 -4.63,-3.89 -2.04373,-1.017745 -4.30804,-1.512526 -6.59,-1.44 -1.40785,-0.02195 -2.80876,0.201387 -4.14,0.66 -1.16063,0.399115 -2.24085,1.001871 -3.19,1.78 -0.8713,0.712445 -1.5718,1.611145 -2.05,2.63 -0.4819,1.011666 -0.72807,2.119452 -0.72,3.24 -0.05,1.231532 0.24064,2.452997 0.84,3.53 0.55827,0.895068 1.31002,1.653654 2.2,2.22 0.94422,0.612326 1.97599,1.077636 3.06,1.38 1.13,0.32 2.29,0.6 3.47,0.84 l 3.26,0.74 c 0.96945,0.22193 1.90929,0.557589 2.8,1 0.77256,0.367753 1.45522,0.900225 2,1.56 0.51019,0.701297 0.77072,1.553301 0.74,2.42 0.0438,1.566414 -0.62122,3.069031 -1.81,4.09 -1.52512,1.147855 -3.41702,1.699065 -5.32,1.55 -4.03416,0.15747 -7.83041,-1.90763 -9.89,-5.38 l -3,2.34 c 1.3876,1.880136 3.1735,3.430427 5.23,4.54 2.3855,1.197767 5.03194,1.782045 7.7,1.7 1.49114,0.02151 2.97422,-0.222285 4.38,-0.72 1.21788,-0.44929 2.33816,-1.128248 3.3,-2 0.88604,-0.797749 1.60053,-1.767412 2.1,-2.85 0.48895,-1.06318 0.74142,-2.219779 0.74,-3.39 0.0397,-1.336553 -0.30755,-2.656119 -1,-3.8 -0.62101,-0.95962 -1.44763,-1.769154 -2.42,-2.37 z m 27.51,-4.72 c -1.0207,-1.016684 -2.23916,-1.813109 -3.58,-2.34 -1.42831,-0.567565 -2.95311,-0.852828 -4.49,-0.84 -1.58532,-0.01887 -3.15769,0.287432 -4.62,0.9 -1.3691,0.572827 -2.61257,1.408599 -3.66,2.46 -2.1451,2.217513 -3.33989,5.184759 -3.33,8.27 -0.0138,1.54162 0.26439,3.071916 0.82,4.51 0.5255,1.363982 1.32922,2.603618 2.36,3.64 1.06096,1.043663 2.31862,1.866239 3.7,2.42 1.53222,0.610739 3.17082,0.909903 4.82,0.88 2.13421,0.08534 4.25095,-0.416179 6.12,-1.45 1.69947,-1.049265 3.13073,-2.480527 4.18,-4.18 l -2.88,-1.69 c -1.41279,2.768876 -4.32635,4.443291 -7.43,4.27 -1.09666,0.02103 -2.18793,-0.158593 -3.22,-0.53 -0.93382,-0.341463 -1.79784,-0.849713 -2.55,-1.5 -0.72694,-0.645531 -1.33013,-1.418157 -1.78,-2.28 -0.47812,-0.903522 -0.77374,-1.892313 -0.87,-2.91 h 19.59 v -1.52 c 0.0166,-1.555338 -0.27566,-3.098506 -0.86,-4.54 -0.54053,-1.333176 -1.33916,-2.54641 -2.35,-3.57 z m -16.28,6.67 c 0.18109,-0.958759 0.51895,-1.881119 1,-2.73 0.47186,-0.820757 1.07675,-1.557447 1.79,-2.18 0.72195,-0.61779 1.5482,-1.102022 2.44,-1.43 0.95944,-0.356614 1.97651,-0.532906 3,-0.52 4.04346,-0.224227 7.5255,2.82256 7.84,6.86 z M 158.82,28 c -0.83726,-0.883328 -1.8626,-1.566885 -3,-2 -1.25447,-0.462049 -2.58329,-0.689169 -3.92,-0.67 -1.60057,-0.03131 -3.18086,0.362037 -4.58,1.14 -1.28188,0.720594 -2.36173,1.752297 -3.14,3 v -3.65 h -3.29 V 48 h 3.37 V 35.67 c -0.0102,-1.001391 0.16968,-1.995625 0.53,-2.93 0.3373,-0.856524 0.84023,-1.638106 1.48,-2.3 0.62704,-0.649648 1.38331,-1.160636 2.22,-1.5 0.87089,-0.363534 1.8063,-0.547214 2.75,-0.54 1.87023,-0.128793 3.70135,0.578019 5,1.93 1.22147,1.441484 1.85048,3.292756 1.76,5.18 V 48 h 3.41 V 35.34 c 0.0211,-1.424123 -0.20214,-2.84132 -0.66,-4.19 -0.40985,-1.176324 -1.06809,-2.250653 -1.93,-3.15 z m 27,-12.42 v 14.1 c -0.43264,-0.685249 -0.96517,-1.302051 -1.58,-1.83 -0.60967,-0.53196 -1.28117,-0.98858 -2,-1.36 -0.73088,-0.369676 -1.5029,-0.651634 -2.3,-0.84 -0.78611,-0.187908 -1.59174,-0.281898 -2.4,-0.28 -1.50724,-0.0078 -3.00162,0.277523 -4.4,0.84 -1.34071,0.551089 -2.56038,1.35967 -3.59,2.38 -1.03697,1.047216 -1.85907,2.287165 -2.42,3.65 -1.17023,2.996466 -1.17023,6.323534 0,9.32 0.55964,1.361695 1.37424,2.603955 2.4,3.66 1.02081,1.031107 2.2428,1.841226 3.59,2.38 1.40561,0.561607 2.90636,0.846817 4.42,0.84 0.80981,-0.0026 1.6161,-0.106786 2.4,-0.31 0.79636,-0.199929 1.56783,-0.488392 2.3,-0.86 0.72123,-0.371416 1.39312,-0.831661 2,-1.37 0.61025,-0.540083 1.14205,-1.162767 1.58,-1.85 v 4 h 3.33 V 15.59 Z m -0.37,24.58 c -1.76276,4.229524 -6.6195,6.23041 -10.85,4.47 v 0 c -0.97862,-0.401365 -1.86378,-1.000551 -2.6,-1.76 -0.7522,-0.76312 -1.34086,-1.671634 -1.73,-2.67 -0.41974,-1.066531 -0.63023,-2.203893 -0.62,-3.35 -0.0103,-1.129892 0.20027,-2.250911 0.62,-3.3 0.39328,-0.993283 0.98151,-1.897738 1.73,-2.66 0.74207,-0.76001 1.62521,-1.368023 2.6,-1.79 2.07874,-0.890012 4.43126,-0.890012 6.51,0 0.98149,0.434716 1.87338,1.048526 2.63,1.81 0.74927,0.763509 1.33458,1.672102 1.72,2.67 0.41464,1.036611 0.62516,2.14355 0.62,3.26 -1.3e-4,1.141508 -0.22084,2.272237 -0.65,3.33 z" fill="currentColor"/>
|
||||
</symbol>
|
||||
<use xlink:href="#logo"/>
|
||||
</svg>
|
||||
|
|
Before Width: | Height: | Size: 6.3 KiB After Width: | Height: | Size: 4.7 KiB |
|
@ -2,4 +2,5 @@ last 2 chrome versions
|
|||
last 2 firefox versions
|
||||
last 2 safari versions
|
||||
last 2 edge versions
|
||||
edge 18
|
||||
firefox esr
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -64,10 +64,10 @@
|
|||
"node": "^12.16.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.12.0",
|
||||
"@babel/plugin-proposal-class-properties": "^7.10.4",
|
||||
"@babel/core": "^7.12.1",
|
||||
"@babel/plugin-proposal-class-properties": "^7.12.1",
|
||||
"@babel/plugin-syntax-dynamic-import": "^7.2.0",
|
||||
"@babel/preset-env": "^7.12.0",
|
||||
"@babel/preset-env": "^7.12.1",
|
||||
"@dannycoates/webcrypto-liner": "^0.1.37",
|
||||
"@fullhuman/postcss-purgecss": "^1.3.0",
|
||||
"@mattiasbuelens/web-streams-polyfill": "0.2.1",
|
||||
|
@ -136,7 +136,7 @@
|
|||
"@dannycoates/express-ws": "^5.0.3",
|
||||
"@fluent/bundle": "^0.13.0",
|
||||
"@fluent/langneg": "^0.3.0",
|
||||
"@google-cloud/storage": "^4.1.1",
|
||||
"@google-cloud/storage": "^5.1.2",
|
||||
"@sentry/node": "^5.26.0",
|
||||
"aws-sdk": "^2.772.0",
|
||||
"body-parser": "^1.19.0",
|
||||
|
@ -145,7 +145,6 @@
|
|||
"configstore": "github:dannycoates/configstore#master",
|
||||
"convict": "^5.2.0",
|
||||
"express": "^4.17.1",
|
||||
"fxa-geodb": "^1.0.4",
|
||||
"helmet": "^3.23.3",
|
||||
"mkdirp": "^0.5.1",
|
||||
"mozlog": "^2.2.0",
|
||||
|
|
|
@ -107,7 +107,7 @@ tooManyArchives =
|
|||
*[other] Namái se permiten { $count } archivos
|
||||
}
|
||||
expiredTitle = Esti enllaz caducó.
|
||||
notSupportedDescription = { -send-brand } nun va funcionar con esti restolador. { -send-short-brand } funciona meyor cola versión última de { -firefox } y cola versión actual de la mayoría de restoladores.
|
||||
notSupportedDescription = { -send-brand } nun va funcionar con esti restolador. { -send-short-brand } funciona meyor cola última versión de { -firefox } y l'actual de la mayoría de restoladores.
|
||||
downloadFirefox = Baxar { -firefox }
|
||||
legalTitle = Avisu de privacidá de { -send-short-brand }
|
||||
legalDateStamp = Versión 1.0, con data del 12 de marzu de 2019
|
||||
|
@ -131,8 +131,8 @@ accountBenefitLargeFiles = Comparti ficheros d'hasta { $size }
|
|||
accountBenefitDownloadCount = Comparti ficheros con más xente
|
||||
accountBenefitTimeLimit =
|
||||
{ $count ->
|
||||
[one] Caltén activos los enllaces demientres 1 día
|
||||
*[other] Caltén activos los enllaces demientres { $count } díes
|
||||
[one] Caltién activos los enllaces demientres 1 día
|
||||
*[other] Caltién activos los enllaces demientres { $count } díes
|
||||
}
|
||||
accountBenefitSync = Xestiona los ficheros compartíos dende cualesquier preséu
|
||||
accountBenefitMoz = Deprendi más tocante a otros servicios de { -mozilla }
|
||||
|
|
|
@ -138,7 +138,7 @@ accountBenefitSync = Διαχειριστείτε τα διαμοιρασμέν
|
|||
accountBenefitMoz = Μάθετε για τις άλλες υπηρεσίες της { -mozilla }
|
||||
signOut = Αποσύνδεση
|
||||
okButton = OK
|
||||
downloadingTitle = Λήψη
|
||||
downloadingTitle = Γίνεται λήψη
|
||||
noStreamsWarning = Αυτό το πρόγραμμα περιήγησης ενδέχεται να μην μπορέσει να αποκρυπτογραφήσει αρχεία αυτού του μεγέθους.
|
||||
noStreamsOptionCopy = Αντιγράψτε το σύνδεσμο για άνοιγμα σε άλλο πρόγραμμα περιήγησης
|
||||
noStreamsOptionFirefox = Δοκιμάστε το αγαπημένο μας πρόγραμμα περιήγησης
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
title = Send
|
||||
siteFeedback = Comentario
|
||||
importingFile = Importando...
|
||||
encryptingFile = Encriptando...
|
||||
decryptingFile = Desencriptando...
|
||||
encryptingFile = Cifrando...
|
||||
decryptingFile = Descifrando...
|
||||
downloadCount =
|
||||
{ $num ->
|
||||
[one] 1 descarga
|
||||
|
@ -20,11 +20,11 @@ downloadButtonLabel = Descargar
|
|||
downloadFinish = Descarga completa
|
||||
fileSizeProgress = ({ $partialSize } de { $totalSize })
|
||||
sendYourFilesLink = Prueba Send
|
||||
errorPageHeader = ¡Se produjo un error!
|
||||
errorPageHeader = ¡Se ha producido un error!
|
||||
fileTooBig = Ese archivo es muy grande. Debería ocupar menos de { $size }.
|
||||
linkExpiredAlt = Enlace caducado
|
||||
notSupportedHeader = Tu navegador no está admitido.
|
||||
notSupportedLink = ¿Por qué no se admite mi navegador?
|
||||
notSupportedHeader = Tu navegador no es compatible.
|
||||
notSupportedLink = ¿Por qué mi navegador no es compatible?
|
||||
notSupportedOutdatedDetail = Lamentablemente, esta versión de Firefox no admite la tecnología web que impulsa Send. Tendrás que actualizar tu navegador.
|
||||
updateFirefox = Actualizar Firefox
|
||||
deletePopupCancel = Cancelar
|
||||
|
@ -32,7 +32,7 @@ deleteButtonHover = Eliminar
|
|||
footerLinkLegal = Legal
|
||||
footerLinkPrivacy = Privacidad
|
||||
footerLinkCookies = Cookies
|
||||
passwordTryAgain = Contraseña incorrecta. Inténtelo de nuevo.
|
||||
passwordTryAgain = Contraseña incorrecta. Inténtalo de nuevo.
|
||||
javascriptRequired = Send requiere JavaScript
|
||||
whyJavascript = ¿Por qué Send requiere JavaScript?
|
||||
enableJavascript = Por favor, activa JavaScript y vuelve a intentarlo.
|
||||
|
|
|
@ -51,7 +51,7 @@ passwordSetError = Ndaikatúi oikóvo ko ñe’ẽñemi
|
|||
-send-short-brand = Send
|
||||
-firefox = Firefox
|
||||
-mozilla = Mozilla
|
||||
introTitle = Marandurenda ñemoambue hasy'ỹ ha ñemiguáva
|
||||
introTitle = Marandurenda ñemoambue hasy’ỹ ha ñemiguáva
|
||||
introDescription = { -send-brand } omoherakuãkuaa marandurenda papapýpe ñepyrũ guive opa peve ha juajuha opareíva ijehegui. Ikatu oreko ñemihápe emoherakuãva ha ehecháta mba’éicha ne mba’ekuéra noĩri ñandutípe opa ára.
|
||||
notifyUploadEncryptDone = Ne marandurenda oñemo’ã ha ikatúma emondo
|
||||
# downloadCount is from the downloadCount string and timespan is a timespanMinutes string. ex. 'Expires after 2 downloads or 25 minutes'
|
||||
|
@ -135,7 +135,7 @@ accountBenefitTimeLimit =
|
|||
*[other] Eguereko juajuha hendyhápe { $count } ára
|
||||
}
|
||||
accountBenefitSync = Eñangareko marandurenda moherakuãmbyrére oimeraẽ mba’e’oka guive.
|
||||
accountBenefitMoz = Eikuaa ambue { -mozilla } mba'epytyvõrã
|
||||
accountBenefitMoz = Eikuaa ambue { -mozilla } mba’epytyvõrã
|
||||
signOut = Emboty tembiapo
|
||||
okButton = OK
|
||||
downloadingTitle = Oñemboguejyhína
|
||||
|
@ -148,6 +148,6 @@ downloadFirefoxPromo = Ipyahúva { -firefox } ome’ẽse ndéve { -send-short-b
|
|||
shareLinkDescription = Emoherakuã juajuha ne mba’e’oka ndive:
|
||||
shareLinkButton = Emoherakuã juajuha
|
||||
# $name is the name of the file
|
||||
shareMessage = Emboguejy “{ $name }” { -send-brand } ndive: emoherakuã marandurenda tasy'ỹ ha tekorosãme
|
||||
shareMessage = Emboguejy “{ $name }” { -send-brand } ndive: emoherakuã marandurenda tasy’ỹ ha tekorosãme
|
||||
trailheadPromo = Mba’éichapa emo’ãta ne ñemigua. Eipuru Firefox.
|
||||
learnMore = Kuaave.
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
# Send is a brand name and should not be localized.
|
||||
title = Zipu
|
||||
siteFeedback = Nzaghachi
|
||||
importingFile = Mbubata…
|
||||
|
@ -30,7 +29,12 @@ notSupportedOutdatedDetail = Ọ dị nwute na ụdị Firefox a anaghị akwado
|
|||
updateFirefox = Melite Firefox
|
||||
deletePopupCancel = Kagbuo
|
||||
deleteButtonHover = Hichapụ
|
||||
whyJavascript = Kedu ihe kpatara Send jiri chọ JavaScript?
|
||||
footerLinkLegal = n'Iwu
|
||||
footerLinkPrivacy = nzuzo
|
||||
footerLinkCookies = Kuki ga
|
||||
passwordTryAgain = okwuntughe ezighi ezi.Nwaa ọzọ
|
||||
javascriptRequired = Zipu chọrọ
|
||||
whyJavascript = Kedu ihe kpatara Zipu jiri chọ JavaScript?
|
||||
enableJavascript = Biko họrọ JavaScript ma nwaa ọzọ
|
||||
# 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
|
||||
|
@ -43,11 +47,12 @@ passwordSetError = Enweghị ike ịtọ paswọọdụ a
|
|||
|
||||
## Send version 2 strings
|
||||
|
||||
-send-brand = Send
|
||||
-send-brand = Zipu
|
||||
-send-short-brand = Zipu, Ziga
|
||||
-firefox = Firefox
|
||||
-mozilla = Mozilla
|
||||
introTitle = Mfe, nkekọrịta faịlụ nkeonwe
|
||||
introDescription = na-ahapu gị ịkekọrịta faịlụ na izo ya na njedebe na njedebe na-akwụsị na akpaghị aka. Yabụ ị nwere ike idobe ihe ị na -eche ma hụ na ngwongwo gị agaghị adị n'ịntanetị ruo mgbe ebighi ebi.
|
||||
notifyUploadEncryptDone = Failu gi zoro ezo ma di njikere iziga
|
||||
# downloadCount is from the downloadCount string and timespan is a timespanMinutes string. ex. 'Expires after 2 downloads or 25 minutes'
|
||||
archiveExpiryInfo = Ọ ga-agwu mgbe { $downloadCount } ma ọ bụ { $timespan } gasịrị
|
||||
|
@ -61,4 +66,27 @@ timespanWeeks =
|
|||
[one] 1 izu
|
||||
*[other] izu { $num }
|
||||
}
|
||||
# byte abbreviation
|
||||
bytes = B
|
||||
# kibibyte abbreviation
|
||||
kb = KB
|
||||
# mebibyte abbreviation
|
||||
mb = MB
|
||||
# gibibyte abbreviation
|
||||
gb = GB
|
||||
# localized number and byte abbreviation. example "2.5MB"
|
||||
fileSize = { $Number } { $nkeji }
|
||||
# $size is the size of the file, displayed using the fileSize message as format (e.g. "2.5MB")
|
||||
totalSize = { $nha }
|
||||
# the next line after the colon contains a file name
|
||||
copyLinkDescription = Detuo njikọ ahụ iji kee faịlụ gị
|
||||
copyLinkButton = Detuo njikọ
|
||||
downloadTitle = Budata faịlụ gasi
|
||||
downloadDescription = Nkekọrịta faịlụ a site na site na iji zoo njedebe na-njedebe yana otu njikọ na-akwụsị na-akpaghị aka.
|
||||
trySendDescription = Gbalịa maka nyefe faịlụ dị mfe.
|
||||
expiredTitle = Njikọ a emebiela.
|
||||
notSupportedDescription = agaghị eji ihe nchọgharị a rụọ ọrụ. na arụ ọrụ kacha mma na ụdị nke , ọ ga-arụkwa ụdị nke ihe nchọgharị ka ugbu a.
|
||||
downloadFirefox = Budata
|
||||
legalTitle = Nkwupụta Nzuzo
|
||||
legalDateStamp = 1.dị 1.0, akara ụbọchị Maachi 12, 2019
|
||||
okButton = O
|
||||
|
|
|
@ -74,7 +74,7 @@ timespanWeeks =
|
|||
fileCount =
|
||||
{ $num ->
|
||||
[one] 1 n ufaylu
|
||||
*[other] { $num } n ifuyla
|
||||
*[other] { $num } n yifuyla
|
||||
}
|
||||
# byte abbreviation
|
||||
bytes = B
|
||||
|
|
|
@ -96,7 +96,7 @@ tooManyArchives =
|
|||
expiredTitle = 이 링크는 만료되었습니다.
|
||||
notSupportedDescription = { -send-brand }는 이 브라우저와 작동하지 않습니다. { -send-short-brand }는 최신 { -firefox }와 가장 잘 작동하며, 대부분의 최신 웹 브라우저와도 잘 작동합니다.
|
||||
downloadFirefox = { -firefox } 다운로드
|
||||
legalTitle = { -send-short-brand } 개인정보 보호 공지
|
||||
legalTitle = { -send-short-brand } 개인정보처리방침
|
||||
legalDateStamp = 버전 1.0, 2019년 3월 12일자
|
||||
# A short representation of a countdown timer containing the number of days, hours, and minutes remaining as digits, example "2d 11h 56m"
|
||||
expiresDaysHoursMinutes = { $days }일 { $hours }시간 { $minutes }분
|
||||
|
|
|
@ -2,7 +2,7 @@ title = Send
|
|||
siteFeedback = Feedback
|
||||
importingFile = Importeren…
|
||||
encryptingFile = Versleutelen…
|
||||
decryptingFile = Ontcijferen…
|
||||
decryptingFile = Ontsleutelen…
|
||||
downloadCount =
|
||||
{ $num ->
|
||||
[one] 1 download
|
||||
|
|
|
@ -158,5 +158,5 @@ shareLinkDescription = Partajează linkul către fișier:
|
|||
shareLinkButton = Partajează linkul
|
||||
# $name is the name of the file
|
||||
shareMessage = Descarcă „{ $name }” cu { -send-brand }: partajare simplă și sigură a fișierelor
|
||||
trailheadPromo = Există o modalitate de a-ți proteja viața privată. Alătură-te Firefox.
|
||||
trailheadPromo = Există o modalitate de a-ți proteja viața privată. Folosește Firefox.
|
||||
learnMore = Află mai multe.
|
||||
|
|
|
@ -117,7 +117,7 @@ tooManyArchives =
|
|||
expiredTitle = Срок действия этой ссылки истёк.
|
||||
notSupportedDescription = { -send-brand } не будет работать в этом браузере. { -send-short-brand } лучше всего работает с последней версией { -firefox }, и будет работать с последними версиями популярных браузеров.
|
||||
downloadFirefox = Загрузить { -firefox }
|
||||
legalTitle = Политика приватности { -send-short-brand }
|
||||
legalTitle = Уведомление о конфиденциальности { -send-short-brand }
|
||||
legalDateStamp = Версия 1.0, от 12 марта 2019 года
|
||||
# A short representation of a countdown timer containing the number of days, hours, and minutes remaining as digits, example "2d 11h 56m"
|
||||
expiresDaysHoursMinutes = { $days } дн. { $hours } ч. { $minutes } мин.
|
||||
|
|
|
@ -51,6 +51,9 @@ passwordSetError = ఈ సంకేతపదం పెట్టలేకపో
|
|||
-send-short-brand = పంపించు
|
||||
-firefox = Firefox
|
||||
-mozilla = Mozilla
|
||||
notifyUploadEncryptDone = మీ ఫైలు గుప్తీకరించబడింది, పంపడానికి సిద్ధంగా ఉంది
|
||||
# downloadCount is from the downloadCount string and timespan is a timespanMinutes string. ex. 'Expires after 2 downloads or 25 minutes'
|
||||
archiveExpiryInfo = { $downloadCount } లేదా { $timespan } తర్వాత కాలంచెల్లుతుంది
|
||||
timespanMinutes =
|
||||
{ $num ->
|
||||
[one] 1 నిమిషం
|
||||
|
@ -109,6 +112,10 @@ accountBenefitTitle = ఒక { -firefox } ఖాతాని సృష్టి
|
|||
# $size is the size of the file, displayed using the fileSize message as format (e.g. "2.5MB")
|
||||
accountBenefitLargeFiles = { $size } పరిమాణం ఫైళ్ళ వరకు పంచుకోండి
|
||||
accountBenefitDownloadCount = ఫైళ్లను ఎక్కువ మందితో పంచుకోండి
|
||||
accountBenefitTimeLimit =
|
||||
{ $count ->
|
||||
*[other] లంకెలను { $count } రోజుల వరకు చేతనంగా ఉంచు
|
||||
}
|
||||
accountBenefitSync = ఏదైనా పరికరం నుండి పంచుకున్న ఫైళ్ళను నిర్వహించండి
|
||||
accountBenefitMoz = ఇతర { -mozilla } సేవల గురించి తెలుసుకోండి
|
||||
signOut = నిష్క్రమించు
|
||||
|
@ -122,4 +129,7 @@ downloadFirefoxPromo = { -send-short-brand } క్రొత్త { -firefox }
|
|||
# the next line after the colon contains a file name
|
||||
shareLinkDescription = మీ ఫైలుకు లంకెను పంచుకోండి:
|
||||
shareLinkButton = లంకెను పంచుకోండి
|
||||
# $name is the name of the file
|
||||
shareMessage = “{ $name }”ని { -send-brand }తో దించుకోండి: తేలికైన, సురక్షితమైన ఫైలు పంచుకోలు సేవ
|
||||
trailheadPromo = మీ అంతరంగికతను కాపాడుకోడానికి ఓ మార్గం ఉంది. Firefoxతో చేరండి.
|
||||
learnMore = ఇంకా తెలుసుకోండి.
|
||||
|
|
|
@ -3,6 +3,16 @@ siteFeedback = abo
|
|||
importingFile = akowọle…
|
||||
encryptingFile = Fifi ọrọ ṣiṣẹ…
|
||||
decryptingFile = Ti nkọ nkan…
|
||||
downloadCount =
|
||||
{ $num ->
|
||||
[one] ìsíwá kan…
|
||||
*[other] ọ̀pọ̀ ìsíwá…
|
||||
}
|
||||
timespanHours =
|
||||
{ $num ->
|
||||
[one] Wákàtí kan
|
||||
*[other] Ọ̀pọ wákàtí
|
||||
}
|
||||
copiedUrl = dakọ
|
||||
unlockInputPlaceholder =
|
||||
aṣínà
|
||||
|
@ -85,3 +95,23 @@ passwordSetError =
|
|||
introTitle =
|
||||
Fáìlì pípín níkọ̀kọ̀ tó dẹrùn
|
||||
Fáìlì pípín níkọ̀kọ̀ onírọ̀rùn
|
||||
# byte abbreviation
|
||||
bytes = B
|
||||
# kibibyte abbreviation
|
||||
kb = Kilobaiti
|
||||
# mebibyte abbreviation
|
||||
mb = Megabaiti
|
||||
# gibibyte abbreviation
|
||||
gb = Gigabaiti
|
||||
downloadTitle = Se igabisile faili
|
||||
addFilesButton = E yan awon faili lati gbasoke
|
||||
# the first part of the string 'Drag and drop files or click to send up to 1GB'
|
||||
dragAndDropFiles = E mu awon faili ki ede ju si bi
|
||||
emailPlaceholder = E fi imeli si
|
||||
accountBenefitDownloadCount = E pin faili pelu awon eyan si
|
||||
okButton = O DA
|
||||
downloadingTitle = N se igabsile
|
||||
noStreamsOptionFirefox = E gbiyanju asawakiri to je ayanfe wa
|
||||
noStreamsOptionDownload = Tesiwaju pelu aṣàwákiri yi
|
||||
trailheadPromo = Ona wa lati dabobo ipamo re. Darapo mo Firefox
|
||||
learnMore = Keeko si
|
||||
|
|
|
@ -3,10 +3,6 @@ const fetch = require('node-fetch');
|
|||
const config = require('./config');
|
||||
const pkg = require('../package.json');
|
||||
|
||||
const geoip = config.ip_db
|
||||
? require('fxa-geodb')({ dbPath: config.ip_db })
|
||||
: () => ({});
|
||||
|
||||
const HOUR = 1000 * 60 * 60;
|
||||
|
||||
function truncateToHour(timestamp) {
|
||||
|
@ -24,20 +20,11 @@ function userId(fileId, ownerId) {
|
|||
return hash.digest('hex').substring(32);
|
||||
}
|
||||
|
||||
function location(ip) {
|
||||
try {
|
||||
return geoip(ip);
|
||||
} catch (e) {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
function statUploadEvent(data) {
|
||||
const loc = location(data.ip);
|
||||
const event = {
|
||||
session_id: -1,
|
||||
country: loc.country,
|
||||
region: loc.state,
|
||||
country: data.country,
|
||||
region: data.state,
|
||||
user_id: userId(data.id, data.owner),
|
||||
app_version: pkg.version,
|
||||
time: truncateToHour(Date.now()),
|
||||
|
@ -57,11 +44,10 @@ function statUploadEvent(data) {
|
|||
}
|
||||
|
||||
function statDownloadEvent(data) {
|
||||
const loc = location(data.ip);
|
||||
const event = {
|
||||
session_id: -1,
|
||||
country: loc.country,
|
||||
region: loc.state,
|
||||
country: data.country,
|
||||
region: data.state,
|
||||
user_id: userId(data.id, data.owner),
|
||||
app_version: pkg.version,
|
||||
time: truncateToHour(Date.now()),
|
||||
|
@ -77,11 +63,10 @@ function statDownloadEvent(data) {
|
|||
}
|
||||
|
||||
function statDeleteEvent(data) {
|
||||
const loc = location(data.ip);
|
||||
const event = {
|
||||
session_id: -1,
|
||||
country: loc.country,
|
||||
region: loc.state,
|
||||
country: data.country,
|
||||
region: data.state,
|
||||
user_id: userId(data.id, data.owner),
|
||||
app_version: pkg.version,
|
||||
time: truncateToHour(Date.now()),
|
||||
|
@ -96,8 +81,16 @@ function statDeleteEvent(data) {
|
|||
return sendBatch([event]);
|
||||
}
|
||||
|
||||
function clientEvent(event, ua, language, session_id, deltaT, platform, ip) {
|
||||
const loc = location(ip);
|
||||
function clientEvent(
|
||||
event,
|
||||
ua,
|
||||
language,
|
||||
session_id,
|
||||
deltaT,
|
||||
platform,
|
||||
country,
|
||||
state
|
||||
) {
|
||||
const ep = event.event_properties || {};
|
||||
const up = event.user_properties || {};
|
||||
const event_properties = {
|
||||
|
@ -133,7 +126,7 @@ function clientEvent(event, ua, language, session_id, deltaT, platform, ip) {
|
|||
};
|
||||
return {
|
||||
app_version: pkg.version,
|
||||
country: loc.country,
|
||||
country: country,
|
||||
device_id: event.device_id,
|
||||
event_properties,
|
||||
event_type: event.event_type,
|
||||
|
@ -141,7 +134,7 @@ function clientEvent(event, ua, language, session_id, deltaT, platform, ip) {
|
|||
os_name: ua.os.name,
|
||||
os_version: ua.os.version,
|
||||
platform,
|
||||
region: loc.state,
|
||||
region: state,
|
||||
session_id,
|
||||
time: event.time + deltaT,
|
||||
user_id: event.user_id,
|
||||
|
|
|
@ -70,6 +70,10 @@ module.exports = {
|
|||
const token = authHeader.split(' ')[1];
|
||||
req.user = await fxa.verify(token);
|
||||
}
|
||||
return next();
|
||||
if (req.user) {
|
||||
next();
|
||||
} else {
|
||||
res.sendStatus(401);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
@ -11,6 +11,8 @@ module.exports = async function(req, res) {
|
|||
statDeleteEvent({
|
||||
id,
|
||||
ip: req.ip,
|
||||
country: req.geo.country,
|
||||
state: req.geo.state,
|
||||
owner: meta.owner,
|
||||
download_count: meta.dl,
|
||||
ttl,
|
||||
|
|
|
@ -7,6 +7,7 @@ module.exports = async function(req, res) {
|
|||
const id = req.params.id;
|
||||
try {
|
||||
const meta = req.meta;
|
||||
const contentLength = await storage.length(id);
|
||||
const fileStream = await storage.get(id);
|
||||
let cancelled = false;
|
||||
|
||||
|
@ -15,6 +16,10 @@ module.exports = async function(req, res) {
|
|||
fileStream.destroy();
|
||||
});
|
||||
|
||||
res.writeHead(200, {
|
||||
'Content-Type': 'application/octet-stream',
|
||||
'Content-Length': contentLength
|
||||
});
|
||||
fileStream.pipe(res).on('finish', async () => {
|
||||
if (cancelled) {
|
||||
return;
|
||||
|
@ -26,6 +31,8 @@ module.exports = async function(req, res) {
|
|||
statDownloadEvent({
|
||||
id,
|
||||
ip: req.ip,
|
||||
country: req.geo.country,
|
||||
state: req.geo.state,
|
||||
owner: meta.owner,
|
||||
download_count: dl,
|
||||
ttl,
|
||||
|
|
|
@ -13,9 +13,6 @@ function id(user, kid) {
|
|||
|
||||
module.exports = {
|
||||
async get(req, res) {
|
||||
if (!req.user) {
|
||||
return res.sendStatus(401);
|
||||
}
|
||||
const kid = req.params.id;
|
||||
try {
|
||||
const fileId = id(req.user, kid);
|
||||
|
@ -32,9 +29,6 @@ module.exports = {
|
|||
},
|
||||
|
||||
async post(req, res) {
|
||||
if (!req.user) {
|
||||
return res.sendStatus(401);
|
||||
}
|
||||
const kid = req.params.id;
|
||||
try {
|
||||
const limiter = new Limiter(1024 * 1024 * 10);
|
||||
|
|
|
@ -32,55 +32,54 @@ module.exports = function(app) {
|
|||
});
|
||||
if (!IS_DEV) {
|
||||
let csp = {
|
||||
directives: {
|
||||
defaultSrc: ["'self'"],
|
||||
connectSrc: [
|
||||
"'self'",
|
||||
'wss://*.dev.lcip.org',
|
||||
'wss://*.send.nonprod.cloudops.mozgcp.net',
|
||||
config.base_url.replace(/^https:\/\//, 'wss://'),
|
||||
'https://*.dev.lcip.org',
|
||||
'https://accounts.firefox.com',
|
||||
'https://*.accounts.firefox.com',
|
||||
'https://sentry.prod.mozaws.net'
|
||||
],
|
||||
imgSrc: [
|
||||
"'self'",
|
||||
'https://*.dev.lcip.org',
|
||||
'https://firefoxusercontent.com',
|
||||
'https://secure.gravatar.com'
|
||||
],
|
||||
scriptSrc: [
|
||||
"'self'",
|
||||
function(req) {
|
||||
return `'nonce-${req.cspNonce}'`;
|
||||
}
|
||||
],
|
||||
formAction: ["'none'"],
|
||||
frameAncestors: ["'none'"],
|
||||
objectSrc: ["'none'"],
|
||||
reportUri: '/__cspreport__'
|
||||
}
|
||||
directives: {
|
||||
defaultSrc: ["'self'"],
|
||||
connectSrc: [
|
||||
"'self'",
|
||||
'wss://*.dev.lcip.org',
|
||||
'wss://*.send.nonprod.cloudops.mozgcp.net',
|
||||
config.base_url.replace(/^https:\/\//, 'wss://'),
|
||||
'https://*.dev.lcip.org',
|
||||
'https://accounts.firefox.com',
|
||||
'https://*.accounts.firefox.com',
|
||||
'https://sentry.prod.mozaws.net'
|
||||
],
|
||||
imgSrc: [
|
||||
"'self'",
|
||||
'https://*.dev.lcip.org',
|
||||
'https://firefoxusercontent.com',
|
||||
'https://secure.gravatar.com'
|
||||
],
|
||||
scriptSrc: [
|
||||
"'self'",
|
||||
function(req) {
|
||||
return `'nonce-${req.cspNonce}'`;
|
||||
}
|
||||
],
|
||||
formAction: ["'none'"],
|
||||
frameAncestors: ["'none'"],
|
||||
objectSrc: ["'none'"],
|
||||
reportUri: '/__cspreport__'
|
||||
}
|
||||
};
|
||||
|
||||
csp.directives.connectSrc.push(config.base_url.replace(/^https:\/\//,'wss://'))
|
||||
if(config.fxa_csp_oauth_url != ""){
|
||||
csp.directives.connectSrc.push(config.fxa_csp_oauth_url)
|
||||
}
|
||||
if(config.fxa_csp_content_url != "" ){
|
||||
csp.directives.connectSrc.push(config.fxa_csp_content_url)
|
||||
}
|
||||
if(config.fxa_csp_profile_url != "" ){
|
||||
csp.directives.connectSrc.push(config.fxa_csp_profile_url)
|
||||
}
|
||||
if(config.fxa_csp_profileimage_url != ""){
|
||||
csp.directives.imgSrc.push(config.fxa_csp_profileimage_url)
|
||||
}
|
||||
|
||||
|
||||
app.use(
|
||||
helmet.contentSecurityPolicy(csp)
|
||||
csp.directives.connectSrc.push(
|
||||
config.base_url.replace(/^https:\/\//, 'wss://')
|
||||
);
|
||||
if (config.fxa_csp_oauth_url != '') {
|
||||
csp.directives.connectSrc.push(config.fxa_csp_oauth_url);
|
||||
}
|
||||
if (config.fxa_csp_content_url != '') {
|
||||
csp.directives.connectSrc.push(config.fxa_csp_content_url);
|
||||
}
|
||||
if (config.fxa_csp_profile_url != '') {
|
||||
csp.directives.connectSrc.push(config.fxa_csp_profile_url);
|
||||
}
|
||||
if (config.fxa_csp_profileimage_url != '') {
|
||||
csp.directives.imgSrc.push(config.fxa_csp_profileimage_url);
|
||||
}
|
||||
|
||||
app.use(helmet.contentSecurityPolicy(csp));
|
||||
}
|
||||
|
||||
app.use(function(req, res, next) {
|
||||
|
@ -91,6 +90,19 @@ module.exports = function(app) {
|
|||
);
|
||||
next();
|
||||
});
|
||||
app.use(function(req, res, next) {
|
||||
try {
|
||||
// set by the load balancer
|
||||
const [country, state] = req.header('X-Client-Geo-Location').split(',');
|
||||
req.geo = {
|
||||
country,
|
||||
state
|
||||
};
|
||||
} catch (e) {
|
||||
req.geo = {};
|
||||
}
|
||||
next();
|
||||
});
|
||||
app.use(bodyParser.json());
|
||||
app.use(bodyParser.text());
|
||||
app.get('/', language, pages.index);
|
||||
|
|
|
@ -12,7 +12,8 @@ module.exports = async function(req, res) {
|
|||
data.session_id + deltaT,
|
||||
deltaT,
|
||||
data.platform,
|
||||
req.ip
|
||||
req.geo.country,
|
||||
req.geo.state
|
||||
)
|
||||
);
|
||||
const status = await sendBatch(events);
|
||||
|
|
|
@ -56,6 +56,15 @@ module.exports = {
|
|||
|
||||
notfound: async function(req, res) {
|
||||
const appState = await state(req);
|
||||
res.status(404).send(stripEvents(routes().toString('/404', appState)));
|
||||
res
|
||||
.status(404)
|
||||
.send(
|
||||
stripEvents(
|
||||
routes().toString(
|
||||
'/404',
|
||||
Object.assign(appState, { downloadMetadata: { status: 404 } })
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -41,6 +41,14 @@ module.exports = function(ws, req) {
|
|||
? config.max_downloads
|
||||
: config.anon_max_downloads;
|
||||
|
||||
if (config.fxa_required && !user) {
|
||||
ws.send(
|
||||
JSON.stringify({
|
||||
error: 401
|
||||
})
|
||||
);
|
||||
return ws.close();
|
||||
}
|
||||
if (
|
||||
!metadata ||
|
||||
!auth ||
|
||||
|
@ -103,6 +111,8 @@ module.exports = function(ws, req) {
|
|||
statUploadEvent({
|
||||
id: newId,
|
||||
ip: req.ip,
|
||||
country: req.geo.country,
|
||||
state: req.geo.state,
|
||||
owner,
|
||||
dlimit,
|
||||
timeLimit,
|
||||
|
|
|
@ -181,14 +181,15 @@ describe('Upload / Download flow', function() {
|
|||
|
||||
it('can allow multiple downloads', async function() {
|
||||
const fs = new FileSender();
|
||||
const file = await fs.upload(archive);
|
||||
const a = new Archive([blob]);
|
||||
a.dlimit = 2;
|
||||
const file = await fs.upload(a);
|
||||
const fr = new FileReceiver({
|
||||
secretKey: file.toJSON().secretKey,
|
||||
id: file.id,
|
||||
nonce: file.keychain.nonce,
|
||||
requiresPassword: false
|
||||
});
|
||||
await file.changeLimit(2);
|
||||
await fr.getMetadata();
|
||||
await fr.download(options);
|
||||
await file.updateDownloadCount();
|
||||
|
|
|
@ -12,6 +12,7 @@ const webJsOptions = {
|
|||
[
|
||||
'@babel/preset-env',
|
||||
{
|
||||
bugfixes: true,
|
||||
useBuiltIns: 'entry',
|
||||
corejs: 3
|
||||
}
|
||||
|
@ -78,9 +79,9 @@ const serviceWorker = {
|
|||
const web = {
|
||||
target: 'web',
|
||||
entry: {
|
||||
app: ['./app/main.js'],
|
||||
android: ['./android/android.js'],
|
||||
ios: ['./ios/ios.js']
|
||||
app: ['./app/main.js']
|
||||
// android: ['./android/android.js'],
|
||||
// ios: ['./ios/ios.js']
|
||||
},
|
||||
output: {
|
||||
chunkFilename: '[name].[contenthash:8].js',
|
||||
|
|
Loading…
Reference in New Issue