mirror of
https://github.com/schlagmichdoch/PairDrop.git
synced 2026-04-06 09:53:49 +00:00
Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1bb8a63eed | ||
|
|
7d581ca858 | ||
|
|
dcc4e8b747 | ||
|
|
ce549adf22 | ||
|
|
bdb39a1d2c | ||
|
|
195dfd0bb3 | ||
|
|
680ed81bd7 | ||
|
|
b7781e2bab |
@@ -9,3 +9,6 @@ RUN npm ci
|
||||
COPY . .
|
||||
|
||||
EXPOSE 3000
|
||||
|
||||
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
|
||||
CMD wget --quiet --tries=1 --spider http://localhost:3000 || exit 1
|
||||
|
||||
43
docs/docker-swarm-usage.md
Normal file
43
docs/docker-swarm-usage.md
Normal file
@@ -0,0 +1,43 @@
|
||||
# Docker Swarm Usage
|
||||
|
||||
## Healthcheck
|
||||
|
||||
The [Docker Image](../Dockerfile) includes a Healthcheck with the following options:
|
||||
|
||||
```
|
||||
--interval=30s
|
||||
```
|
||||
> Specifies the time interval at which the health check should be performed. In this case, the health check will be performed every 30 seconds.
|
||||
|
||||
<br>
|
||||
|
||||
```
|
||||
--timeout=10s
|
||||
```
|
||||
> Specifies the amount of time to wait for a response from the health check command. If the response does not arrive within 10 seconds, the health check will be considered a failure.
|
||||
|
||||
<br>
|
||||
|
||||
```
|
||||
--start-period=5s
|
||||
```
|
||||
> Specifies the amount of time to wait before starting the health check process. In this case, the health check process will begin 5 seconds after the container is started.
|
||||
|
||||
<br>
|
||||
|
||||
```
|
||||
--retries=3
|
||||
```
|
||||
> Specifies the number of times Docker should retry the health check before considering the container to be unhealthy.
|
||||
|
||||
<br>
|
||||
|
||||
|
||||
The CMD instruction is used to define the command that will be run as part of the health check.
|
||||
In this case, the command is `wget --quiet --tries=1 --spider http://localhost:3000/ || exit 1`. This command will attempt to connect to `http://localhost:3000/`
|
||||
and if it fails it will exit with a status code of `1`. If this command returns a status code other than `0`, the health check will be considered a failure.
|
||||
|
||||
Overall, this HEALTHCHECK instruction is defining a health check process that will run every 30 seconds, wait up to 10 seconds for a response,
|
||||
begin 5 seconds after the container is started, and retry up to 3 times.
|
||||
The health check will consist of attempting to connect to http://localhost:3000/ and will consider the container to be unhealthy if it is unable to connect.
|
||||
|
||||
46
docs/faq.md
46
docs/faq.md
@@ -11,6 +11,16 @@ by clicking the install-button in the top-right corner while on [pairdrop.net](h
|
||||
<img width="400" src="pwa-install.png" alt="Example on how to install a pwa with Edge">
|
||||
|
||||
On Firefox, PWAs are installable via [this browser extensions](https://addons.mozilla.org/de/firefox/addon/pwas-for-firefox/)
|
||||
|
||||
<br>
|
||||
|
||||
<b>Self-Hosted Instance?</b>
|
||||
|
||||
To be able to install the PWA from a self-hosted instance, the connection needs to be [established through HTTPS](https://developer.mozilla.org/en-US/docs/Web/Progressive_web_apps/Installable_PWAs).
|
||||
See [this host your own section](https://github.com/schlagmichdoch/PairDrop/blob/master/docs/host-your-own.md#testing-pwa-related-features) for more information.
|
||||
|
||||
<br>
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
@@ -23,6 +33,9 @@ Shortcuts!
|
||||
- Close all send and pair dialogs by pressing `Escape`.
|
||||
- Copy a received message to clipboard with `CTRL/⌘ + C`.
|
||||
- Accept file transfer request with `Enter` and decline with `Escape`.
|
||||
|
||||
<br>
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
@@ -37,6 +50,9 @@ iOS Shortcuts to the win:
|
||||
I created a simple iOS shortcut that takes your photos and saves them to your gallery:
|
||||
https://routinehub.co/shortcut/13988/
|
||||
|
||||
|
||||
<br>
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
@@ -49,6 +65,9 @@ Yes, it finally is!
|
||||
* [Send directly from share menu on iOS](/docs/how-to.md#send-directly-from-share-menu-on-ios)
|
||||
* [Send directly from share menu on Android](/docs/how-to.md#send-directly-from-share-menu-on-android)
|
||||
|
||||
|
||||
<br>
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
@@ -60,6 +79,9 @@ Yes, it is!
|
||||
|
||||
* [Send directly from command-line interface](/docs/how-to.md#send-directly-via-command-line-interface)
|
||||
|
||||
|
||||
<br>
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
@@ -72,6 +94,9 @@ Here's a list of some third-party apps compatible with PairDrop:
|
||||
1. [Snapdrop Android App](https://github.com/fm-sys/snapdrop-android)
|
||||
2. [Snapdrop for Firefox (Addon)](https://github.com/ueen/SnapdropFirefoxAddon)
|
||||
3. Feel free to make one :)
|
||||
|
||||
<br>
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
@@ -83,6 +108,9 @@ It uses a P2P connection if WebRTC is supported by the browser. WebRTC needs a S
|
||||
|
||||
If your devices are paired and behind a NAT, the public TURN Server from [Open Relay](https://www.metered.ca/tools/openrelay/) is used to route your files and messages.
|
||||
|
||||
|
||||
<br>
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
@@ -95,6 +123,9 @@ WebRTC encrypts the files on transit.
|
||||
|
||||
If your devices are paired and behind a NAT, the public TURN Server from [Open Relay](https://www.metered.ca/tools/openrelay/) is used to route your files and messages.
|
||||
|
||||
|
||||
<br>
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
@@ -104,6 +135,9 @@ If your devices are paired and behind a NAT, the public TURN Server from [Open R
|
||||
|
||||
Yes. Your files are sent using WebRTC, which encrypts them on transit. To ensure the connection is secure and there is no MITM, compare the security number shown under the device name on both devices. The security number is different for every connection.
|
||||
|
||||
|
||||
<br>
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
@@ -124,6 +158,9 @@ Alternatively, you can open a hotspot on one of your devices to bridge the conne
|
||||
You can also use mobile hotspots on phones to do that.
|
||||
Then, all data should be sent directly between devices and your data plan should not be charged.
|
||||
|
||||
|
||||
<br>
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
@@ -136,6 +173,9 @@ We are not trying to optimize for some edge-cases. We are optimizing the user fl
|
||||
|
||||
If you want to learn more about simplicity you can read [Insanely Simple: The Obsession that Drives Apple's Success](https://www.amazon.com/Insanely-Simple-Ken-Segall-audiobook/dp/B007Z9686O) or [Thinking, Fast and Slow](https://www.amazon.com/Thinking-Fast-Slow-Daniel-Kahneman/dp/0374533555).
|
||||
|
||||
|
||||
<br>
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
@@ -150,6 +190,9 @@ If you want to learn more about simplicity you can read [Insanely Simple: The Ob
|
||||
* Do security analysis and suggestions
|
||||
* To support the original Snapdrop and its creator go to [his GitHub page](https://github.com/RobinLinus/snapdrop)
|
||||
|
||||
|
||||
<br>
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
@@ -158,6 +201,9 @@ If you want to learn more about simplicity you can read [Insanely Simple: The Ob
|
||||
</summary>
|
||||
|
||||
[See here for Information about the Technical Implementation](/docs/technical-documentation.md)
|
||||
|
||||
<br>
|
||||
|
||||
</details>
|
||||
|
||||
[< Back](/README.md)
|
||||
|
||||
@@ -82,6 +82,8 @@ docker run -d --restart=unless-stopped --name=pairdrop -p 127.0.0.1:3000:3000 gh
|
||||
>
|
||||
> To specify options replace `npm run start:prod` according to [the documentation below.](#options--flags-1)
|
||||
|
||||
> The Docker Image includes a Healthcheck. To learn more see [Docker Swarm Usage](./docker-swarm-usage.md#docker-swarm-usage)
|
||||
|
||||
### Docker Image self-built
|
||||
#### Build the image
|
||||
```bash
|
||||
@@ -101,6 +103,8 @@ docker run -d --restart=unless-stopped --name=pairdrop -p 127.0.0.1:3000:3000 -i
|
||||
>
|
||||
> To specify options replace `npm run start:prod` according to [the documentation below.](#options--flags-1)
|
||||
|
||||
> The Docker Image includes a Healthcheck. To learn more see [Docker Swarm Usage](./docker-swarm-usage.md#docker-swarm-usage)
|
||||
|
||||
<br>
|
||||
|
||||
## Deployment with Docker Compose
|
||||
|
||||
4
package-lock.json
generated
4
package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "pairdrop",
|
||||
"version": "1.5.0",
|
||||
"version": "1.5.1",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "pairdrop",
|
||||
"version": "1.5.0",
|
||||
"version": "1.5.1",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"express": "^4.18.2",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "pairdrop",
|
||||
"version": "1.5.0",
|
||||
"version": "1.5.1",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
|
||||
@@ -1943,68 +1943,84 @@ Events.on('load', () => {
|
||||
style.zIndex = -1;
|
||||
style.top = 0;
|
||||
style.left = 0;
|
||||
let ctx = c.getContext('2d');
|
||||
let cCtx = c.getContext('2d');
|
||||
let x0, y0, w, h, dw, offset;
|
||||
|
||||
let offscreenCanvases = [];
|
||||
|
||||
function init() {
|
||||
let oldW = w;
|
||||
let oldH = h;
|
||||
let oldOffset = offset
|
||||
w = document.documentElement.clientWidth;
|
||||
h = document.documentElement.clientHeight;
|
||||
c.width = w;
|
||||
c.height = h;
|
||||
offset = $$('footer').offsetHeight - 32;
|
||||
if (h > 800) offset += 16;
|
||||
|
||||
if (oldW === w && oldH === h && oldOffset === offset) return; // nothing has changed
|
||||
|
||||
c.width = w;
|
||||
c.height = h;
|
||||
x0 = w / 2;
|
||||
y0 = h - offset;
|
||||
dw = Math.max(w, h, 1000) / 13;
|
||||
drawCircles();
|
||||
dw = Math.round(Math.max(w, h, 1000) / 13);
|
||||
drawCircles(cCtx, 0);
|
||||
|
||||
// enforce redrawing of frames
|
||||
offscreenCanvases = [];
|
||||
}
|
||||
Events.on('bg-resize', _ => init());
|
||||
window.onresize = _ => Events.fire('bg-resize');
|
||||
|
||||
function drawCircle(radius) {
|
||||
function drawCircle(ctx, radius) {
|
||||
ctx.beginPath();
|
||||
let color = Math.round(255 * (1 - radius / Math.max(w, h)));
|
||||
ctx.strokeStyle = 'rgba(' + color + ',' + color + ',' + color + ',0.1)';
|
||||
ctx.lineWidth = 2;
|
||||
let opacity = 0.2 * (1 - 1.2 * radius / Math.max(w, h));
|
||||
ctx.strokeStyle = `rgb(128, 128, 128, ${opacity})`;
|
||||
ctx.arc(x0, y0, radius, 0, 2 * Math.PI);
|
||||
ctx.stroke();
|
||||
ctx.lineWidth = 2;
|
||||
}
|
||||
|
||||
let step = 0;
|
||||
|
||||
function drawCircles() {
|
||||
ctx.clearRect(0, 0, w, h);
|
||||
for (let i = 0; i < 8; i++) {
|
||||
drawCircle(dw * i + step % dw);
|
||||
}
|
||||
step += 1;
|
||||
}
|
||||
|
||||
let loading = true;
|
||||
|
||||
function animate() {
|
||||
if (loading || !finished()) {
|
||||
requestAnimationFrame(function() {
|
||||
drawCircles();
|
||||
animate();
|
||||
});
|
||||
function drawCircles(ctx, frame) {
|
||||
for (let i = 0; i < 13; i++) {
|
||||
drawCircle(ctx, dw * i + frame);
|
||||
}
|
||||
}
|
||||
|
||||
function finished() {
|
||||
return step % dw >= dw - 5;
|
||||
function createOffscreenCanvas(frame) {
|
||||
let canvas = document.createElement("canvas");
|
||||
canvas.width = c.width;
|
||||
canvas.height = c.height;
|
||||
offscreenCanvases[frame] = canvas;
|
||||
let ctx = canvas.getContext('2d');
|
||||
drawCircles(ctx, frame);
|
||||
}
|
||||
|
||||
function drawFrame(frame) {
|
||||
cCtx.clearRect(0, 0, w, h);
|
||||
if (!offscreenCanvases[frame]) {
|
||||
createOffscreenCanvas(frame);
|
||||
}
|
||||
cCtx.drawImage(offscreenCanvases[frame], 0, 0);
|
||||
}
|
||||
|
||||
let animate = true;
|
||||
let currentFrame = 0;
|
||||
|
||||
function animateBg() {
|
||||
if (currentFrame + 1 < dw || animate) {
|
||||
currentFrame = (currentFrame + 1) % dw;
|
||||
drawFrame(currentFrame);
|
||||
}
|
||||
setTimeout(_ => animateBg(), 3000 / dw);
|
||||
}
|
||||
|
||||
window.animateBackground = function(l) {
|
||||
if (!l) {
|
||||
loading = false;
|
||||
} else if (!loading) {
|
||||
loading = true;
|
||||
if (finished()) animate();
|
||||
}
|
||||
animate = l;
|
||||
};
|
||||
|
||||
init();
|
||||
animate();
|
||||
animateBg();
|
||||
});
|
||||
|
||||
document.changeFavicon = function (src) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
const cacheVersion = 'v1.5.0';
|
||||
const cacheVersion = 'v1.5.1';
|
||||
const cacheTitle = `pairdrop-cache-${cacheVersion}`;
|
||||
const urlsToCache = [
|
||||
'index.html',
|
||||
|
||||
@@ -1944,67 +1944,84 @@ Events.on('load', () => {
|
||||
style.zIndex = -1;
|
||||
style.top = 0;
|
||||
style.left = 0;
|
||||
let ctx = c.getContext('2d');
|
||||
let cCtx = c.getContext('2d');
|
||||
let x0, y0, w, h, dw, offset;
|
||||
|
||||
let offscreenCanvases = [];
|
||||
|
||||
function init() {
|
||||
let oldW = w;
|
||||
let oldH = h;
|
||||
let oldOffset = offset
|
||||
w = document.documentElement.clientWidth;
|
||||
h = document.documentElement.clientHeight;
|
||||
offset = $$('footer').offsetHeight - 32;
|
||||
if (h > 800) offset += 16;
|
||||
|
||||
if (oldW === w && oldH === h && oldOffset === offset) return; // nothing has changed
|
||||
|
||||
c.width = w;
|
||||
c.height = h;
|
||||
offset = $$('footer').offsetHeight - 32;
|
||||
x0 = w / 2;
|
||||
y0 = h - offset;
|
||||
dw = Math.max(w, h, 1000) / 13;
|
||||
drawCircles();
|
||||
dw = Math.round(Math.max(w, h, 1000) / 13);
|
||||
drawCircles(cCtx, 0);
|
||||
|
||||
// enforce redrawing of frames
|
||||
offscreenCanvases = [];
|
||||
}
|
||||
Events.on('bg-resize', _ => init());
|
||||
window.onresize = _ => Events.fire('bg-resize');
|
||||
|
||||
function drawCircle(radius) {
|
||||
function drawCircle(ctx, radius) {
|
||||
ctx.beginPath();
|
||||
let color = Math.round(255 * (1 - radius / Math.max(w, h)));
|
||||
ctx.strokeStyle = 'rgba(' + color + ',' + color + ',' + color + ',0.1)';
|
||||
ctx.lineWidth = 2;
|
||||
let opacity = 0.2 * (1 - 1.2 * radius / Math.max(w, h));
|
||||
ctx.strokeStyle = `rgb(128, 128, 128, ${opacity})`;
|
||||
ctx.arc(x0, y0, radius, 0, 2 * Math.PI);
|
||||
ctx.stroke();
|
||||
ctx.lineWidth = 2;
|
||||
}
|
||||
|
||||
let step = 0;
|
||||
|
||||
function drawCircles() {
|
||||
ctx.clearRect(0, 0, w, h);
|
||||
for (let i = 0; i < 8; i++) {
|
||||
drawCircle(dw * i + step % dw);
|
||||
}
|
||||
step += 1;
|
||||
}
|
||||
|
||||
let loading = true;
|
||||
|
||||
function animate() {
|
||||
if (loading || !finished()) {
|
||||
requestAnimationFrame(function() {
|
||||
drawCircles();
|
||||
animate();
|
||||
});
|
||||
function drawCircles(ctx, frame) {
|
||||
for (let i = 0; i < 13; i++) {
|
||||
drawCircle(ctx, dw * i + frame);
|
||||
}
|
||||
}
|
||||
|
||||
function finished() {
|
||||
return step % dw >= dw - 5;
|
||||
function createOffscreenCanvas(frame) {
|
||||
let canvas = document.createElement("canvas");
|
||||
canvas.width = c.width;
|
||||
canvas.height = c.height;
|
||||
offscreenCanvases[frame] = canvas;
|
||||
let ctx = canvas.getContext('2d');
|
||||
drawCircles(ctx, frame);
|
||||
}
|
||||
|
||||
function drawFrame(frame) {
|
||||
cCtx.clearRect(0, 0, w, h);
|
||||
if (!offscreenCanvases[frame]) {
|
||||
createOffscreenCanvas(frame);
|
||||
}
|
||||
cCtx.drawImage(offscreenCanvases[frame], 0, 0);
|
||||
}
|
||||
|
||||
let animate = true;
|
||||
let currentFrame = 0;
|
||||
|
||||
function animateBg() {
|
||||
if (currentFrame + 1 < dw || animate) {
|
||||
currentFrame = (currentFrame + 1) % dw;
|
||||
drawFrame(currentFrame);
|
||||
}
|
||||
setTimeout(_ => animateBg(), 3000 / dw);
|
||||
}
|
||||
|
||||
window.animateBackground = function(l) {
|
||||
if (!l) {
|
||||
loading = false;
|
||||
} else if (!loading) {
|
||||
loading = true;
|
||||
if (finished()) animate();
|
||||
}
|
||||
animate = l;
|
||||
};
|
||||
|
||||
init();
|
||||
animate();
|
||||
animateBg();
|
||||
});
|
||||
|
||||
document.changeFavicon = function (src) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
const cacheVersion = 'v1.5.0';
|
||||
const cacheVersion = 'v1.5.1';
|
||||
const cacheTitle = `pairdrop-included-ws-fallback-cache-${cacheVersion}`;
|
||||
const urlsToCache = [
|
||||
'index.html',
|
||||
|
||||
Reference in New Issue
Block a user