mirror of
https://github.com/timvisee/send.git
synced 2025-10-03 17:49:18 +02:00
Compare commits
104 commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
e0a3d2bf2c | ||
![]() |
b93256316b | ||
![]() |
8a82e53e82 | ||
![]() |
6215bb1555 | ||
![]() |
d7e929a0d1 | ||
![]() |
154a8a90c1 | ||
![]() |
0a6bf39353 | ||
![]() |
c0cf7bbda2 | ||
![]() |
197b0bcf59 | ||
![]() |
56a0e830bd | ||
![]() |
b3317df707 | ||
![]() |
9931074905 | ||
![]() |
0088a4ccc0 | ||
![]() |
11aad6eac5 | ||
![]() |
1fb2f45285 | ||
![]() |
d0f21e8078 | ||
![]() |
c71100c82a | ||
![]() |
4f2dd96708 | ||
![]() |
d8456c2c51 | ||
![]() |
b7324f3a5b | ||
![]() |
6890165f67 | ||
![]() |
5124572dba | ||
![]() |
0c1819bb15 | ||
![]() |
906990991e | ||
![]() |
48bdf734c5 | ||
![]() |
cf63e0e804 | ||
![]() |
ec66c2dc4e | ||
![]() |
67dfc94ef3 | ||
![]() |
f54f3ccaa2 | ||
![]() |
a35e2e58a3 | ||
![]() |
6ad2885a16 | ||
![]() |
70662888b1 | ||
![]() |
9f09a79986 | ||
![]() |
db64c0467a | ||
![]() |
ca52f84aa5 | ||
![]() |
6fff664947 | ||
![]() |
439ac0ab7c | ||
![]() |
dee6b3e9cc | ||
![]() |
dc9d072472 | ||
![]() |
91773832c1 | ||
![]() |
65730db0db | ||
![]() |
a86221b1cc | ||
![]() |
bcc53f73c6 | ||
![]() |
b0444f488b | ||
![]() |
70a11e5300 | ||
![]() |
f62a99882d | ||
![]() |
309c7d63ac | ||
![]() |
1d75366f66 | ||
![]() |
0a849fb7c6 | ||
![]() |
88725df09d | ||
![]() |
5a92e7e5e7 | ||
![]() |
71541fc2b6 | ||
![]() |
c524804c63 | ||
![]() |
5b4c0d2540 | ||
![]() |
e7f3c91d0b | ||
![]() |
8bb198b73e | ||
![]() |
9e188bc76c | ||
![]() |
1353a54c49 | ||
![]() |
4ae007167d | ||
![]() |
660f36e584 | ||
![]() |
3dede083cd | ||
![]() |
26e81455ff | ||
![]() |
4ceac20623 | ||
![]() |
073accfe65 | ||
![]() |
6306a433e8 | ||
![]() |
1da317bcc1 | ||
![]() |
08f597405c | ||
![]() |
c624766edc | ||
![]() |
e030c46a9c | ||
![]() |
d081affa38 | ||
![]() |
71372fcbc1 | ||
![]() |
671390ca24 | ||
![]() |
9221b86660 | ||
![]() |
fd2e954b3e | ||
![]() |
c528ad3147 | ||
![]() |
df9c7ea734 | ||
![]() |
e32ea7d0aa | ||
![]() |
55ad08fd96 | ||
![]() |
96d53e4118 | ||
![]() |
bce861bcaf | ||
![]() |
643287e235 | ||
![]() |
c619be58ae | ||
![]() |
9b8b11ffc3 | ||
![]() |
1725ff434e | ||
![]() |
e1d6224570 | ||
![]() |
38746b86fd | ||
![]() |
64644b57e3 | ||
![]() |
625fdf5bca | ||
![]() |
951c613095 | ||
![]() |
16e78847a2 | ||
![]() |
310271c10f | ||
![]() |
55344f8a9d | ||
![]() |
2b22e8cd05 | ||
![]() |
47ff32fc9f | ||
![]() |
b598a1c090 | ||
![]() |
3ae9e6adeb | ||
![]() |
33e7e0f5ba | ||
![]() |
ca3b5cf7ca | ||
![]() |
44a25e4156 | ||
![]() |
000854104f | ||
![]() |
1a0ddf9a05 | ||
![]() |
0ac1eeed2c | ||
![]() |
2457545502 | ||
![]() |
75637807eb |
44 changed files with 19859 additions and 12520 deletions
137
.gitlab-ci.yml
137
.gitlab-ci.yml
|
@ -1,105 +1,72 @@
|
||||||
image: "node:15-slim"
|
|
||||||
|
|
||||||
stages:
|
stages:
|
||||||
- test
|
- test
|
||||||
- artifact
|
|
||||||
- release
|
- release
|
||||||
|
|
||||||
before_script:
|
|
||||||
# Install dependencies
|
|
||||||
- apt-get update
|
|
||||||
- apt-get install -y git python3 build-essential libxtst6
|
|
||||||
|
|
||||||
# Prepare Chrome for puppeteer
|
|
||||||
- apt-get install -y wget gnupg
|
|
||||||
- wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add -
|
|
||||||
- sh -c 'echo "deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google.list'
|
|
||||||
- apt-get update
|
|
||||||
- apt-get install -y google-chrome-stable fonts-ipafont-gothic fonts-wqy-zenhei fonts-thai-tlwg fonts-kacst fonts-freefont-ttf libxss1 --no-install-recommends
|
|
||||||
|
|
||||||
# Build Send, run npm tests
|
# Build Send, run npm tests
|
||||||
test:
|
test:
|
||||||
stage: test
|
stage: test
|
||||||
|
image: "node:16-slim"
|
||||||
|
only:
|
||||||
|
- api
|
||||||
|
- branches
|
||||||
|
- chat
|
||||||
|
- merge_requests
|
||||||
|
- pushes
|
||||||
|
- schedules
|
||||||
|
- tags
|
||||||
|
- triggers
|
||||||
|
- web
|
||||||
|
before_script:
|
||||||
|
# Install dependencies
|
||||||
|
- apt-get update
|
||||||
|
- apt-get install -y git python3 build-essential libxtst6
|
||||||
|
|
||||||
|
# Prepare Chrome for puppeteer
|
||||||
|
- apt-get install -y wget gnupg
|
||||||
|
- wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add -
|
||||||
|
- sh -c 'echo "deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google.list'
|
||||||
|
- apt-get update
|
||||||
|
- apt-get install -y gconf-service libasound2 libatk1.0-0 libc6 libcairo2 libcups2 libdbus-1-3 libexpat1 libfontconfig1 libgcc1 libgconf-2-4 libgdk-pixbuf2.0-0 libglib2.0-0 libgtk-3-0 libnspr4 libpango-1.0-0 libpangocairo-1.0-0 libstdc++6 libx11-6 libx11-xcb1 libxcb1 libxcomposite1 libxcursor1 libxdamage1 libxext6 libxfixes3 libxi6 libxrandr2 libxrender1 libxss1 libxtst6 ca-certificates fonts-liberation libappindicator1 libnss3 lsb-release xdg-utils
|
||||||
|
- apt-get install -y google-chrome-stable fonts-ipafont-gothic fonts-wqy-zenhei fonts-thai-tlwg fonts-kacst fonts-freefont-ttf libxss1 --no-install-recommends
|
||||||
script:
|
script:
|
||||||
- npm ci
|
- npm ci
|
||||||
- npm run lint
|
- npm run lint
|
||||||
- npm test
|
- npm test
|
||||||
|
|
||||||
# Build Docker image, export Docker image artifact
|
|
||||||
artifact-docker:
|
|
||||||
stage: artifact
|
|
||||||
image: docker:latest
|
|
||||||
needs: []
|
|
||||||
services:
|
|
||||||
- docker:dind
|
|
||||||
variables:
|
|
||||||
IMG_FILE: "send:git-$CI_COMMIT_SHORT_SHA.tar"
|
|
||||||
IMG_NAME: "send:git-$CI_COMMIT_SHORT_SHA"
|
|
||||||
before_script: []
|
|
||||||
script:
|
|
||||||
- docker build -t $IMG_NAME .
|
|
||||||
- docker image save -o $IMG_FILE $IMG_NAME
|
|
||||||
artifacts:
|
|
||||||
name: artifact-docker
|
|
||||||
paths:
|
|
||||||
- $IMG_FILE
|
|
||||||
expire_in: 1 week
|
|
||||||
|
|
||||||
# Release public Docker image for the master branch
|
|
||||||
release-docker-master:
|
|
||||||
stage: release
|
|
||||||
image: docker:latest
|
|
||||||
dependencies:
|
|
||||||
- artifact-docker
|
|
||||||
services:
|
|
||||||
- docker:dind
|
|
||||||
only:
|
|
||||||
- master
|
|
||||||
variables:
|
|
||||||
IMG_IMPORT_FILE: "send:git-$CI_COMMIT_SHORT_SHA.tar"
|
|
||||||
IMG_IMPORT_NAME: "send:git-$CI_COMMIT_SHORT_SHA"
|
|
||||||
IMG_NAME: "registry.gitlab.com/timvisee/send:master-$CI_COMMIT_SHORT_SHA"
|
|
||||||
before_script: []
|
|
||||||
script:
|
|
||||||
# Login in to registry
|
|
||||||
- 'docker login registry.gitlab.com -u $DOCKER_USER -p $DOCKER_PASS'
|
|
||||||
|
|
||||||
# Load existing, retag for new image images
|
|
||||||
- docker image load -i $IMG_IMPORT_FILE
|
|
||||||
- docker tag $IMG_IMPORT_NAME $IMG_NAME
|
|
||||||
|
|
||||||
# Publish tagged image
|
|
||||||
- docker push $IMG_NAME
|
|
||||||
|
|
||||||
- 'echo "Docker image artifact published, available as:" && echo " docker pull $IMG_NAME"'
|
|
||||||
|
|
||||||
# Release public Docker image for a version tag
|
|
||||||
release-docker:
|
release-docker:
|
||||||
stage: release
|
stage: release
|
||||||
image: docker:latest
|
image: docker:latest
|
||||||
dependencies:
|
|
||||||
- artifact-docker
|
|
||||||
services:
|
services:
|
||||||
- docker:dind
|
- docker:dind
|
||||||
only:
|
only:
|
||||||
- /^v(\d+\.)*\d+$/
|
- api
|
||||||
variables:
|
- branches
|
||||||
IMG_IMPORT_FILE: "send:git-$CI_COMMIT_SHORT_SHA.tar"
|
- chat
|
||||||
IMG_IMPORT_NAME: "send:git-$CI_COMMIT_SHORT_SHA"
|
- merge_requests
|
||||||
IMG_NAME: "registry.gitlab.com/timvisee/send:$CI_COMMIT_REF_NAME"
|
- pushes
|
||||||
IMG_NAME_LATEST: "registry.gitlab.com/timvisee/send:latest"
|
- schedules
|
||||||
before_script: []
|
- tags
|
||||||
|
- triggers
|
||||||
|
- web
|
||||||
script:
|
script:
|
||||||
# Login in to registry
|
- docker login "$CI_REGISTRY" -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD"
|
||||||
- 'docker login registry.gitlab.com -u $DOCKER_USER -p $DOCKER_PASS'
|
- docker buildx create --name sendBuilder
|
||||||
|
- docker buildx use sendBuilder
|
||||||
# Load existing, retag for new image images
|
- |
|
||||||
- docker image load -i $IMG_IMPORT_FILE
|
if [ "$CI_PIPELINE_SOURCE" == "merge_request_event" ]; then
|
||||||
- docker tag $IMG_IMPORT_NAME $IMG_NAME
|
IMAGE_NAMES="$CI_REGISTRY_IMAGE/mr:$CI_MERGE_REQUEST_IID"
|
||||||
- docker tag $IMG_IMPORT_NAME $IMG_NAME_LATEST
|
elif [ "$CI_COMMIT_TAG" != "" ]; then
|
||||||
|
IMAGE_NAMES="$CI_REGISTRY_IMAGE:$CI_COMMIT_TAG $CI_REGISTRY_IMAGE:latest"
|
||||||
# Publish tagged image
|
else
|
||||||
- docker push $IMG_NAME
|
IMAGE_NAMES="$CI_REGISTRY_IMAGE/$CI_COMMIT_BRANCH:$CI_COMMIT_SHORT_SHA"
|
||||||
- docker push $IMG_NAME_LATEST
|
fi
|
||||||
|
- |
|
||||||
- 'echo "Docker image artifact published, available as:" && echo " docker pull $IMG_NAME_LATEST" && echo " docker pull $IMG_NAME"'
|
for image in $IMAGE_NAMES; do
|
||||||
|
docker buildx build --platform linux/amd64,linux/arm64 -t $image . --push
|
||||||
|
done
|
||||||
|
- |
|
||||||
|
echo "Container image pushed. You can pull it with";
|
||||||
|
for image in $IMAGE_NAMES; do
|
||||||
|
echo "docker pull $image"
|
||||||
|
done
|
||||||
|
|
|
@ -11,3 +11,5 @@ rules:
|
||||||
selector-list-comma-newline-after: null
|
selector-list-comma-newline-after: null
|
||||||
value-list-comma-newline-after: null
|
value-list-comma-newline-after: null
|
||||||
at-rule-no-unknown: null
|
at-rule-no-unknown: null
|
||||||
|
# Conflicts with prettier
|
||||||
|
string-quotes: null
|
||||||
|
|
|
@ -152,7 +152,7 @@ AWS example using Ubuntu Server `20.04`: [docs/AWS.md](docs/AWS.md)
|
||||||
- Web: _this repository_
|
- Web: _this repository_
|
||||||
- Command-line: [`ffsend`](https://github.com/timvisee/ffsend)
|
- Command-line: [`ffsend`](https://github.com/timvisee/ffsend)
|
||||||
- Android: _see [Android](#android) section_
|
- Android: _see [Android](#android) section_
|
||||||
- Thunderbird: [FileLink provider for Send](https://addons.thunderbird.net/en-US/thunderbird/addon/filelink-provider-for-send/)
|
- Thunderbird: [FileLink provider for Send](https://addons.thunderbird.net/thunderbird/addon/filelink-provider-for-send/)
|
||||||
|
|
||||||
#### Android
|
#### Android
|
||||||
|
|
||||||
|
|
|
@ -43,7 +43,7 @@ function post(obj, bearerToken) {
|
||||||
'Content-Type': 'application/json'
|
'Content-Type': 'application/json'
|
||||||
};
|
};
|
||||||
if (bearerToken) {
|
if (bearerToken) {
|
||||||
h['Authentication'] = `Bearer ${bearerToken}`;
|
h['Authorization'] = `Bearer ${bearerToken}`;
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
|
|
|
@ -17,6 +17,7 @@ export default class Archive {
|
||||||
constructor(files = [], defaultTimeLimit = 86400, defaultDownloadLimit = 1) {
|
constructor(files = [], defaultTimeLimit = 86400, defaultDownloadLimit = 1) {
|
||||||
this.files = Array.from(files);
|
this.files = Array.from(files);
|
||||||
this.defaultTimeLimit = defaultTimeLimit;
|
this.defaultTimeLimit = defaultTimeLimit;
|
||||||
|
this.defaultDownloadLimit = defaultDownloadLimit;
|
||||||
this.timeLimit = defaultTimeLimit;
|
this.timeLimit = defaultTimeLimit;
|
||||||
this.dlimit = defaultDownloadLimit;
|
this.dlimit = defaultDownloadLimit;
|
||||||
this.password = null;
|
this.password = null;
|
||||||
|
@ -76,7 +77,7 @@ export default class Archive {
|
||||||
|
|
||||||
clear() {
|
clear() {
|
||||||
this.files = [];
|
this.files = [];
|
||||||
this.dlimit = 1;
|
this.dlimit = this.defaultDownloadLimit;
|
||||||
this.timeLimit = this.defaultTimeLimit;
|
this.timeLimit = this.defaultTimeLimit;
|
||||||
this.password = null;
|
this.password = null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import { FluentBundle } from '@fluent/bundle';
|
import { FluentBundle, FluentResource } from '@fluent/bundle';
|
||||||
|
|
||||||
function makeBundle(locale, ftl) {
|
function makeBundle(locale, ftl) {
|
||||||
const bundle = new FluentBundle(locale, { useIsolating: false });
|
const bundle = new FluentBundle(locale, { useIsolating: false });
|
||||||
bundle.addMessages(ftl);
|
bundle.addResource(new FluentResource(ftl));
|
||||||
return bundle;
|
return bundle;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,7 +19,7 @@ export async function getTranslator(locale) {
|
||||||
return function(id, data) {
|
return function(id, data) {
|
||||||
for (let bundle of bundles) {
|
for (let bundle of bundles) {
|
||||||
if (bundle.hasMessage(id)) {
|
if (bundle.hasMessage(id)) {
|
||||||
return bundle.format(bundle.getMessage(id), data);
|
return bundle.formatPattern(bundle.getMessage(id).value, data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
57
app/main.css
57
app/main.css
|
@ -7,17 +7,14 @@ html {
|
||||||
@tailwind components;
|
@tailwind components;
|
||||||
|
|
||||||
:not(input) {
|
:not(input) {
|
||||||
-webkit-user-select: none;
|
|
||||||
-moz-user-select: none;
|
|
||||||
-ms-user-select: none;
|
|
||||||
user-select: none;
|
user-select: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
:root {
|
:root {
|
||||||
--violet-gradient: linear-gradient(
|
--violet-gradient: linear-gradient(
|
||||||
-180deg,
|
-180deg,
|
||||||
rgba(144, 89, 255, 0.8) 0%,
|
rgb(144 89 255 / 80%) 0%,
|
||||||
rgba(144, 89, 255, 0.4) 100%
|
rgb(144 89 255 / 40%) 100%
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -71,7 +68,7 @@ body {
|
||||||
|
|
||||||
.checkbox > label::before {
|
.checkbox > label::before {
|
||||||
/* @apply bg-grey-10; */
|
/* @apply bg-grey-10; */
|
||||||
@apply border;
|
@apply border-default;
|
||||||
@apply rounded-sm;
|
@apply rounded-sm;
|
||||||
|
|
||||||
content: '';
|
content: '';
|
||||||
|
@ -170,6 +167,22 @@ footer li a:hover {
|
||||||
width: auto;
|
width: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.text-underline {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
.d-block {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.d-inline-block {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.align-middle {
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
.main {
|
.main {
|
||||||
display: flex;
|
display: flex;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
@ -204,19 +217,18 @@ progress::-webkit-progress-value {
|
||||||
background-image: -webkit-linear-gradient(
|
background-image: -webkit-linear-gradient(
|
||||||
-45deg,
|
-45deg,
|
||||||
transparent 20%,
|
transparent 20%,
|
||||||
rgba(255, 255, 255, 0.4) 20%,
|
rgb(255 255 255 / 40%) 20%,
|
||||||
rgba(255, 255, 255, 0.4) 40%,
|
rgb(255 255 255 / 40%) 40%,
|
||||||
transparent 40%,
|
transparent 40%,
|
||||||
transparent 60%,
|
transparent 60%,
|
||||||
rgba(255, 255, 255, 0.4) 60%,
|
rgb(255 255 255 / 40%) 60%,
|
||||||
rgba(255, 255, 255, 0.4) 80%,
|
rgb(255 255 255 / 40%) 80%,
|
||||||
transparent 80%
|
transparent 80%
|
||||||
),
|
),
|
||||||
-webkit-linear-gradient(left, var(--color-primary), var(--color-primary));
|
-webkit-linear-gradient(left, var(--color-primary), var(--color-primary));
|
||||||
/* stylelint-enable */
|
/* stylelint-enable */
|
||||||
border-radius: 2px;
|
border-radius: 2px;
|
||||||
background-size: 21px 20px, 100% 100%, 100% 100%;
|
background-size: 21px 20px, 100% 100%, 100% 100%;
|
||||||
-webkit-animation: animate-stripes 1s linear infinite;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
progress::-moz-progress-bar {
|
progress::-moz-progress-bar {
|
||||||
|
@ -224,12 +236,12 @@ progress::-moz-progress-bar {
|
||||||
background-image: -moz-linear-gradient(
|
background-image: -moz-linear-gradient(
|
||||||
135deg,
|
135deg,
|
||||||
transparent 20%,
|
transparent 20%,
|
||||||
rgba(255, 255, 255, 0.4) 20%,
|
rgb(255 255 255 / 40%) 20%,
|
||||||
rgba(255, 255, 255, 0.4) 40%,
|
rgb(255 255 255 / 40%) 40%,
|
||||||
transparent 40%,
|
transparent 40%,
|
||||||
transparent 60%,
|
transparent 60%,
|
||||||
rgba(255, 255, 255, 0.4) 60%,
|
rgb(255 255 255 / 40%) 60%,
|
||||||
rgba(255, 255, 255, 0.4) 80%,
|
rgb(255 255 255 / 40%) 80%,
|
||||||
transparent 80%
|
transparent 80%
|
||||||
),
|
),
|
||||||
-moz-linear-gradient(left, var(--color-primary), var(--color-primary));
|
-moz-linear-gradient(left, var(--color-primary), var(--color-primary));
|
||||||
|
@ -239,12 +251,6 @@ progress::-moz-progress-bar {
|
||||||
animation: animate-stripes 1s linear infinite;
|
animation: animate-stripes 1s linear infinite;
|
||||||
}
|
}
|
||||||
|
|
||||||
@-webkit-keyframes animate-stripes {
|
|
||||||
100% {
|
|
||||||
background-position: -21px 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes animate-stripes {
|
@keyframes animate-stripes {
|
||||||
100% {
|
100% {
|
||||||
background-position: -21px 0;
|
background-position: -21px 0;
|
||||||
|
@ -313,7 +319,7 @@ select {
|
||||||
|
|
||||||
@screen md {
|
@screen md {
|
||||||
.main > section {
|
.main > section {
|
||||||
@apply border;
|
@apply border-default;
|
||||||
@apply border-grey-80;
|
@apply border-grey-80;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -323,13 +329,12 @@ select {
|
||||||
|
|
||||||
@responsive {
|
@responsive {
|
||||||
.shadow-light {
|
.shadow-light {
|
||||||
box-shadow: 0 0 8px 0 rgba(12, 12, 13, 0.1);
|
box-shadow: 0 0 8px 0 rgb(12 12 13 / 10%);
|
||||||
}
|
}
|
||||||
|
|
||||||
.shadow-big {
|
.shadow-big {
|
||||||
box-shadow: 0 12px 18px 2px rgba(34, 0, 51, 0.04),
|
box-shadow: 0 12px 18px 2px rgb(34 0 51 / 4%),
|
||||||
0 6px 22px 4px rgba(7, 48, 114, 0.12),
|
0 6px 22px 4px rgb(7 48 114 / 12%), 0 6px 10px -4px rgb(14 13 26 / 12%);
|
||||||
0 6px 10px -4px rgba(14, 13, 26, 0.12);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -83,13 +83,13 @@ class Account extends Component {
|
||||||
<input
|
<input
|
||||||
type="image"
|
type="image"
|
||||||
alt="${user.email}"
|
alt="${user.email}"
|
||||||
class="w-8 h-8 rounded-full border text-primary md:text-white focus:outline"
|
class="w-8 h-8 rounded-full border-default text-primary md:text-white focus:outline"
|
||||||
src="${user.avatar}"
|
src="${user.avatar}"
|
||||||
onclick="${e => this.avatarClick(e)}"
|
onclick="${e => this.avatarClick(e)}"
|
||||||
/>
|
/>
|
||||||
<ul
|
<ul
|
||||||
id="accountMenu"
|
id="accountMenu"
|
||||||
class="invisible absolute top-0 right-0 mt-10 pt-2 pb-2 bg-white shadow-md whitespace-no-wrap outline-none z-50 dark:bg-grey-80"
|
class="invisible absolute top-0 right-0 mt-10 pt-2 pb-2 bg-white shadow-md whitespace-nowrap outline-none z-50 dark:bg-grey-80"
|
||||||
onblur="${e => this.hideMenu(e)}"
|
onblur="${e => this.hideMenu(e)}"
|
||||||
>
|
>
|
||||||
<li class="p-2 text-grey-60 dark:text-grey-50">${user.email}</li>
|
<li class="p-2 text-grey-60 dark:text-grey-50">${user.email}</li>
|
||||||
|
|
|
@ -26,7 +26,7 @@ function expiryInfo(translate, archive) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function password(state) {
|
function password(state) {
|
||||||
const MAX_LENGTH = 32;
|
const MAX_LENGTH = 4096;
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<div class="mb-2 px-1">
|
<div class="mb-2 px-1">
|
||||||
|
@ -53,7 +53,7 @@ function password(state) {
|
||||||
id="password-input"
|
id="password-input"
|
||||||
class="${state.archive.password
|
class="${state.archive.password
|
||||||
? ''
|
? ''
|
||||||
: 'invisible'} border rounded focus:border-primary leading-normal my-1 py-1 px-2 h-8 dark:bg-grey-80"
|
: 'invisible'} border-default rounded-default focus:border-primary leading-normal my-1 py-1 px-2 h-8 dark:bg-grey-80"
|
||||||
autocomplete="off"
|
autocomplete="off"
|
||||||
maxlength="${MAX_LENGTH}"
|
maxlength="${MAX_LENGTH}"
|
||||||
type="password"
|
type="password"
|
||||||
|
@ -261,7 +261,7 @@ module.exports = function(state, emit, archive) {
|
||||||
return html`
|
return html`
|
||||||
<send-archive
|
<send-archive
|
||||||
id="archive-${archive.id}"
|
id="archive-${archive.id}"
|
||||||
class="flex flex-col items-start rounded shadow-light bg-white p-4 w-full dark:bg-grey-90 dark:border dark:border-grey-70"
|
class="flex flex-col items-start rounded-default shadow-light bg-white p-4 w-full dark:bg-grey-90 dark:border-default dark:border-grey-70"
|
||||||
>
|
>
|
||||||
${archiveInfo(
|
${archiveInfo(
|
||||||
archive,
|
archive,
|
||||||
|
@ -335,7 +335,7 @@ module.exports.wip = function(state, emit) {
|
||||||
fileInfo(f, remove(f, state.translate('deleteButtonHover')))
|
fileInfo(f, remove(f, state.translate('deleteButtonHover')))
|
||||||
),
|
),
|
||||||
'flex-shrink bg-grey-10 rounded-t overflow-y-auto px-6 py-4 md:h-full md:max-h-half-screen dark:bg-black',
|
'flex-shrink bg-grey-10 rounded-t overflow-y-auto px-6 py-4 md:h-full md:max-h-half-screen dark:bg-black',
|
||||||
'bg-white px-2 my-2 shadow-light rounded dark:bg-grey-90 dark:border dark:border-grey-80'
|
'bg-white px-2 my-2 shadow-light rounded-default dark:bg-grey-90 dark:border-default dark:border-grey-80'
|
||||||
)}
|
)}
|
||||||
<div
|
<div
|
||||||
class="flex-shrink-0 flex-grow flex items-end p-4 bg-grey-10 rounded-b mb-1 font-medium dark:bg-grey-90"
|
class="flex-shrink-0 flex-grow flex items-end p-4 bg-grey-10 rounded-b mb-1 font-medium dark:bg-grey-90"
|
||||||
|
@ -438,7 +438,7 @@ module.exports.uploading = function(state, emit) {
|
||||||
return html`
|
return html`
|
||||||
<send-upload-area
|
<send-upload-area
|
||||||
id="${archive.id}"
|
id="${archive.id}"
|
||||||
class="flex flex-col items-start rounded shadow-light bg-white p-4 w-full dark:bg-grey-90"
|
class="flex flex-col items-start rounded-default shadow-light bg-white p-4 w-full dark:bg-grey-90"
|
||||||
>
|
>
|
||||||
${archiveInfo(archive)}
|
${archiveInfo(archive)}
|
||||||
<div class="text-xs opacity-75 w-full mt-2 mb-2">
|
<div class="text-xs opacity-75 w-full mt-2 mb-2">
|
||||||
|
@ -486,9 +486,19 @@ module.exports.empty = function(state, emit) {
|
||||||
})}
|
})}
|
||||||
</button>
|
</button>
|
||||||
`;
|
`;
|
||||||
|
const uploadNotice = state.WEB_UI.UPLOAD_AREA_NOTICE_HTML
|
||||||
|
? html`
|
||||||
|
<p
|
||||||
|
class="w-full mt-8 p-2 border-default dark:border-grey-70 rounded-default text-orange-60 bg-yellow-40 text-center leading-normal"
|
||||||
|
>
|
||||||
|
${raw(state.WEB_UI.UPLOAD_AREA_NOTICE_HTML)}
|
||||||
|
</p>
|
||||||
|
`
|
||||||
|
: '';
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<send-upload-area
|
<send-upload-area
|
||||||
class="flex flex-col items-center justify-center border-2 border-dashed border-grey-transparent rounded px-6 py-16 h-full w-full dark:border-grey-60"
|
class="flex flex-col items-center justify-center border-2 border-dashed border-grey-transparent rounded-default px-6 py-16 h-full w-full dark:border-grey-60"
|
||||||
onclick="${e => {
|
onclick="${e => {
|
||||||
if (e.target.tagName !== 'LABEL') {
|
if (e.target.tagName !== 'LABEL') {
|
||||||
document.getElementById('file-upload').click();
|
document.getElementById('file-upload').click();
|
||||||
|
@ -526,7 +536,7 @@ module.exports.empty = function(state, emit) {
|
||||||
>
|
>
|
||||||
${state.translate('addFilesButton')}
|
${state.translate('addFilesButton')}
|
||||||
</label>
|
</label>
|
||||||
${upsell}
|
${upsell} ${uploadNotice}
|
||||||
</send-upload-area>
|
</send-upload-area>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
@ -559,11 +569,43 @@ module.exports.preview = function(state, emit) {
|
||||||
${archiveDetails(state.translate, archive)}
|
${archiveDetails(state.translate, archive)}
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
const notice = state.WEB_UI.DOWNLOAD_NOTICE_HTML
|
||||||
|
? html`
|
||||||
|
<p
|
||||||
|
class="w-full mt-4 p-2 border-default dark:border-grey-70 rounded-default text-orange-60 bg-yellow-40 text-center leading-normal"
|
||||||
|
>
|
||||||
|
${raw(state.WEB_UI.DOWNLOAD_NOTICE_HTML)}
|
||||||
|
</p>
|
||||||
|
`
|
||||||
|
: '';
|
||||||
|
const sponsor = state.WEB_UI.SHOW_THUNDERBIRD_SPONSOR
|
||||||
|
? html`
|
||||||
|
<a
|
||||||
|
class="w-full mt-5 mb-2 p-2 border-default dark:border-grey-70 rounded-default text-orange-60 bg-yellow-40 text-center leading-normal"
|
||||||
|
href="https://www.thunderbird.net/"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
width="30"
|
||||||
|
height="30"
|
||||||
|
class="m-2 mr-3 d-inline-block align-middle"
|
||||||
|
>
|
||||||
|
<image
|
||||||
|
xlink:href="${assets.get('thunderbird-icon.svg')}"
|
||||||
|
src="${assets.get('thunderbird-icon.svg')}"
|
||||||
|
width="30"
|
||||||
|
height="30"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
${state.translate('sponsoredByThunderbird')}
|
||||||
|
</a>
|
||||||
|
`
|
||||||
|
: '';
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<send-archive
|
<send-archive
|
||||||
class="flex flex-col max-h-full bg-white p-4 w-full md:w-128 dark:bg-grey-90"
|
class="flex flex-col max-h-full bg-white p-4 w-full md:w-128 dark:bg-grey-90"
|
||||||
>
|
>
|
||||||
<div class="border rounded py-3 px-6 dark:border-grey-70">
|
<div class="border-default rounded-default py-3 px-6 dark:border-grey-70">
|
||||||
${archiveInfo(archive)} ${details}
|
${archiveInfo(archive)} ${details}
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
|
@ -574,6 +616,7 @@ module.exports.preview = function(state, emit) {
|
||||||
>
|
>
|
||||||
${state.translate('downloadButtonLabel')}
|
${state.translate('downloadButtonLabel')}
|
||||||
</button>
|
</button>
|
||||||
|
${notice} ${sponsor}
|
||||||
</send-archive>
|
</send-archive>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
@ -590,7 +633,7 @@ module.exports.downloading = function(state) {
|
||||||
const progressPercent = percent(progress);
|
const progressPercent = percent(progress);
|
||||||
return html`
|
return html`
|
||||||
<send-archive
|
<send-archive
|
||||||
class="flex flex-col bg-white rounded shadow-light p-4 w-full max-w-sm md:w-128 dark:bg-grey-90"
|
class="flex flex-col bg-white rounded-default shadow-light p-4 w-full max-w-sm md:w-128 dark:bg-grey-90"
|
||||||
>
|
>
|
||||||
${archiveInfo(archive)}
|
${archiveInfo(archive)}
|
||||||
<div class="link-primary text-sm font-medium mt-2">
|
<div class="link-primary text-sm font-medium mt-2">
|
||||||
|
|
|
@ -21,7 +21,7 @@ module.exports = function(name, url) {
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
id="share-url"
|
id="share-url"
|
||||||
class="block w-full my-4 border rounded-lg leading-loose h-12 px-2 py-1 dark:bg-grey-80"
|
class="block w-full my-4 border-default rounded-lg leading-loose h-12 px-2 py-1 dark:bg-grey-80"
|
||||||
value="${url}"
|
value="${url}"
|
||||||
readonly="true"
|
readonly="true"
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -17,7 +17,7 @@ module.exports = function(state, emit) {
|
||||||
${state.translate('downloadDescription')}
|
${state.translate('downloadDescription')}
|
||||||
</p>
|
</p>
|
||||||
<form
|
<form
|
||||||
class="flex flex-row flex-no-wrap w-full md:w-4/5"
|
class="flex flex-row flex-nowrap w-full md:w-4/5"
|
||||||
onsubmit="${checkPassword}"
|
onsubmit="${checkPassword}"
|
||||||
data-no-csrf
|
data-no-csrf
|
||||||
>
|
>
|
||||||
|
@ -32,7 +32,7 @@ module.exports = function(state, emit) {
|
||||||
class="w-full border-l border-t border-b rounded-l-lg rounded-r-none ${invalid
|
class="w-full border-l border-t border-b rounded-l-lg rounded-r-none ${invalid
|
||||||
? 'border-red dark:border-red-40'
|
? 'border-red dark:border-red-40'
|
||||||
: 'border-grey'} leading-loose px-2 py-1 dark:bg-grey-80"
|
: 'border-grey'} leading-loose px-2 py-1 dark:bg-grey-80"
|
||||||
maxlength="32"
|
maxlength="4096"
|
||||||
autocomplete="off"
|
autocomplete="off"
|
||||||
placeholder="${state.translate('unlockInputPlaceholder')}"
|
placeholder="${state.translate('unlockInputPlaceholder')}"
|
||||||
oninput="${inputChanged}"
|
oninput="${inputChanged}"
|
||||||
|
|
|
@ -65,6 +65,45 @@ class Footer extends Component {
|
||||||
`);
|
`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Defining a custom footer
|
||||||
|
var footer = [];
|
||||||
|
if (this.state != undefined && this.state.WEB_UI != undefined) {
|
||||||
|
const WEB_UI = this.state.WEB_UI;
|
||||||
|
|
||||||
|
if (WEB_UI.CUSTOM_FOOTER_URL != '' && WEB_UI.CUSTOM_FOOTER_TEXT != '') {
|
||||||
|
footer.push(html`
|
||||||
|
<li class="m-2">
|
||||||
|
<a href="${WEB_UI.CUSTOM_FOOTER_URL}" target="_blank">
|
||||||
|
${WEB_UI.CUSTOM_FOOTER_TEXT}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
`);
|
||||||
|
}
|
||||||
|
else if (WEB_UI.CUSTOM_FOOTER_URL != '') {
|
||||||
|
footer.push(html`
|
||||||
|
<li class="m-2">
|
||||||
|
<a href="${WEB_UI.CUSTOM_FOOTER_URL}" target="_blank">
|
||||||
|
${WEB_UI.CUSTOM_FOOTER_URL}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
`);
|
||||||
|
}
|
||||||
|
else if (WEB_UI.CUSTOM_FOOTER_TEXT != '') {
|
||||||
|
footer.push(html`
|
||||||
|
<li class="m-2">
|
||||||
|
${WEB_UI.CUSTOM_FOOTER_TEXT}
|
||||||
|
</li>
|
||||||
|
`)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
footer.push(html`
|
||||||
|
<li class="m-2">
|
||||||
|
${translate('footerText')}
|
||||||
|
</li>
|
||||||
|
`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<footer
|
<footer
|
||||||
class="flex flex-col md:flex-row items-start w-full flex-none self-start p-6 md:p-8 font-medium text-xs text-grey-60 dark:text-grey-40 md:items-center justify-between"
|
class="flex flex-col md:flex-row items-start w-full flex-none self-start p-6 md:p-8 font-medium text-xs text-grey-60 dark:text-grey-40 md:items-center justify-between"
|
||||||
|
@ -72,7 +111,7 @@ class Footer extends Component {
|
||||||
<ul
|
<ul
|
||||||
class="flex flex-col md:flex-row items-start md:items-center md:justify-start"
|
class="flex flex-col md:flex-row items-start md:items-center md:justify-start"
|
||||||
>
|
>
|
||||||
<li class="m-2">${translate('footerText')}</li>
|
${footer}
|
||||||
</ul>
|
</ul>
|
||||||
<ul
|
<ul
|
||||||
class="flex flex-col md:flex-row items-start md:items-center md:justify-end"
|
class="flex flex-col md:flex-row items-start md:items-center md:justify-end"
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
const html = require('choo/html');
|
const html = require('choo/html');
|
||||||
|
const raw = require('choo/html/raw');
|
||||||
const { list } = require('../utils');
|
const { list } = require('../utils');
|
||||||
const archiveTile = require('./archiveTile');
|
const archiveTile = require('./archiveTile');
|
||||||
const modal = require('./modal');
|
const modal = require('./modal');
|
||||||
const intro = require('./intro');
|
const intro = require('./intro');
|
||||||
|
const assets = require('../../common/assets');
|
||||||
|
|
||||||
module.exports = function(state, emit) {
|
module.exports = function(state, emit) {
|
||||||
const archives = state.storage.files
|
const archives = state.storage.files
|
||||||
|
@ -16,7 +18,42 @@ module.exports = function(state, emit) {
|
||||||
} else {
|
} else {
|
||||||
left = archiveTile.empty(state, emit);
|
left = archiveTile.empty(state, emit);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (archives.length > 0 && state.WEB_UI.UPLOADS_LIST_NOTICE_HTML) {
|
||||||
|
archives.push(html`
|
||||||
|
<p
|
||||||
|
class="w-full p-2 border-default dark:border-grey-70 rounded-default text-orange-60 bg-yellow-40 text-center leading-normal"
|
||||||
|
>
|
||||||
|
${raw(state.WEB_UI.UPLOADS_LIST_NOTICE_HTML)}
|
||||||
|
</p>
|
||||||
|
`);
|
||||||
|
}
|
||||||
|
|
||||||
archives.reverse();
|
archives.reverse();
|
||||||
|
|
||||||
|
if (archives.length > 0 && state.WEB_UI.SHOW_THUNDERBIRD_SPONSOR) {
|
||||||
|
archives.push(html`
|
||||||
|
<a
|
||||||
|
class="w-full p-2 border-default dark:border-grey-70 rounded-default text-orange-60 bg-yellow-40 text-center leading-normal d-block"
|
||||||
|
href="https://www.thunderbird.net/"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
width="30"
|
||||||
|
height="30"
|
||||||
|
class="m-2 mr-3 d-inline-block align-middle"
|
||||||
|
>
|
||||||
|
<image
|
||||||
|
xlink:href="${assets.get('thunderbird-icon.svg')}"
|
||||||
|
src="${assets.get('thunderbird-icon.svg')}"
|
||||||
|
width="30"
|
||||||
|
height="30"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
Sponsored by Thunderbird
|
||||||
|
</a>
|
||||||
|
`);
|
||||||
|
}
|
||||||
|
|
||||||
const right =
|
const right =
|
||||||
archives.length === 0
|
archives.length === 0
|
||||||
? intro(state)
|
? intro(state)
|
||||||
|
|
|
@ -1,10 +1,46 @@
|
||||||
const html = require('choo/html');
|
const html = require('choo/html');
|
||||||
|
const raw = require('choo/html/raw');
|
||||||
|
const assets = require('../../common/assets');
|
||||||
|
|
||||||
module.exports = function intro(state) {
|
module.exports = function intro(state) {
|
||||||
|
const notice = state.WEB_UI.MAIN_NOTICE_HTML
|
||||||
|
? html`
|
||||||
|
<p
|
||||||
|
class="w-full mt-2 p-2 border-default dark:border-grey-70 rounded-default text-orange-60 bg-yellow-40 text-center leading-normal"
|
||||||
|
>
|
||||||
|
${raw(state.WEB_UI.MAIN_NOTICE_HTML)}
|
||||||
|
</p>
|
||||||
|
`
|
||||||
|
: '';
|
||||||
|
|
||||||
|
const sponsor = state.WEB_UI.SHOW_THUNDERBIRD_SPONSOR
|
||||||
|
? html`
|
||||||
|
<a
|
||||||
|
class="w-full mt-5 mb-2 p-2 border-default dark:border-grey-70 rounded-default text-orange-60 bg-yellow-40 text-center leading-normal"
|
||||||
|
href="https://www.thunderbird.net/"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
width="30"
|
||||||
|
height="30"
|
||||||
|
class="m-2 mr-3 d-inline-block align-middle"
|
||||||
|
>
|
||||||
|
<image
|
||||||
|
xlink:href="${assets.get('thunderbird-icon.svg')}"
|
||||||
|
src="${assets.get('thunderbird-icon.svg')}"
|
||||||
|
width="30"
|
||||||
|
height="30"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
Sponsored by Thunderbird
|
||||||
|
</a>
|
||||||
|
`
|
||||||
|
: '';
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<send-intro
|
<send-intro
|
||||||
class="flex flex-col items-center justify-center bg-white px-6 md:py-0 py-6 mb-0 h-full w-full dark:bg-grey-90"
|
class="flex flex-col items-center justify-center bg-white px-6 md:py-0 py-6 mb-0 h-full w-full dark:bg-grey-90"
|
||||||
>
|
>
|
||||||
|
${notice}
|
||||||
<div class="mt-12 flex flex-col h-full">
|
<div class="mt-12 flex flex-col h-full">
|
||||||
<h1 class="text-3xl font-bold md:pb-2">
|
<h1 class="text-3xl font-bold md:pb-2">
|
||||||
${state.translate('introTitle')}
|
${state.translate('introTitle')}
|
||||||
|
@ -13,6 +49,7 @@ module.exports = function intro(state) {
|
||||||
${state.translate('introDescription')}
|
${state.translate('introDescription')}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
${sponsor}
|
||||||
</send-intro>
|
</send-intro>
|
||||||
`;
|
`;
|
||||||
};
|
};
|
||||||
|
|
|
@ -12,12 +12,12 @@ module.exports = function(state, emit) {
|
||||||
'downloadTitle'
|
'downloadTitle'
|
||||||
)}</h1>
|
)}</h1>
|
||||||
<p
|
<p
|
||||||
class="w-full p-2 border border-yellow-50 rounded md:w-4/5 text-orange-60 bg-yellow-40 text-center leading-normal"
|
class="w-full p-2 border-default border-yellow-50 rounded-default md:w-4/5 text-orange-60 bg-yellow-40 text-center leading-normal"
|
||||||
>
|
>
|
||||||
⚠️ ${state.translate('noStreamsWarning')} ⚠️
|
⚠️ ${state.translate('noStreamsWarning')} ⚠️
|
||||||
</p>
|
</p>
|
||||||
<form class="md:w-128" onsubmit=${submit}>
|
<form class="md:w-128" onsubmit=${submit}>
|
||||||
<fieldset class="border rounded p-4 my-4" onchange=${optionChanged}>
|
<fieldset class="border-default rounded-default p-4 my-4" onchange=${optionChanged}>
|
||||||
<div class="flex items-center mb-2">
|
<div class="flex items-center mb-2">
|
||||||
<svg class="h-8 w-6 mr-3 flex-shrink-0 text-primary">
|
<svg class="h-8 w-6 mr-3 flex-shrink-0 text-primary">
|
||||||
<use xlink:href="${assets.get('blue_file.svg')}#icon"/>
|
<use xlink:href="${assets.get('blue_file.svg')}#icon"/>
|
||||||
|
|
|
@ -18,7 +18,7 @@ module.exports = function(selected, options, translate, changed, htmlId) {
|
||||||
return html`
|
return html`
|
||||||
<select
|
<select
|
||||||
id="${htmlId}"
|
id="${htmlId}"
|
||||||
class="appearance-none cursor-pointer border rounded bg-grey-10 hover:border-primary focus:border-primary pl-1 pr-8 py-1 my-1 h-8 dark:bg-grey-80"
|
class="appearance-none cursor-pointer border-default rounded-default bg-grey-10 hover:border-primary focus:border-primary pl-1 pr-8 py-1 my-1 h-8 dark:bg-grey-80"
|
||||||
data-selected="${selected}"
|
data-selected="${selected}"
|
||||||
onchange="${choose}"
|
onchange="${choose}"
|
||||||
>
|
>
|
||||||
|
|
|
@ -18,7 +18,7 @@ module.exports = function(name, url) {
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
id="share-url"
|
id="share-url"
|
||||||
class="w-full my-4 border rounded-lg leading-loose h-12 px-2 py-1 dark:bg-grey-80"
|
class="w-full my-4 border-default rounded-lg leading-loose h-12 px-2 py-1 dark:bg-grey-80"
|
||||||
value="${url}"
|
value="${url}"
|
||||||
readonly="true"
|
readonly="true"
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -35,7 +35,7 @@ module.exports = function() {
|
||||||
<input
|
<input
|
||||||
id="email-input"
|
id="email-input"
|
||||||
type="email"
|
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-default 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')}
|
placeholder=${state.translate('emailPlaceholder')}
|
||||||
/>
|
/>
|
||||||
<input
|
<input
|
||||||
|
|
|
@ -27,7 +27,7 @@ module.exports = function(state, emit) {
|
||||||
<main class="main">
|
<main class="main">
|
||||||
${state.modal && modal(state, emit)}
|
${state.modal && modal(state, emit)}
|
||||||
<section
|
<section
|
||||||
class="flex flex-col items-center justify-center text-center bg-white m-6 px-6 py-8 border border-grey-30 md:border-none md:px-12 md:py-16 shadow w-full md:h-full dark:bg-grey-90"
|
class="flex flex-col items-center justify-center text-center bg-white m-6 px-6 py-8 border-default border-grey-30 md:border-none md:px-12 md:py-16 shadow-default w-full md:h-full dark:bg-grey-90"
|
||||||
>
|
>
|
||||||
<h1 class="text-3xl font-bold">${strings.header}</h1>
|
<h1 class="text-3xl font-bold">${strings.header}</h1>
|
||||||
<p class="mt-4 mb-8 max-w-md leading-normal">${strings.description}</p>
|
<p class="mt-4 mb-8 max-w-md leading-normal">${strings.description}</p>
|
||||||
|
|
65
assets/thunderbird-icon.svg
Normal file
65
assets/thunderbird-icon.svg
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 750 750">
|
||||||
|
<path fill="url(#a)" d="m 314.805,154.949 h 0.06 c 22.04,-77.0499 118.08,-114.6599 215.95,-114.6599 67.63,0 128.341,21.41 169.961,55.4 -24.838,1.1712 -49.362,6.0449 -72.76,14.4599 33.63,12.5 62.519,31.73 83.929,55.52 -16.153,-2.78 -32.532,-4.019 -48.919,-3.7 40.276,58.343 61.794,127.585 61.68,198.48 0,193.3 -156.701,350 -350.001,350 -190.32,0 -349.9999,-159.35 -349.9999,-350 0,-30.11 4,-61.2 11.77,-90.36 2.04,-6.12 4.88,-11.99 8.65,-14.14 4.72,-2.69 9.02,5.33 9.71,7.94 5.1177,19.17 12.0039,37.823 20.57,55.72 -0.75,-39.96 16.32,-76.36 39.7999,-107.84 15.66,-20.99 30.18,-40.44 36.88,-96.57 0.45,-3.77 4.02,-6.48 7.63,-5.3 50.96,16.68 78.2,101.54 73.97,172.5 28.15,4.03 28.02,-25.38 28.02,-25.38 -9,-27.66 -3,-79.07 53,-102.07 z"/>
|
||||||
|
<path fill="url(#b)" d="m 713.254,271.32 c 8.51,192.5 -149.74,358.47 -342.71,358.47 -180.65,0 -328.6792,-139.63 -342.0692,-316.85 -2.3889,16.596 -3.6452,33.334 -3.76,50.1 1.37,189.62 160.6592,347.41 349.9992,347.41 193.3,0 350,-156.7 350,-350 0,-30.8 -3.99,-60.67 -11.46,-89.13 z" opacity="0.9"/>
|
||||||
|
<path fill="url(#c)" d="m 366.175,180.459 c -3.77,-6.67 -21.18,-16.54 -28.79,-18.26 28.8,-92.2499 175.521,-120.5699 265.321,-104.2499 37.37,6.8 83.91,27.16 98.07,37.74 -41.62,-33.99 -102.341,-55.4 -169.961,-55.4 -97.87,0 -193.91,37.61 -215.95,114.6599 h -0.16 c -56,23 -62,74.43 -53,102.08 8.64,-32.99 49.75,-73.53 104.47,-76.57 z" style="mix-blend-mode:screen"/>
|
||||||
|
<path fill="url(#d)" d="m 467.885,116.748 c -78.62,15.47 -104.31,20.53 -130.62,45.54 29.55,-78.2297 104.98,-94.0897 194.86,-58.43 -21.391,4.41 -42.805,8.71 -64.24,12.9 z"/>
|
||||||
|
<path fill="url(#e)" d="m 43.7049,259.38 c -21.48,87.94 -4.88,191.3 92.6991,278.04 -29.05,-31.77 -64.5191,-149.07 13.75,-232.87 5.27,-5.65 14.34,-1.5 14.63,6.22 6.45,174.16 146.98,280.53 309,260.64 -50.2,-2.82 -216.22,-60.97 -92.72,-83.97 64.55,-12.03 165.76,-30.88 165.76,-121.7 0,-147.22 -113.83,-190.26 -182.86,-183.86 -47.24,4.38 -89.29,34.36 -102.23,75.13 4.97,16.07 -14.84,27.32 -28.05,25.43 4.24,-70.95 -23,-155.86 -73.97,-172.54 -3.6,-1.18 -7.18,1.53 -7.63,5.3 -6.7,56.13 -21.22,75.58 -36.87,96.57 -23.4891,31.49 -40.5591,67.88 -39.8091,107.84 -8.5659,-17.897 -15.4521,-36.55 -20.57,-55.72 -0.57,-2.16 -3.7,-8.19 -7.48,-8.47 -2.05,-0.15 -3.14,1.85 -3.65,3.96 z"/>
|
||||||
|
<path fill="url(#f)" d="m 337.695,496.349 c 95.04,77.17 286.17,19.31 286.17,-168.32 -77.16,116.96 -175.44,197.64 -286.16,168.32 z" style="mix-blend-mode:screen"/>
|
||||||
|
<path fill="url(#g)" d="m 150.155,304.55 c 0.864,-0.967 1.953,-1.706 3.17,-2.153 1.217,-0.446 2.526,-0.586 3.81,-0.407 -70.0595,85.45 -13.549,235.52 25.231,272.39 2.17,6.14 -36.791,-25.79 -42.161,-33.26 -29.5,-25.03 -71.7795,-149.06 9.95,-236.57 z" style="mix-blend-mode:screen"/>
|
||||||
|
<path fill="url(#h)" d="m 374.705,503.419 c 95.06,0 172.13,-62.879 172.13,-140.449 0,-77.57 -77.07,-140.45 -172.13,-140.45 -81.1,0 -172.17,52.76 -172.13,142.5 0.04,138.67 146.54,218.45 271.42,206.37 -9.38,-1.09 -67.9,-4.2 -107.45,-48.94 -3.57,-4.03 -9.76,-11.07 -6.95,-15.64 2.8,-4.57 10.52,-3.4 15.1,-3.4 z"/>
|
||||||
|
<path fill="#fff" d="m 528.085,299.15 -136.04,130.18 c -12.07,8.58 -24.92,9.2 -37.6,1.42 L 221.015,299.63 c 3.807,-6.118 8.087,-11.929 12.8,-17.38 l 14,13.1 c 35.04,32.84 63.37,59.37 103.3,93.45 18.02,15.38 23.62,15.08 41.3,0 45.68,-39 79.09,-68.5 122.52,-107.29 4.841,5.526 9.235,11.428 13.14,17.65 z" opacity="0.6"/>
|
||||||
|
<mask id="i" width="345" height="276" x="202" y="297" maskUnits="userSpaceOnUse">
|
||||||
|
<path fill="#fff" d="m 546.835,362.969 c 0,77.57 -77.07,140.45 -172.13,140.45 -4.59,0 -12.3,-1.18 -15.11,3.4 -2.81,4.56 3.38,11.6 6.95,15.63 37.16,42.04 91.07,47.33 105.22,48.72 l 2.23,0.22 c -124.88,12.08 -271.38,-67.7 -271.42,-206.37 -0.131,-23.089 6.295,-45.74 18.53,-65.32 l 134.08,121.78 c 9.54,8.66 25.61,8.66 35.15,0 l 136.62,-124.09 c 12.7,19.58 19.88,41.9 19.88,65.58 z"/>
|
||||||
|
</mask>
|
||||||
|
<g mask="url(#i)">
|
||||||
|
<path fill="url(#j)" d="m 162.705,200.979 h 435.07 v 394.47 h -435.07 z" opacity="0.7"/>
|
||||||
|
<g filter="url(#k)">
|
||||||
|
<path fill="#458fcd" fill-rule="evenodd" d="m 334.745,422.298 c -25.35,-26.38 -101.27,-114.03 -101.27,-114.03 l 5.87,0.26 118.86,88.77 c 8.9,6.47 21.6,6.4 30.42,-0.15 l 116.55,-88.5 6.18,-0.49 c 0,0 -73.44,85.57 -101.57,113.76 -28.13,28.19 -49.69,26.76 -75.04,0.38 z" clip-rule="evenodd"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<path fill="#fff" d="m 398.855,152.258 c 18.42,-5.8 16.8,-24.03 16.8,-24.03 0,0 -9.21,-10.85 -27.46,-4.84 -17.08,5.63 -19.73,17.8 -19.73,17.8 0,0 9.33,17.7 30.39,11.07 z"/>
|
||||||
|
<defs>
|
||||||
|
<linearGradient id="a" x1="146.185" x2="639.58502" y1="155.649" y2="615.86902" gradientUnits="userSpaceOnUse">
|
||||||
|
<stop stop-color="#1B91F3"/>
|
||||||
|
<stop offset="1" stop-color="#0B68CB"/>
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient id="d" x1="283.47501" x2="461.01501" y1="273.45801" y2="75.818298" gradientUnits="userSpaceOnUse">
|
||||||
|
<stop stop-color="#0F5DB0"/>
|
||||||
|
<stop offset="1" stop-color="#0F5DB0" stop-opacity="0"/>
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient id="f" x1="594.625" x2="512.07501" y1="416.55899" y2="619.24902" gradientUnits="userSpaceOnUse">
|
||||||
|
<stop stop-color="#E247C4" stop-opacity="0"/>
|
||||||
|
<stop offset="1" stop-color="#E247C4" stop-opacity="0.64"/>
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient id="g" x1="82.7155" x2="155.44501" y1="234.78999" y2="527.51001" gradientUnits="userSpaceOnUse">
|
||||||
|
<stop offset="0.1" stop-color="#EF3ACC"/>
|
||||||
|
<stop offset="1" stop-color="#EF3ACC" stop-opacity="0"/>
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient id="h" x1="374.70499" x2="374.70499" y1="273.95001" y2="569.94897" gradientUnits="userSpaceOnUse">
|
||||||
|
<stop stop-color="#fff"/>
|
||||||
|
<stop offset="0.91" stop-color="#BEE1FE"/>
|
||||||
|
<stop offset="1" stop-color="#96CEFD"/>
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient id="j" x1="380.245" x2="380.245" y1="441.44901" y2="565.44897" gradientUnits="userSpaceOnUse">
|
||||||
|
<stop stop-color="#BCE0FD"/>
|
||||||
|
<stop offset="1" stop-color="#88CCFC"/>
|
||||||
|
</linearGradient>
|
||||||
|
<radialGradient id="b" cx="0" cy="0" r="1" gradientTransform="matrix(117.92992,271.44954,-260.03887,112.97261,145.274,314)" gradientUnits="userSpaceOnUse">
|
||||||
|
<stop offset="0.53" stop-color="#0B4186" stop-opacity="0"/>
|
||||||
|
<stop offset="1" stop-color="#0B4186" stop-opacity="0.45"/>
|
||||||
|
</radialGradient>
|
||||||
|
<radialGradient id="c" cx="0" cy="0" r="1" gradientTransform="matrix(-38.950414,-49.859637,82.506525,-64.454205,383.705,192.449)" gradientUnits="userSpaceOnUse">
|
||||||
|
<stop stop-color="#EF3ACC" stop-opacity="0"/>
|
||||||
|
<stop offset="1" stop-color="#EF3ACC" stop-opacity="0.64"/>
|
||||||
|
</radialGradient>
|
||||||
|
<radialGradient id="e" cx="0" cy="0" r="1" gradientTransform="matrix(200.50032,-415.91019,513.77136,247.67684,242.704,551.15)" gradientUnits="userSpaceOnUse">
|
||||||
|
<stop offset="0.02" stop-color="#094188"/>
|
||||||
|
<stop offset="0.97" stop-color="#0B4186" stop-opacity="0"/>
|
||||||
|
</radialGradient>
|
||||||
|
<filter id="k" width="341.88" height="198.416" x="201.47501" y="276.15799" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse">
|
||||||
|
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||||
|
<feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
|
||||||
|
<feGaussianBlur result="effect1_foregroundBlur_106_1002" stdDeviation="16"/>
|
||||||
|
</filter>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 7.1 KiB |
|
@ -9,6 +9,7 @@ docker pull registry.gitlab.com/timvisee/send:latest
|
||||||
docker run -v $PWD/uploads:/uploads -p 1443:1443 \
|
docker run -v $PWD/uploads:/uploads -p 1443:1443 \
|
||||||
-e 'DETECT_BASE_URL=true' \
|
-e 'DETECT_BASE_URL=true' \
|
||||||
-e 'REDIS_HOST=localhost' \
|
-e 'REDIS_HOST=localhost' \
|
||||||
|
-e 'FILE_DIR=/uploads' \
|
||||||
registry.gitlab.com/timvisee/send:latest
|
registry.gitlab.com/timvisee/send:latest
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -33,7 +34,7 @@ Config options expecting array values (e.g. `EXPIRE_TIMES_SECONDS`, `DOWNLOAD_CO
|
||||||
| `PORT` | Port the server will listen on (defaults to `1443`)
|
| `PORT` | Port the server will listen on (defaults to `1443`)
|
||||||
| `NODE_ENV` | Run in `development` mode (unsafe) or `production` mode (the default)
|
| `NODE_ENV` | Run in `development` mode (unsafe) or `production` mode (the default)
|
||||||
| `SEND_FOOTER_DMCA_URL` | A URL to a contact page for DMCA requests (empty / not shown by default)
|
| `SEND_FOOTER_DMCA_URL` | A URL to a contact page for DMCA requests (empty / not shown by default)
|
||||||
| `SENTRY_CLIENT`, `SENTRY_DSN` | Sentry Client ID and DNS for error tracking (optional, disabled by default)
|
| `SENTRY_CLIENT`, `SENTRY_DSN` | Sentry Client ID and DSN for error tracking (optional, disabled by default)
|
||||||
|
|
||||||
*Note: more options can be found here: https://github.com/timvisee/send/blob/master/server/config.js*
|
*Note: more options can be found here: https://github.com/timvisee/send/blob/master/server/config.js*
|
||||||
|
|
||||||
|
@ -96,6 +97,11 @@ See the table below for the variables and their default values.
|
||||||
| UI_CUSTOM_ASSETS_FACEBOOK | | A custom header image for Facebook |
|
| UI_CUSTOM_ASSETS_FACEBOOK | | A custom header image for Facebook |
|
||||||
| UI_CUSTOM_ASSETS_TWITTER | | A custom header image for Twitter |
|
| UI_CUSTOM_ASSETS_TWITTER | | A custom header image for Twitter |
|
||||||
| UI_CUSTOM_ASSETS_WORDMARK | | A custom wordmark (Text next to the logo) |
|
| UI_CUSTOM_ASSETS_WORDMARK | | A custom wordmark (Text next to the logo) |
|
||||||
|
| UI_CUSTOM_CSS | | Allows you to define a custom CSS file for custom styling |
|
||||||
|
| CUSTOM_FOOTER_TEXT | | Allows you to define a custom footer |
|
||||||
|
| CUSTOM_FOOTER_URL | | Allows you to define a custom URL in your footer |
|
||||||
|
|
||||||
|
Side note: If you define a custom URL and a custom footer, only the footer text will display, but will be hyperlinked to the URL.
|
||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
|
|
||||||
|
|
31532
package-lock.json
generated
31532
package-lock.json
generated
File diff suppressed because it is too large
Load diff
48
package.json
48
package.json
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"name": "send",
|
"name": "send",
|
||||||
"description": "File Sharing Experiment",
|
"description": "File Sharing Experiment",
|
||||||
"version": "3.4.20",
|
"version": "3.4.27",
|
||||||
"author": "Mozilla (https://mozilla.org)",
|
"author": "Mozilla (https://mozilla.org)",
|
||||||
"contributors": [
|
"contributors": [
|
||||||
"Tim Visee <3a4fb3964f@sinenomine.email> (https://timvisee.com)"
|
"Tim Visee <3a4fb3964f@sinenomine.email> (https://timvisee.com)"
|
||||||
|
@ -67,7 +67,7 @@
|
||||||
"@babel/plugin-syntax-dynamic-import": "^7.2.0",
|
"@babel/plugin-syntax-dynamic-import": "^7.2.0",
|
||||||
"@babel/preset-env": "^7.16.11",
|
"@babel/preset-env": "^7.16.11",
|
||||||
"@dannycoates/webcrypto-liner": "^0.1.37",
|
"@dannycoates/webcrypto-liner": "^0.1.37",
|
||||||
"@fullhuman/postcss-purgecss": "^1.3.0",
|
"@fullhuman/postcss-purgecss": "^4.1.3",
|
||||||
"@mattiasbuelens/web-streams-polyfill": "0.2.1",
|
"@mattiasbuelens/web-streams-polyfill": "0.2.1",
|
||||||
"@sentry/browser": "^5.30.0",
|
"@sentry/browser": "^5.30.0",
|
||||||
"asmcrypto.js": "^0.22.0",
|
"asmcrypto.js": "^0.22.0",
|
||||||
|
@ -75,13 +75,13 @@
|
||||||
"babel-plugin-istanbul": "^5.2.0",
|
"babel-plugin-istanbul": "^5.2.0",
|
||||||
"base64-js": "^1.5.1",
|
"base64-js": "^1.5.1",
|
||||||
"content-disposition": "^0.5.4",
|
"content-disposition": "^0.5.4",
|
||||||
"copy-webpack-plugin": "^5.1.2",
|
"copy-webpack-plugin": "^6.4.0",
|
||||||
"core-js": "^3.21.1",
|
"core-js": "^3.21.1",
|
||||||
"crc": "^3.8.0",
|
"crc": "^3.8.0",
|
||||||
"cross-env": "^6.0.3",
|
"cross-env": "^6.0.3",
|
||||||
"css-loader": "^3.6.0",
|
"css-loader": "^5.2.7",
|
||||||
"css-mqpacker": "^7.0.0",
|
"css-mqpacker": "^7.0.0",
|
||||||
"cssnano": "^4.1.11",
|
"cssnano": "^5.1.12",
|
||||||
"eslint": "^6.6.0",
|
"eslint": "^6.6.0",
|
||||||
"eslint-config-prettier": "^6.15.0",
|
"eslint-config-prettier": "^6.15.0",
|
||||||
"eslint-plugin-mocha": "^6.2.1",
|
"eslint-plugin-mocha": "^6.2.1",
|
||||||
|
@ -91,22 +91,23 @@
|
||||||
"extract-loader": "^3.2.0",
|
"extract-loader": "^3.2.0",
|
||||||
"extract-text-webpack-plugin": "^4.0.0-beta.0",
|
"extract-text-webpack-plugin": "^4.0.0-beta.0",
|
||||||
"fast-text-encoding": "^1.0.3",
|
"fast-text-encoding": "^1.0.3",
|
||||||
"file-loader": "^4.2.0",
|
"file-loader": "^6.2.0",
|
||||||
"git-rev-sync": "^1.12.0",
|
"git-rev-sync": "^3.0.2",
|
||||||
"html-loader": "^0.5.5",
|
"html-loader": "^0.5.5",
|
||||||
"http_ece": "^1.1.0",
|
"http_ece": "^1.1.0",
|
||||||
"husky": "^3.0.9",
|
"husky": "^3.0.9",
|
||||||
"intl-pluralrules": "^1.3.1",
|
"intl-pluralrules": "^1.3.1",
|
||||||
"lint-staged": "^9.4.2",
|
"lint-staged": "^9.4.2",
|
||||||
"mocha": "^6.2.2",
|
"mocha": "^10.1.0",
|
||||||
"morgan": "^1.9.1",
|
"morgan": "^1.9.1",
|
||||||
"nanobus": "^4.5.0",
|
"nanobus": "^4.5.0",
|
||||||
"nanohtml": "^1.9.0",
|
"nanohtml": "^1.9.0",
|
||||||
"nanotiming": "^7.3.1",
|
"nanotiming": "^7.3.1",
|
||||||
"npm-run-all": "^4.1.5",
|
"npm-run-all": "^4.1.5",
|
||||||
"nyc": "^14.1.1",
|
"nyc": "^14.1.1",
|
||||||
"postcss-loader": "^3.0.0",
|
"postcss": "^8.4.14",
|
||||||
"postcss-preset-env": "^6.7.1",
|
"postcss-loader": "^4.2.0",
|
||||||
|
"postcss-preset-env": "^7.7.2",
|
||||||
"prettier": "^1.19.1",
|
"prettier": "^1.19.1",
|
||||||
"proxyquire": "^2.1.3",
|
"proxyquire": "^2.1.3",
|
||||||
"puppeteer": "^2.0.0",
|
"puppeteer": "^2.0.0",
|
||||||
|
@ -115,13 +116,13 @@
|
||||||
"script-loader": "^0.7.2",
|
"script-loader": "^0.7.2",
|
||||||
"sinon": "^7.5.0",
|
"sinon": "^7.5.0",
|
||||||
"string-hash": "^1.1.3",
|
"string-hash": "^1.1.3",
|
||||||
"stylelint": "^13.13.1",
|
"stylelint": "^14.9.1",
|
||||||
"stylelint-config-standard": "^19.0.0",
|
"stylelint-config-standard": "^26.0.0",
|
||||||
"stylelint-no-unsupported-browser-features": "^4.1.4",
|
"stylelint-no-unsupported-browser-features": "^5.0.3",
|
||||||
"svgo": "^1.3.2",
|
"svgo": "^2.8.0",
|
||||||
"svgo-loader": "^2.2.2",
|
"svgo-loader": "^3.0.1",
|
||||||
"tailwindcss": "^1.9.6",
|
"tailwindcss": "^2",
|
||||||
"val-loader": "^1.1.1",
|
"val-loader": "^2.1.2",
|
||||||
"webpack": "4.38.0",
|
"webpack": "4.38.0",
|
||||||
"webpack-cli": "^3.3.12",
|
"webpack-cli": "^3.3.12",
|
||||||
"webpack-dev-middleware": "^3.7.3",
|
"webpack-dev-middleware": "^3.7.3",
|
||||||
|
@ -131,22 +132,21 @@
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@dannycoates/express-ws": "^5.0.3",
|
"@dannycoates/express-ws": "^5.0.3",
|
||||||
"@fluent/bundle": "^0.13.0",
|
"@fluent/bundle": "^0.17.1",
|
||||||
"@fluent/langneg": "^0.3.0",
|
"@fluent/langneg": "^0.6.2",
|
||||||
"@google-cloud/storage": "^5.19.0",
|
"@google-cloud/storage": "^6.2.3",
|
||||||
"@sentry/node": "^5.30.0",
|
"@sentry/node": "^7.7.0",
|
||||||
"aws-sdk": "^2.1109.0",
|
"aws-sdk": "^2.1109.0",
|
||||||
"body-parser": "^1.20.0",
|
"body-parser": "^1.20.0",
|
||||||
"choo": "^7.0.0",
|
"choo": "^7.0.0",
|
||||||
"cldr-core": "^35.1.0",
|
"cldr-core": "^35.1.0",
|
||||||
"configstore": "github:dannycoates/configstore#master",
|
"configstore": "github:dannycoates/configstore#master",
|
||||||
"convict": "^6.2.2",
|
"convict": "^6.2.4",
|
||||||
"convict-format-with-validator": "^6.2.0",
|
"convict-format-with-validator": "^6.2.0",
|
||||||
"double-ended-queue": "^2.1.0-0",
|
"double-ended-queue": "^2.1.0-0",
|
||||||
"express": "^4.17.3",
|
"express": "^4.17.3",
|
||||||
"helmet": "^3.23.3",
|
"helmet": "^3.23.3",
|
||||||
"mkdirp": "^0.5.6",
|
"mozlog": "^3.0.1",
|
||||||
"mozlog": "^2.2.0",
|
|
||||||
"node-fetch": "^2.6.7",
|
"node-fetch": "^2.6.7",
|
||||||
"redis": "^3.1.1",
|
"redis": "^3.1.1",
|
||||||
"redis-mock": "^0.47.0",
|
"redis-mock": "^0.47.0",
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
class TailwindExtractor {
|
const TailwindExtractor = content => {
|
||||||
static extract(content) {
|
return content.match(/[A-Za-z0-9-_:/]+/g) || [];
|
||||||
return content.match(/[A-Za-z0-9-_:/]+/g) || [];
|
};
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const options = {
|
const options = {
|
||||||
plugins: [
|
plugins: [
|
||||||
|
|
|
@ -1,17 +1,17 @@
|
||||||
{
|
{
|
||||||
"name": "firefox-send",
|
"name": "send",
|
||||||
"description": "File Sharing Experiment",
|
"description": "File Sharing Experiment",
|
||||||
"repository": {
|
"repository": {
|
||||||
"url": "https://github.com/send/send/",
|
"url": "https://github.com/timvisee/send/",
|
||||||
"license": "MPL-2.0"
|
"license": "MPL-2.0"
|
||||||
},
|
},
|
||||||
"participate": {
|
"participate": {
|
||||||
"home": "https://github.com/send/send/blob/master/README.md",
|
"home": "https://github.om/timvisee/send/blob/master/README.md",
|
||||||
"docs": "https://github.com/send/send/blob/master/README.md"
|
"docs": "https://github.com/timvisee/send/blob/master/README.md"
|
||||||
},
|
},
|
||||||
"bugs": {
|
"bugs": {
|
||||||
"list": "https://gitlab.com/send/send/issues",
|
"list": "https://github.com/timvisee/send/issues",
|
||||||
"report": "https://gitlab.com/send/send/issues/new"
|
"report": "https://github.com/timvisee/send/issues/new"
|
||||||
},
|
},
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"JavaScript",
|
"JavaScript",
|
||||||
|
|
|
@ -48,12 +48,12 @@ passwordSetError = Toto heslo nemohlo být nastaveno
|
||||||
-send-brand =
|
-send-brand =
|
||||||
{ $case ->
|
{ $case ->
|
||||||
*[nom] Send
|
*[nom] Send
|
||||||
[gen] Send
|
[gen] Sendu
|
||||||
[dat] Send
|
[dat] Sendu
|
||||||
[acc] Send
|
[acc] Send
|
||||||
[voc] Send
|
[voc] Sende
|
||||||
[loc] Send
|
[loc] Sendu
|
||||||
[ins] Send
|
[ins] Sendem
|
||||||
}
|
}
|
||||||
-send-short-brand =
|
-send-short-brand =
|
||||||
{ $case ->
|
{ $case ->
|
||||||
|
@ -86,7 +86,7 @@ passwordSetError = Toto heslo nemohlo být nastaveno
|
||||||
[ins] Mozillou
|
[ins] Mozillou
|
||||||
}
|
}
|
||||||
introTitle = Jednoduché a soukromé sdílení souborů
|
introTitle = Jednoduché a soukromé sdílení souborů
|
||||||
introDescription = S { -send-brand(case: "ins") } jsou sdílené soubory šifrované end-to-end, takže ani my nevíme, co sdílíte. Platnost odkazů je navíc omezená. Soubory tak můžete sdílet soukromě a s jistotou, že se nezůstanou na internetu válet navždy.
|
introDescription = Se { -send-brand(case: "ins") } jsou sdílené soubory šifrované end-to-end, takže ani my nevíme, co sdílíte. Platnost odkazů je navíc omezená. Soubory tak můžete sdílet soukromě a s jistotou, že se nezůstanou na internetu válet navždy.
|
||||||
notifyUploadEncryptDone = Váš soubor je zašifrovaný a připraven k odeslání
|
notifyUploadEncryptDone = Váš soubor je zašifrovaný a připraven k odeslání
|
||||||
# downloadCount is from the downloadCount string and timespan is a timespanMinutes string. ex. 'Expires after 2 downloads or 25 minutes'
|
# downloadCount is from the downloadCount string and timespan is a timespanMinutes string. ex. 'Expires after 2 downloads or 25 minutes'
|
||||||
archiveExpiryInfo = Platnost vyprší po { $downloadCount } nebo za { $timespan }
|
archiveExpiryInfo = Platnost vyprší po { $downloadCount } nebo za { $timespan }
|
||||||
|
@ -131,7 +131,7 @@ copyLinkDescription = Soubor můžete sdílet tímto odkazem:
|
||||||
copyLinkButton = Zkopírovat odkaz
|
copyLinkButton = Zkopírovat odkaz
|
||||||
downloadTitle = Stáhnout soubory
|
downloadTitle = Stáhnout soubory
|
||||||
downloadDescription = Tento soubor byl sdílen přes { -send-brand(case: "acc") } s end-to-end šifrováním a odkazem s omezenou platností.
|
downloadDescription = Tento soubor byl sdílen přes { -send-brand(case: "acc") } s end-to-end šifrováním a odkazem s omezenou platností.
|
||||||
trySendDescription = Vyzkoušejte jednoduché a bezpečné sdílení souborů s { -send-brand(case: "ins") }
|
trySendDescription = Vyzkoušejte jednoduché a bezpečné sdílení souborů se { -send-brand(case: "ins") }
|
||||||
# count will always be > 10
|
# count will always be > 10
|
||||||
tooManyFiles =
|
tooManyFiles =
|
||||||
{ $count ->
|
{ $count ->
|
||||||
|
@ -189,6 +189,6 @@ downloadFirefoxPromo = { -send-short-brand } od aplikace { -firefox }.
|
||||||
shareLinkDescription = Sdílet odkaz na soubor:
|
shareLinkDescription = Sdílet odkaz na soubor:
|
||||||
shareLinkButton = Sdílet odkaz
|
shareLinkButton = Sdílet odkaz
|
||||||
# $name is the name of the file
|
# $name is the name of the file
|
||||||
shareMessage = Stáhněte si soubor „{ $name }“ s { -send-brand(case: "ins") } - jednoduché a bezpečné sdílení souborů
|
shareMessage = Stáhněte si soubor „{ $name }“ se { -send-brand(case: "ins") } - jednoduché a bezpečné sdílení souborů
|
||||||
trailheadPromo = Existuje způsob, jak ochránit své soukromí. Používejte Firefox.
|
trailheadPromo = Existuje způsob, jak ochránit své soukromí. Používejte Firefox.
|
||||||
learnMore = Zjistit více.
|
learnMore = Zjistit více.
|
||||||
|
|
|
@ -147,3 +147,5 @@ shareLinkButton = Share link
|
||||||
shareMessage = Download “{ $name }” with { -send-brand }: simple, safe file sharing
|
shareMessage = Download “{ $name }” with { -send-brand }: simple, safe file sharing
|
||||||
trailheadPromo = There is a way to protect your privacy. Join Firefox.
|
trailheadPromo = There is a way to protect your privacy. Join Firefox.
|
||||||
learnMore = Learn more.
|
learnMore = Learn more.
|
||||||
|
|
||||||
|
sponsoredByThunderbird = Sponsored by Thunderbird
|
||||||
|
|
|
@ -152,3 +152,5 @@ shareLinkButton = Share link
|
||||||
shareMessage = Download “{ $name }” with { -send-brand }: simple, safe file sharing
|
shareMessage = Download “{ $name }” with { -send-brand }: simple, safe file sharing
|
||||||
trailheadPromo = There is a way to protect your privacy. Join Firefox.
|
trailheadPromo = There is a way to protect your privacy. Join Firefox.
|
||||||
learnMore = Learn more.
|
learnMore = Learn more.
|
||||||
|
|
||||||
|
sponsoredByThunderbird = Sponsored by Thunderbird
|
||||||
|
|
|
@ -143,3 +143,5 @@ shareLinkButton = Share link
|
||||||
# $name is the name of the file
|
# $name is the name of the file
|
||||||
shareMessage = Download “{ $name }” with { -send-brand }: simple, safe file sharing
|
shareMessage = Download “{ $name }” with { -send-brand }: simple, safe file sharing
|
||||||
learnMore = Learn more.
|
learnMore = Learn more.
|
||||||
|
|
||||||
|
sponsoredByThunderbird = Sponsored by Thunderbird
|
||||||
|
|
|
@ -52,7 +52,7 @@ passwordSetError = Dit wachtwoord kon niet worden ingesteld
|
||||||
-send-short-brand = Send
|
-send-short-brand = Send
|
||||||
-firefox = Firefox
|
-firefox = Firefox
|
||||||
-mozilla = Mozilla
|
-mozilla = Mozilla
|
||||||
introTitle = Bestanden delen, eenvoudig en privé
|
introTitle = Bestanden delen, eenvoudig en privé
|
||||||
introDescription = Met { -send-brand } kunt u bestanden delen met end-to-endversleuteling en een koppeling die automatisch verloopt. Hierdoor kunt u privé houden wat u wilt delen en er zeker van zijn dat uw zaken niet voor altijd online blijven.
|
introDescription = Met { -send-brand } kunt u bestanden delen met end-to-endversleuteling en een koppeling die automatisch verloopt. Hierdoor kunt u privé houden wat u wilt delen en er zeker van zijn dat uw zaken niet voor altijd online blijven.
|
||||||
notifyUploadEncryptDone = Uw bestand is versleuteld en klaar voor verzending
|
notifyUploadEncryptDone = Uw bestand is versleuteld en klaar voor verzending
|
||||||
# downloadCount is from the downloadCount string and timespan is a timespanMinutes string. ex. 'Expires after 2 downloads or 25 minutes'
|
# downloadCount is from the downloadCount string and timespan is a timespanMinutes string. ex. 'Expires after 2 downloads or 25 minutes'
|
||||||
|
@ -152,3 +152,5 @@ shareLinkButton = Koppeling delen
|
||||||
shareMessage = Download ‘{ $name }’ met { -send-brand }: eenvoudig, veilig bestanden delen
|
shareMessage = Download ‘{ $name }’ met { -send-brand }: eenvoudig, veilig bestanden delen
|
||||||
trailheadPromo = Er is een manier om uw privacy te beschermen. Doe mee met Firefox.
|
trailheadPromo = Er is een manier om uw privacy te beschermen. Doe mee met Firefox.
|
||||||
learnMore = Meer info.
|
learnMore = Meer info.
|
||||||
|
|
||||||
|
sponsoredByThunderbird = Gesponsord door Thunderbird
|
||||||
|
|
|
@ -53,7 +53,7 @@ introTitle = Простой и безопасный обмен файлами
|
||||||
introDescription = { -send-brand } позволяет вам делиться файлами со сквозным шифрованием и ограниченным сроком действия ссылки на загрузку. Так что, вы сможете делиться файлами приватно и они не останутся в сети навсегда.
|
introDescription = { -send-brand } позволяет вам делиться файлами со сквозным шифрованием и ограниченным сроком действия ссылки на загрузку. Так что, вы сможете делиться файлами приватно и они не останутся в сети навсегда.
|
||||||
notifyUploadEncryptDone = Ваш файл зашифрован и готов к отправке
|
notifyUploadEncryptDone = Ваш файл зашифрован и готов к отправке
|
||||||
# downloadCount is from the downloadCount string and timespan is a timespanMinutes string. ex. 'Expires after 2 downloads or 25 minutes'
|
# downloadCount is from the downloadCount string and timespan is a timespanMinutes string. ex. 'Expires after 2 downloads or 25 minutes'
|
||||||
archiveExpiryInfo = Срок хранения истекает после { $downloadCount } или через { $timespan }
|
archiveExpiryInfo = Удалить после { $downloadCount } или через { $timespan }
|
||||||
timespanMinutes =
|
timespanMinutes =
|
||||||
{ $num ->
|
{ $num ->
|
||||||
[one] { $num } минуту
|
[one] { $num } минуту
|
||||||
|
@ -89,7 +89,7 @@ gb = ГБ
|
||||||
# localized number and byte abbreviation. example "2.5MB"
|
# localized number and byte abbreviation. example "2.5MB"
|
||||||
fileSize = { $num }{ $units }
|
fileSize = { $num }{ $units }
|
||||||
# $size is the size of the file, displayed using the fileSize message as format (e.g. "2.5MB")
|
# $size is the size of the file, displayed using the fileSize message as format (e.g. "2.5MB")
|
||||||
totalSize = Общий размер: { $size }
|
totalSize = Всего: { $size }
|
||||||
# the next line after the colon contains a file name
|
# the next line after the colon contains a file name
|
||||||
copyLinkDescription = Скопируйте ссылку, чтобы поделиться своим файлом:
|
copyLinkDescription = Скопируйте ссылку, чтобы поделиться своим файлом:
|
||||||
copyLinkButton = Копировать ссылку
|
copyLinkButton = Копировать ссылку
|
||||||
|
@ -117,13 +117,13 @@ legalTitle = Уведомление о конфиденциальности { -s
|
||||||
legalDateStamp = Версия 1.0, от 12 марта 2019 года
|
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"
|
# 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 } мин.
|
expiresDaysHoursMinutes = { $days } дн. { $hours } ч. { $minutes } мин.
|
||||||
addFilesButton = Выберите файлы для выгрузки
|
addFilesButton = Добавить
|
||||||
uploadButton = Выгрузить
|
uploadButton = Выгрузить
|
||||||
# the first part of the string 'Drag and drop files or click to send up to 1GB'
|
# the first part of the string 'Drag and drop files or click to send up to 1GB'
|
||||||
dragAndDropFiles = Перетащите файлы сюда
|
dragAndDropFiles = Перетащите файлы сюда
|
||||||
# the second part of the string 'Drag and drop files or click to send up to 1GB'
|
# the second part of the string 'Drag and drop files or click to send up to 1GB'
|
||||||
# $size is the size of the file, displayed using the fileSize message as format (e.g. "2.5MB")
|
# $size is the size of the file, displayed using the fileSize message as format (e.g. "2.5MB")
|
||||||
orClickWithSize = или щёлкните здесь, чтобы отправить их (до { $size })
|
orClickWithSize = или кликните сюда для отправки файлов до { $size }
|
||||||
addPassword = Защитить паролем
|
addPassword = Защитить паролем
|
||||||
emailPlaceholder = Введите ваш адрес электронной почты
|
emailPlaceholder = Введите ваш адрес электронной почты
|
||||||
# $size is the size of the file, displayed using the fileSize message as format (e.g. "2.5MB")
|
# $size is the size of the file, displayed using the fileSize message as format (e.g. "2.5MB")
|
||||||
|
|
|
@ -2,12 +2,12 @@ title = Send
|
||||||
importingFile = 正在导入…
|
importingFile = 正在导入…
|
||||||
encryptingFile = 正在加密…
|
encryptingFile = 正在加密…
|
||||||
decryptingFile = 正在解密…
|
decryptingFile = 正在解密…
|
||||||
downloadCount =
|
downloadCount = { $num ->
|
||||||
{ $num ->
|
[one] 1 次下载
|
||||||
*[other] { $num } 次下载
|
*[other] { $num } 次下载
|
||||||
}
|
}
|
||||||
timespanHours =
|
timespanHours = { $num ->
|
||||||
{ $num ->
|
[one] 1 小时
|
||||||
*[other] { $num } 小时
|
*[other] { $num } 小时
|
||||||
}
|
}
|
||||||
copiedUrl = 已复制!
|
copiedUrl = 已复制!
|
||||||
|
@ -26,6 +26,11 @@ notSupportedOutdatedDetail = 很可惜,此版本的 Firefox 不支持 Send 所
|
||||||
updateFirefox = 更新 Firefox
|
updateFirefox = 更新 Firefox
|
||||||
deletePopupCancel = 取消
|
deletePopupCancel = 取消
|
||||||
deleteButtonHover = 删除
|
deleteButtonHover = 删除
|
||||||
|
footerText = 不附属于 Mozilla 或 Firefox。
|
||||||
|
footerLinkDonate = 捐助
|
||||||
|
footerLinkCli = 命令行
|
||||||
|
footerLinkDmca = DMCA
|
||||||
|
footerLinkSource = 源代码
|
||||||
passwordTryAgain = 密码不正确。请重试。
|
passwordTryAgain = 密码不正确。请重试。
|
||||||
javascriptRequired = Send 需要 JavaScript
|
javascriptRequired = Send 需要 JavaScript
|
||||||
whyJavascript = 为什么 Send 需要 JavaScript?
|
whyJavascript = 为什么 Send 需要 JavaScript?
|
||||||
|
@ -142,5 +147,4 @@ shareLinkDescription = 您的文件链接:
|
||||||
shareLinkButton = 分享链接
|
shareLinkButton = 分享链接
|
||||||
# $name is the name of the file
|
# $name is the name of the file
|
||||||
shareMessage = 使用 { -send-brand } 下载“{ $name }”:简单、安全的文件分享服务
|
shareMessage = 使用 { -send-brand } 下载“{ $name }”:简单、安全的文件分享服务
|
||||||
trailheadPromo = 捍卫隐私不是幻想。加入 Firefox 一同抗争。
|
|
||||||
learnMore = 详细了解。
|
learnMore = 详细了解。
|
||||||
|
|
|
@ -2,12 +2,12 @@ title = Send
|
||||||
importingFile = 匯入中…
|
importingFile = 匯入中…
|
||||||
encryptingFile = 加密中…
|
encryptingFile = 加密中…
|
||||||
decryptingFile = 解密中…
|
decryptingFile = 解密中…
|
||||||
downloadCount =
|
downloadCount = { $num ->
|
||||||
{ $num ->
|
[one] 1 次下載
|
||||||
*[other] { $num } 次下載
|
*[other] { $num } 次下載
|
||||||
}
|
}
|
||||||
timespanHours =
|
timespanHours = { $num ->
|
||||||
{ $num ->
|
[one] 1 小時
|
||||||
*[other] { $num } 小時
|
*[other] { $num } 小時
|
||||||
}
|
}
|
||||||
copiedUrl = 已複製!
|
copiedUrl = 已複製!
|
||||||
|
@ -26,6 +26,11 @@ notSupportedOutdatedDetail = 很可惜,此版本的 Firefox 不支援 Send 所
|
||||||
updateFirefox = 更新 Firefox
|
updateFirefox = 更新 Firefox
|
||||||
deletePopupCancel = 取消
|
deletePopupCancel = 取消
|
||||||
deleteButtonHover = 刪除
|
deleteButtonHover = 刪除
|
||||||
|
footerText = 不隸屬於 Mozilla 或 Firefox。
|
||||||
|
footerLinkDonate = 捐助
|
||||||
|
footerLinkCli = 命令列
|
||||||
|
footerLinkDmca = DMCA
|
||||||
|
footerLinkSource = 原始碼
|
||||||
passwordTryAgain = 密碼不正確,請再試一次。
|
passwordTryAgain = 密碼不正確,請再試一次。
|
||||||
javascriptRequired = Send 需要開啟 JavaScript 功能
|
javascriptRequired = Send 需要開啟 JavaScript 功能
|
||||||
whyJavascript = 為什麼 Send 需要 JavaScript 才能使用?
|
whyJavascript = 為什麼 Send 需要 JavaScript 才能使用?
|
||||||
|
@ -45,27 +50,28 @@ passwordSetError = 無法設定此密碼
|
||||||
-send-short-brand = Send
|
-send-short-brand = Send
|
||||||
-firefox = Firefox
|
-firefox = Firefox
|
||||||
-mozilla = Mozilla
|
-mozilla = Mozilla
|
||||||
|
|
||||||
introTitle = 簡單而私密的檔案共享服務
|
introTitle = 簡單而私密的檔案共享服務
|
||||||
introDescription = { -send-brand } 讓您可透過點對點加密的方式來分享檔案,並提供會自動失效的鏈結。這樣一來就可以保留分享時的隱私,也確保檔案不會永久保存於網路上。
|
introDescription = { -send-brand } 讓您可透過點對點加密的方式來分享檔案,並提供會自動失效的鏈結。這樣一來就可以保留分享時的隱私,也確保檔案不會永久保存於網路上。
|
||||||
notifyUploadEncryptDone = 已加密您的檔案,可以傳送
|
notifyUploadEncryptDone = 已加密您的檔案,可以傳送
|
||||||
# downloadCount is from the downloadCount string and timespan is a timespanMinutes string. ex. 'Expires after 2 downloads or 25 minutes'
|
# downloadCount is from the downloadCount string and timespan is a timespanMinutes string. ex. 'Expires after 2 downloads or 25 minutes'
|
||||||
archiveExpiryInfo = { $downloadCount } 或 { $timespan } 後失效
|
archiveExpiryInfo = { $downloadCount } 或 { $timespan } 後失效
|
||||||
timespanMinutes =
|
timespanMinutes = { $num ->
|
||||||
{ $num ->
|
[one] 1 分鐘
|
||||||
*[other] { $num } 分鐘
|
*[other] { $num } 分鐘
|
||||||
}
|
}
|
||||||
timespanDays =
|
timespanDays = { $num ->
|
||||||
{ $num ->
|
[one] 1 天
|
||||||
*[other] { $num } 天
|
*[other] { $num } 天
|
||||||
}
|
}
|
||||||
timespanWeeks =
|
timespanWeeks = { $num ->
|
||||||
{ $num ->
|
[one] 1 週
|
||||||
*[other] { $num } 週
|
*[other] { $num } 週
|
||||||
}
|
}
|
||||||
fileCount =
|
fileCount = { $num ->
|
||||||
{ $num ->
|
[one] 1 個檔案
|
||||||
*[other] { $num } 個檔案
|
*[other] { $num } 個檔案
|
||||||
}
|
}
|
||||||
# byte abbreviation
|
# byte abbreviation
|
||||||
bytes = 位元組
|
bytes = 位元組
|
||||||
# kibibyte abbreviation
|
# kibibyte abbreviation
|
||||||
|
@ -85,15 +91,15 @@ downloadTitle = 下載檔案
|
||||||
downloadDescription = 此檔案是透過 { -send-brand } 進行分享,以點對點加密的方式來分享檔案,並提供會自動失效的鏈結。
|
downloadDescription = 此檔案是透過 { -send-brand } 進行分享,以點對點加密的方式來分享檔案,並提供會自動失效的鏈結。
|
||||||
trySendDescription = 快試試 { -send-brand },簡單安全的檔案分享機制。
|
trySendDescription = 快試試 { -send-brand },簡單安全的檔案分享機制。
|
||||||
# count will always be > 10
|
# count will always be > 10
|
||||||
tooManyFiles =
|
tooManyFiles = { $count ->
|
||||||
{ $count ->
|
[one] 一次僅能上傳 1 個檔案。
|
||||||
*[other] 一次僅能上傳 { $count } 個檔案。
|
*[other] 一次僅能上傳 { $count } 個檔案。
|
||||||
}
|
}
|
||||||
# count will always be > 10
|
# count will always be > 10
|
||||||
tooManyArchives =
|
tooManyArchives = { $count ->
|
||||||
{ $count ->
|
[one] 僅允許 1 個壓縮檔。
|
||||||
*[other] 僅允許 { $count } 個壓縮檔。
|
*[other] 僅允許 { $count } 個壓縮檔。
|
||||||
}
|
}
|
||||||
expiredTitle = 此鏈結已經失效。
|
expiredTitle = 此鏈結已經失效。
|
||||||
notSupportedDescription = 無法於此瀏覽器使用 { -send-brand }。在最新版的 { -firefox } 中使用 { -send-short-brand } 會有最佳效果,也可在大部分瀏覽器的最新版本當中使用。
|
notSupportedDescription = 無法於此瀏覽器使用 { -send-brand }。在最新版的 { -firefox } 中使用 { -send-short-brand } 會有最佳效果,也可在大部分瀏覽器的最新版本當中使用。
|
||||||
downloadFirefox = 下載 { -firefox }
|
downloadFirefox = 下載 { -firefox }
|
||||||
|
@ -136,5 +142,4 @@ shareLinkDescription = 您的檔案鏈結:
|
||||||
shareLinkButton = 分享鏈結
|
shareLinkButton = 分享鏈結
|
||||||
# $name is the name of the file
|
# $name is the name of the file
|
||||||
shareMessage = 使用 { -send-brand } 下載「{ $name }」: 簡單安全的檔案分享機制
|
shareMessage = 使用 { -send-brand } 下載「{ $name }」: 簡單安全的檔案分享機制
|
||||||
trailheadPromo = 有種方法可以保護您的隱私,加入 Firefox。
|
|
||||||
learnMore = 了解更多。
|
learnMore = 了解更多。
|
||||||
|
|
|
@ -13,6 +13,13 @@ module.exports = {
|
||||||
FOOTER_CLI_URL: config.footer_cli_url,
|
FOOTER_CLI_URL: config.footer_cli_url,
|
||||||
FOOTER_DMCA_URL: config.footer_dmca_url,
|
FOOTER_DMCA_URL: config.footer_dmca_url,
|
||||||
FOOTER_SOURCE_URL: config.footer_source_url,
|
FOOTER_SOURCE_URL: config.footer_source_url,
|
||||||
|
CUSTOM_FOOTER_TEXT: config.custom_footer_text,
|
||||||
|
CUSTOM_FOOTER_URL: config.custom_footer_url,
|
||||||
|
MAIN_NOTICE_HTML: config.main_notice_html,
|
||||||
|
UPLOAD_AREA_NOTICE_HTML: config.upload_area_notice_html,
|
||||||
|
UPLOADS_LIST_NOTICE_HTML: config.uploads_list_notice_html,
|
||||||
|
DOWNLOAD_NOTICE_HTML: config.download_notice_html,
|
||||||
|
SHOW_THUNDERBIRD_SPONSOR: config.show_thunderbird_sponsor,
|
||||||
COLORS: {
|
COLORS: {
|
||||||
PRIMARY: config.ui_color_primary,
|
PRIMARY: config.ui_color_primary,
|
||||||
ACCENT: config.ui_color_accent
|
ACCENT: config.ui_color_accent
|
||||||
|
|
|
@ -165,9 +165,20 @@ const conf = convict({
|
||||||
},
|
},
|
||||||
base_url: {
|
base_url: {
|
||||||
format: 'url',
|
format: 'url',
|
||||||
default: 'https://send.firefox.com',
|
default: 'https://send.example.com',
|
||||||
env: 'BASE_URL'
|
env: 'BASE_URL'
|
||||||
},
|
},
|
||||||
|
custom_title: {
|
||||||
|
format: String,
|
||||||
|
default: 'Send',
|
||||||
|
env: 'CUSTOM_TITLE'
|
||||||
|
},
|
||||||
|
custom_description: {
|
||||||
|
format: String,
|
||||||
|
default:
|
||||||
|
'Encrypt and send files with a link that automatically expires to ensure your important documents don’t stay online forever.',
|
||||||
|
env: 'CUSTOM_DESCRIPTION'
|
||||||
|
},
|
||||||
detect_base_url: {
|
detect_base_url: {
|
||||||
format: Boolean,
|
format: Boolean,
|
||||||
default: false,
|
default: false,
|
||||||
|
@ -243,6 +254,41 @@ const conf = convict({
|
||||||
default: 'https://github.com/timvisee/send',
|
default: 'https://github.com/timvisee/send',
|
||||||
env: 'SEND_FOOTER_SOURCE_URL'
|
env: 'SEND_FOOTER_SOURCE_URL'
|
||||||
},
|
},
|
||||||
|
custom_footer_text: {
|
||||||
|
format: String,
|
||||||
|
default: '',
|
||||||
|
env: 'CUSTOM_FOOTER_TEXT'
|
||||||
|
},
|
||||||
|
custom_footer_url: {
|
||||||
|
format: String,
|
||||||
|
default: '',
|
||||||
|
env: 'CUSTOM_FOOTER_URL'
|
||||||
|
},
|
||||||
|
main_notice_html: {
|
||||||
|
format: String,
|
||||||
|
default: '',
|
||||||
|
env: 'SEND_MAIN_NOTICE_HTML'
|
||||||
|
},
|
||||||
|
upload_area_notice_html: {
|
||||||
|
format: String,
|
||||||
|
default: '',
|
||||||
|
env: 'SEND_UPLOAD_AREA_NOTICE_HTML'
|
||||||
|
},
|
||||||
|
uploads_list_notice_html: {
|
||||||
|
format: String,
|
||||||
|
default: '',
|
||||||
|
env: 'SEND_UPLOADS_LIST_NOTICE_HTML'
|
||||||
|
},
|
||||||
|
download_notice_html: {
|
||||||
|
format: String,
|
||||||
|
default: '',
|
||||||
|
env: 'SEND_DOWNLOAD_NOTICE_HTML'
|
||||||
|
},
|
||||||
|
show_thunderbird_sponsor: {
|
||||||
|
format: Boolean,
|
||||||
|
default: false,
|
||||||
|
env: 'SHOW_THUNDERBIRD_SPONSOR'
|
||||||
|
},
|
||||||
ui_color_primary: {
|
ui_color_primary: {
|
||||||
format: String,
|
format: String,
|
||||||
default: '#0a84ff',
|
default: '#0a84ff',
|
||||||
|
@ -253,6 +299,11 @@ const conf = convict({
|
||||||
default: '#003eaa',
|
default: '#003eaa',
|
||||||
env: 'UI_COLOR_ACCENT'
|
env: 'UI_COLOR_ACCENT'
|
||||||
},
|
},
|
||||||
|
custom_locale: {
|
||||||
|
format: String,
|
||||||
|
default: '',
|
||||||
|
env: 'CUSTOM_LOCALE'
|
||||||
|
},
|
||||||
ui_custom_assets: {
|
ui_custom_assets: {
|
||||||
android_chrome_192px: {
|
android_chrome_192px: {
|
||||||
format: String,
|
format: String,
|
||||||
|
@ -303,6 +354,11 @@ const conf = convict({
|
||||||
format: String,
|
format: String,
|
||||||
default: '',
|
default: '',
|
||||||
env: 'UI_CUSTOM_ASSETS_WORDMARK'
|
env: 'UI_CUSTOM_ASSETS_WORDMARK'
|
||||||
|
},
|
||||||
|
custom_css: {
|
||||||
|
format: String,
|
||||||
|
default: '',
|
||||||
|
env: 'UI_CUSTOM_CSS'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -3,6 +3,10 @@ const assets = require('../common/assets');
|
||||||
const initScript = require('./initScript');
|
const initScript = require('./initScript');
|
||||||
|
|
||||||
module.exports = function(state, body = '') {
|
module.exports = function(state, body = '') {
|
||||||
|
const custom_css = state.ui.assets.custom_css !== ''
|
||||||
|
? html`<link rel="stylesheet" type="text/css" href="${state.ui.assets.custom_css}" />`
|
||||||
|
: ''
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="${state.locale}">
|
<html lang="${state.locale}">
|
||||||
|
@ -40,6 +44,7 @@ module.exports = function(state, body = '') {
|
||||||
type="text/css"
|
type="text/css"
|
||||||
href="${assets.get('app.css')}"
|
href="${assets.get('app.css')}"
|
||||||
/>
|
/>
|
||||||
|
${custom_css}
|
||||||
<link
|
<link
|
||||||
rel="apple-touch-icon"
|
rel="apple-touch-icon"
|
||||||
sizes="180x180"
|
sizes="180x180"
|
||||||
|
|
|
@ -1,13 +1,15 @@
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
const { FluentBundle } = require('@fluent/bundle');
|
const { FluentBundle, FluentResource } = require('@fluent/bundle');
|
||||||
const localesPath = path.resolve(__dirname, '../public/locales');
|
const localesPath = path.resolve(__dirname, '../public/locales');
|
||||||
const locales = fs.readdirSync(localesPath);
|
const locales = fs.readdirSync(localesPath);
|
||||||
|
|
||||||
function makeBundle(locale) {
|
function makeBundle(locale) {
|
||||||
const bundle = new FluentBundle(locale, { useIsolating: false });
|
const bundle = new FluentBundle(locale, { useIsolating: false });
|
||||||
bundle.addMessages(
|
bundle.addResource(
|
||||||
fs.readFileSync(path.resolve(localesPath, locale, 'send.ftl'), 'utf8')
|
new FluentResource(
|
||||||
|
fs.readFileSync(path.resolve(localesPath, locale, 'send.ftl'), 'utf8')
|
||||||
|
)
|
||||||
);
|
);
|
||||||
return [locale, bundle];
|
return [locale, bundle];
|
||||||
}
|
}
|
||||||
|
@ -19,8 +21,11 @@ module.exports = function getTranslator(locale) {
|
||||||
const bundle = bundles.get(locale) || defaultBundle;
|
const bundle = bundles.get(locale) || defaultBundle;
|
||||||
return function(id, data) {
|
return function(id, data) {
|
||||||
if (bundle.hasMessage(id)) {
|
if (bundle.hasMessage(id)) {
|
||||||
return bundle.format(bundle.getMessage(id), data);
|
return bundle.formatPattern(bundle.getMessage(id).value, data);
|
||||||
}
|
}
|
||||||
return defaultBundle.format(defaultBundle.getMessage(id), data);
|
return defaultBundle.formatPattern(
|
||||||
|
defaultBundle.getMessage(id).value,
|
||||||
|
data
|
||||||
|
);
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
const assert = require('assert');
|
const assert = require('assert');
|
||||||
const crypto = require('crypto');
|
const crypto = require('crypto');
|
||||||
const storage = require('../storage');
|
const storage = require('../storage');
|
||||||
|
const config = require('../config');
|
||||||
const fxa = require('../fxa');
|
const fxa = require('../fxa');
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
@ -70,10 +71,11 @@ module.exports = {
|
||||||
const token = authHeader.split(' ')[1];
|
const token = authHeader.split(' ')[1];
|
||||||
req.user = await fxa.verify(token);
|
req.user = await fxa.verify(token);
|
||||||
}
|
}
|
||||||
if (req.user) {
|
|
||||||
next();
|
if (config.fxa_required && !req.user) {
|
||||||
} else {
|
|
||||||
res.sendStatus(401);
|
res.sendStatus(401);
|
||||||
|
} else {
|
||||||
|
next();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -3,9 +3,18 @@ const layout = require('./layout');
|
||||||
const assets = require('../common/assets');
|
const assets = require('../common/assets');
|
||||||
const getTranslator = require('./locale');
|
const getTranslator = require('./locale');
|
||||||
const { getFxaConfig } = require('./fxa');
|
const { getFxaConfig } = require('./fxa');
|
||||||
|
const fs = require('fs');
|
||||||
|
const path = require('path');
|
||||||
|
|
||||||
module.exports = async function(req) {
|
module.exports = async function(req) {
|
||||||
const locale = req.language || 'en-US';
|
const locale = (() => {
|
||||||
|
if (config.custom_locale != '' && fs.existsSync(path.join(__dirname,'../public/locales',config.custom_locale))) {
|
||||||
|
return config.custom_locale;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return req.language || 'en-US';
|
||||||
|
}
|
||||||
|
})();
|
||||||
let authConfig = null;
|
let authConfig = null;
|
||||||
let robots = 'none';
|
let robots = 'none';
|
||||||
if (req.route && req.route.path === '/') {
|
if (req.route && req.route.path === '/') {
|
||||||
|
@ -34,7 +43,8 @@ module.exports = async function(req) {
|
||||||
safari_pinned_tab: assets.get('safari-pinned-tab.svg'),
|
safari_pinned_tab: assets.get('safari-pinned-tab.svg'),
|
||||||
facebook: baseUrl + '/' + assets.get('send-fb.jpg'),
|
facebook: baseUrl + '/' + assets.get('send-fb.jpg'),
|
||||||
twitter: baseUrl + '/' + assets.get('send-twitter.jpg'),
|
twitter: baseUrl + '/' + assets.get('send-twitter.jpg'),
|
||||||
wordmark: assets.get('wordmark.svg') + '#logo'
|
wordmark: assets.get('wordmark.svg') + '#logo',
|
||||||
|
custom_css: ''
|
||||||
};
|
};
|
||||||
Object.keys(uiAssets).forEach(index => {
|
Object.keys(uiAssets).forEach(index => {
|
||||||
if (config.ui_custom_assets[index] !== '')
|
if (config.ui_custom_assets[index] !== '')
|
||||||
|
@ -47,9 +57,8 @@ module.exports = async function(req) {
|
||||||
locale,
|
locale,
|
||||||
capabilities: { account: false },
|
capabilities: { account: false },
|
||||||
translate: getTranslator(locale),
|
translate: getTranslator(locale),
|
||||||
title: 'Send',
|
title: config.custom_title,
|
||||||
description:
|
description: config.custom_description,
|
||||||
'Encrypt and send files with a link that automatically expires to ensure your important documents don’t stay online forever.',
|
|
||||||
baseUrl,
|
baseUrl,
|
||||||
ui: {
|
ui: {
|
||||||
colors: {
|
colors: {
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
const promisify = require('util').promisify;
|
const promisify = require('util').promisify;
|
||||||
const mkdirp = require('mkdirp');
|
|
||||||
|
|
||||||
const stat = promisify(fs.stat);
|
const stat = promisify(fs.stat);
|
||||||
|
|
||||||
|
@ -9,7 +8,9 @@ class FSStorage {
|
||||||
constructor(config, log) {
|
constructor(config, log) {
|
||||||
this.log = log;
|
this.log = log;
|
||||||
this.dir = config.file_dir;
|
this.dir = config.file_dir;
|
||||||
mkdirp.sync(this.dir);
|
fs.mkdirSync(this.dir, {
|
||||||
|
recursive: true
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async length(id) {
|
async length(id) {
|
||||||
|
|
|
@ -23,6 +23,7 @@ const colors = {
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
purge: false,
|
||||||
theme: {
|
theme: {
|
||||||
colors: colors,
|
colors: colors,
|
||||||
screens: {
|
screens: {
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
/* eslint-disable no-undef, no-process-exit */
|
/* eslint-disable no-undef, no-process-exit */
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
const mkdirp = require('mkdirp');
|
|
||||||
const puppeteer = require('puppeteer');
|
const puppeteer = require('puppeteer');
|
||||||
const webpack = require('webpack');
|
const webpack = require('webpack');
|
||||||
const config = require('../../webpack.config');
|
const config = require('../../webpack.config');
|
||||||
|
@ -44,7 +43,9 @@ const server = app.listen(async function() {
|
||||||
const coverage = await page.evaluate(() => __coverage__);
|
const coverage = await page.evaluate(() => __coverage__);
|
||||||
if (coverage) {
|
if (coverage) {
|
||||||
const dir = path.resolve(__dirname, '../../.nyc_output');
|
const dir = path.resolve(__dirname, '../../.nyc_output');
|
||||||
mkdirp.sync(dir);
|
fs.mkdirSync(dir, {
|
||||||
|
recursive: true
|
||||||
|
});
|
||||||
fs.writeFileSync(
|
fs.writeFileSync(
|
||||||
path.resolve(dir, 'frontend.json'),
|
path.resolve(dir, 'frontend.json'),
|
||||||
JSON.stringify(coverage)
|
JSON.stringify(coverage)
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
const mkdirp = require('mkdirp');
|
const fs = require('fs');
|
||||||
const rimraf = require('rimraf');
|
const rimraf = require('rimraf');
|
||||||
const dir = path.join(__dirname, 'integration', 'downloads');
|
const dir = path.join(__dirname, 'integration', 'downloads');
|
||||||
|
|
||||||
mkdirp.sync(dir);
|
fs.mkdirSync(dir, {
|
||||||
|
recursive: true
|
||||||
|
});
|
||||||
rimraf.sync(`${dir}${path.sep}*`);
|
rimraf.sync(`${dir}${path.sep}*`);
|
||||||
|
|
||||||
exports.config = {
|
exports.config = {
|
||||||
|
|
|
@ -6,6 +6,13 @@ const VersionPlugin = require('./build/version_plugin');
|
||||||
const AndroidIndexPlugin = require('./build/android_index_plugin');
|
const AndroidIndexPlugin = require('./build/android_index_plugin');
|
||||||
const ExtractTextPlugin = require('extract-text-webpack-plugin');
|
const ExtractTextPlugin = require('extract-text-webpack-plugin');
|
||||||
|
|
||||||
|
// Fix for node 18+
|
||||||
|
// See: <https://stackoverflow.com/a/78005686/1000145>
|
||||||
|
const crypto = require('crypto');
|
||||||
|
const crypto_orig_createHash = crypto.createHash;
|
||||||
|
crypto.createHash = algorithm =>
|
||||||
|
crypto_orig_createHash(algorithm == 'md4' ? 'sha256' : algorithm);
|
||||||
|
|
||||||
const webJsOptions = {
|
const webJsOptions = {
|
||||||
babelrc: false,
|
babelrc: false,
|
||||||
presets: [
|
presets: [
|
||||||
|
@ -42,7 +49,8 @@ const serviceWorker = {
|
||||||
test: /\.(png|jpg)$/,
|
test: /\.(png|jpg)$/,
|
||||||
loader: 'file-loader',
|
loader: 'file-loader',
|
||||||
options: {
|
options: {
|
||||||
name: '[name].[contenthash:8].[ext]'
|
name: '[name].[contenthash:8].[ext]',
|
||||||
|
esModule: false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -51,16 +59,26 @@ const serviceWorker = {
|
||||||
{
|
{
|
||||||
loader: 'file-loader',
|
loader: 'file-loader',
|
||||||
options: {
|
options: {
|
||||||
name: '[name].[contenthash:8].[ext]'
|
name: '[name].[contenthash:8].[ext]',
|
||||||
|
esModule: false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
loader: 'svgo-loader',
|
loader: 'svgo-loader',
|
||||||
options: {
|
options: {
|
||||||
plugins: [
|
plugins: [
|
||||||
{ removeViewBox: false }, // true causes stretched images
|
{
|
||||||
{ convertStyleToAttrs: true }, // for CSP, no unsafe-eval
|
name: 'removeViewBox',
|
||||||
{ removeTitle: true } // for smallness
|
active: false // true causes stretched images
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'convertStyleToAttrs',
|
||||||
|
active: true // for CSP, no unsafe-eval
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'removeTitle',
|
||||||
|
active: true // for smallness
|
||||||
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -127,7 +145,8 @@ const web = {
|
||||||
test: /\.(png|jpg)$/,
|
test: /\.(png|jpg)$/,
|
||||||
loader: 'file-loader',
|
loader: 'file-loader',
|
||||||
options: {
|
options: {
|
||||||
name: '[name].[contenthash:8].[ext]'
|
name: '[name].[contenthash:8].[ext]',
|
||||||
|
esModule: false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -136,17 +155,30 @@ const web = {
|
||||||
{
|
{
|
||||||
loader: 'file-loader',
|
loader: 'file-loader',
|
||||||
options: {
|
options: {
|
||||||
name: '[name].[contenthash:8].[ext]'
|
name: '[name].[contenthash:8].[ext]',
|
||||||
|
esModule: false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
loader: 'svgo-loader',
|
loader: 'svgo-loader',
|
||||||
options: {
|
options: {
|
||||||
plugins: [
|
plugins: [
|
||||||
{ cleanupIDs: false },
|
{
|
||||||
{ removeViewBox: false }, // true causes stretched images
|
name: 'cleanupIDs',
|
||||||
{ convertStyleToAttrs: true }, // for CSP, no unsafe-eval
|
active: false
|
||||||
{ removeTitle: true } // for smallness
|
},
|
||||||
|
{
|
||||||
|
name: 'removeViewBox',
|
||||||
|
active: false // true causes stretched images
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'convertStyleToAttrs',
|
||||||
|
active: true // for CSP, no unsafe-eval
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'removeTitle',
|
||||||
|
active: true // for smallness
|
||||||
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -160,7 +192,8 @@ const web = {
|
||||||
{
|
{
|
||||||
loader: 'css-loader',
|
loader: 'css-loader',
|
||||||
options: {
|
options: {
|
||||||
importLoaders: 1
|
importLoaders: 1,
|
||||||
|
esModule: false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
'postcss-loader'
|
'postcss-loader'
|
||||||
|
@ -184,12 +217,14 @@ const web = {
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
new CopyPlugin([
|
new CopyPlugin({
|
||||||
{
|
patterns: [
|
||||||
context: 'public',
|
{
|
||||||
from: '*.*'
|
context: 'public',
|
||||||
}
|
from: '*.*'
|
||||||
]),
|
}
|
||||||
|
]
|
||||||
|
}),
|
||||||
new webpack.EnvironmentPlugin(['NODE_ENV']),
|
new webpack.EnvironmentPlugin(['NODE_ENV']),
|
||||||
new webpack.IgnorePlugin(/\.\.\/dist/), // used in common/*.js
|
new webpack.IgnorePlugin(/\.\.\/dist/), // used in common/*.js
|
||||||
new ExtractTextPlugin({
|
new ExtractTextPlugin({
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue