diff --git a/.dockerignore b/.dockerignore
index d7e7b41..1b73304 100644
--- a/.dockerignore
+++ b/.dockerignore
@@ -1,13 +1,5 @@
+node_modules
.github
.git*
-.idea
-dev
-docs
-licenses
-node_modules
-pairdrop-cli
-*.md
-*.yml
-Dockerfile
-rtc_config_example.json
-turnserver_example.conf
\ No newline at end of file
+
+*.md
\ No newline at end of file
diff --git a/.github/ISSUE_TEMPLATE/bug-report.md b/.github/ISSUE_TEMPLATE/bug-report.md
index 5abfbb4..cbb6d42 100644
--- a/.github/ISSUE_TEMPLATE/bug-report.md
+++ b/.github/ISSUE_TEMPLATE/bug-report.md
@@ -1,8 +1,8 @@
---
name: Bug Report
-about: Create a report to help us improve. Please check the FAQ first.
-title: '[Bug] '
-labels: 'bug'
+about: Create a report to help us improve
+title: 'Bug:/Enhancement:/Feature Request: '
+labels: ''
assignees: ''
---
@@ -34,17 +34,12 @@ If applicable, add screenshots to help explain your problem.
- Browser [e.g. stock browser, safari]
- Version [e.g. 22]
-**Bug occurs on official PairDrop instance https://pairdrop.net/**
-No | Yes
-Version: v1.11.2
-
-**Bug occurs on self-hosted PairDrop instance**
+**Self-Hosted**
No | Yes
**Self-Hosted Setup**
Proxy: Nginx | Apache2
-Deployment: docker run | docker compose | npm run start:prod
-Version: v1.11.2
+Deployment: docker run | docker-compose | npm run start:prod
**Additional context**
Add any other context about the problem here.
diff --git a/.github/ISSUE_TEMPLATE/enhancement.md b/.github/ISSUE_TEMPLATE/enhancement.md
deleted file mode 100644
index ba85fef..0000000
--- a/.github/ISSUE_TEMPLATE/enhancement.md
+++ /dev/null
@@ -1,20 +0,0 @@
----
-name: Enhancement
-about: Enhancements and feature requests are always welcome. See discussions regarding central topics.
-title: '[Enhancement] '
-labels: 'enhancement'
-assignees: ''
-
----
-
-**What problem is solved by the new feature**
-What's the motivation for this topic
-
-**Describe the feature**
-A clear and concise description of what the new feature/enhancement is.
-
-**Drafts**
-Screenshots of Draw.io graph or drawn sketch.
-
-**Additional context**
-Add any other context here.
diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml
index 9d706b6..bf479b3 100644
--- a/.github/workflows/docker-image.yml
+++ b/.github/workflows/docker-image.yml
@@ -1,14 +1,3 @@
-# This workflow uses actions that are not certified by GitHub.
-# They are provided by a third-party and are governed by
-# separate terms of service, privacy policy, and support
-# documentation.
-
-# GitHub recommends pinning actions to a commit SHA.
-# To get a newer version, you will need to update the SHA.
-# You can also reference a tag or branch, but the action may change without warning.
-
-# Build a Docker image whenever it is pushed to master
-
name: Docker Image CI
on:
@@ -24,6 +13,6 @@ jobs:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+ - uses: actions/checkout@v3
- name: Build the Docker image
run: docker build --pull . -f Dockerfile -t pairdrop
diff --git a/.github/workflows/github-image.yml b/.github/workflows/github-image.yml
deleted file mode 100644
index 05dcffc..0000000
--- a/.github/workflows/github-image.yml
+++ /dev/null
@@ -1,60 +0,0 @@
-# This workflow uses actions that are not certified by GitHub.
-# They are provided by a third-party and are governed by
-# separate terms of service, privacy policy, and support
-# documentation.
-
-# GitHub recommends pinning actions to a commit SHA.
-# To get a newer version, you will need to update the SHA.
-# You can also reference a tag or branch, but the action may change without warning.
-
-# Create a Docker image and push it to ghcr.io whenever a new version tag is pushed
-
-name: GHCR Image CI
-
-on:
- push:
- tags:
- - "v*.*.*"
-
-env:
- REGISTRY: ghcr.io
- IMAGE_NAME: ${{ github.repository }}
-
-jobs:
- build-and-push-image:
- runs-on: ubuntu-latest
- permissions:
- contents: read
- packages: write
-
- steps:
- - name: Checkout repository
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
-
- - name: Setup qemu
- uses: docker/setup-qemu-action@68827325e0b33c7199eb31dd4e31fbe9023e06e3 # v3.0.0
-
- - name: Setup Docker Buildx
- uses: docker/setup-buildx-action@f95db51fddba0c2d1ec667646a06c2ce06100226 # v3.0.0
-
- - name: Log in to the Container registry
- uses: docker/login-action@343f7c4344506bcbf9b4de18042ae17996df046d # v3.0.0
- with:
- registry: ${{ env.REGISTRY }}
- username: ${{ github.actor }}
- password: ${{ secrets.GITHUB_TOKEN }}
-
- - name: Extract metadata (tags, labels) for Docker
- id: meta
- uses: docker/metadata-action@31cebacef4805868f9ce9a0cb03ee36c32df2ac4 # v5.3.0
- with:
- images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
-
- - name: Build and push Docker image
- uses: docker/build-push-action@4a13e500e55cf31b7a5d59a38ab2040ab0f42f56 # v5.1.0
- with:
- context: .
- platforms: linux/amd64,linux/arm64
- push: true
- tags: ${{ steps.meta.outputs.tags }}
- labels: ${{ steps.meta.outputs.labels }}
diff --git a/.github/workflows/zip-release.yml b/.github/workflows/zip-release.yml
deleted file mode 100644
index de3eae3..0000000
--- a/.github/workflows/zip-release.yml
+++ /dev/null
@@ -1,36 +0,0 @@
-# This workflow uses actions that are not certified by GitHub.
-# They are provided by a third-party and are governed by
-# separate terms of service, privacy policy, and support
-# documentation.
-
-# GitHub recommends pinning actions to a commit SHA.
-# To get a newer version, you will need to update the SHA.
-# You can also reference a tag or branch, but the action may change without warning.
-
-# Create a new zip file from pairdrop-cli whenever a new version tag is pushed
-
-name: Zip Release
-
-on:
- push:
- tags:
- - "v*.*.*"
-
-jobs:
- build:
- runs-on: ubuntu-latest
- permissions:
- contents: write
- steps:
- - uses: actions/checkout@master
- - name: Archive Release
- uses: thedoctor0/zip-release@b57d897cb5d60cb78b51a507f63fa184cfe35554 # v0.7.6
- with:
- filename: 'pairdrop-cli.zip'
- directory: 'pairdrop-cli'
- exclusions: '*.git* /*node_modules/* .editorconfig'
- - name: Upload Release
- uses: ncipollo/release-action@6c75be85e571768fa31b40abf38de58ba0397db5 # v1.13.0
- with:
- artifacts: "pairdrop-cli/pairdrop-cli.zip"
- token: ${{ secrets.GITHUB_TOKEN }}
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
index d2eca1e..bd15e97 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,7 +1,5 @@
node_modules
.DS_Store
-/dev/certs
+fqdn.env
+/docker/certs
qrcode-svg/
-turnserver.conf
-rtc_config.json
-ssl/
diff --git a/Dockerfile b/Dockerfile
index 23496e5..3057f35 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,21 +1,11 @@
-FROM alpine:latest
+FROM node:lts-alpine
WORKDIR /home/node/app
COPY package*.json ./
-RUN apk add --no-cache nodejs npm
-RUN NODE_ENV="production" npm ci --omit=dev
+RUN npm ci
-# Directories and files excluded via .dockerignore
COPY . .
-# environment settings
-ENV NODE_ENV="production"
-
EXPOSE 3000
-
-HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
- CMD wget --quiet --tries=1 --spider http://localhost:3000 || exit 1
-
-ENTRYPOINT ["npm", "start"]
\ No newline at end of file
diff --git a/README.md b/README.md
index 3400704..61a5407 100644
--- a/README.md
+++ b/README.md
@@ -1,134 +1,103 @@
-
+
- # _Send it_, with [PairDrop](https://pairdrop.net)
+
-
## Features
-File sharing on your local network that works on all platforms.
+[PairDrop](https://pairdrop.net) is a sublime alternative to AirDrop that works on all platforms.
-- A multi-platform AirDrop-like solution that works.
- - Send images, documents or text via peer-to-peer connection to devices on the same local network.
-- Internet transfers
- - Join temporary public rooms to transfer files easily over the Internet.
-- Web-app
- - Works on all devices with a modern web-browser.
-
-Send a file from your phone to your laptop?
- Share photos in original quality with friends using Android and iOS?
- Share private files peer-to-peer between Linux systems?
+Send images, documents or text via peer to peer connection to devices in the same local network/Wi-Fi or to paired devices.
+As it is web based, it runs on all devices.
-
+You want to quickly send a file from your phone to your laptop?
+ You want to share photos in original quality with friends that use a mixture of Android and iOS?
+ You want to share private files peer to peer between Linux systems?
+ AirDrop is unreliable again?
+ _Send it with PairDrop!_
-## Differences to the [Snapdrop](https://github.com/RobinLinus/snapdrop) it is based on
-View all differences
+Developed based on [Snapdrop](https://github.com/RobinLinus/snapdrop)
-### Paired Devices and Public Rooms — Internet Transfer
-* Transfer files over the Internet between paired devices or by entering temporary public rooms.
-* Connect to devices in complex network environments (public Wi-Fi, company network, iCloud Private Relay, VPN, etc.).
+## Differences to Snapdrop
+
+### Device Pairing
+* Pair devices via 6-digit code or QR-Code
+* Pair devices outside your local network or in complex network environment (public Wi-Fi, company network, Apple Private Relay, VPN etc.).
* Connect to devices on your mobile hotspot.
-* Devices outside of your local network that are behind a NAT are auto-connected via the PairDrop TURN server.
-* Devices from the local network, in the same public room, or previously paired are shown.
+* Paired devices will always find each other via shared secrets even after reopening the browser or the Progressive Web App
+* You will always discover devices on your local network. Paired devices are shown additionally.
+* Paired devices outside your local network that are behind a NAT are connected automatically via [Open Relay: Free WebRTC TURN Server](https://www.metered.ca/tools/openrelay/)
-#### Persistent Device Pairing
+### [Improved UI for sending/receiving files](https://github.com/RobinLinus/snapdrop/issues/560)
+* Files are transferred only after a request is accepted first. On transfer completion they are downloaded automatically if possible.
+* Multiple files are downloaded as ZIP file
+* On iOS and Android the devices share menu is opened instead of downloading the files
+* Multiple files are transferred at once with an overall progress indicator
-Always connect to known devices
+### Share Files Directly From Share / Context Menu
+* [Share files directly from context menu on Windows](/docs/how-to.md#share-files-directly-from-context-menu-on-windows)
+* [Share directly from share menu on iOS](/docs/how-to.md#share-directly-from-share-menu-on-ios)
+* [Share directly from share menu on Android](/docs/how-to.md#share-directly-from-share-menu-on-android)
-* Pair devices via a 6-digit code or a QR-Code.
-* Paired devices always find each other via shared secrets independently of their local network.
-* Pairing is persistent. You find your devices even after reopening PairDrop.
-* You can edit and unpair devices easily.
-#### Temporary Public Rooms
-
-Connect to others in complex network situations, or over the Internet.
-
-* Enter a public room via a 5-letter code or a QR-code.
-* Enter a public room to temporarily connect to devices outside your local network.
-* All devices in the same public room see each other.
-* Public rooms are temporary. Closing PairDrop leaves all rooms.
-
-### [Improved UI for Sending/Receiving Files](https://github.com/RobinLinus/snapdrop/issues/560)
-* Files are transferred after a request is accepted. Files are auto-downloaded upon completing a transfer, if possible.
-* Multiple files are downloaded as a ZIP file
-* Download, share or save to gallery via the "Share" menu on Android and iOS.
-* Multiple files are transferred at once with an overall progress indicator.
-
-### Send Files or Text Directly From Share Menu, Context Menu or CLI
-* [Send files directly from context menu on Ubuntu (using Nautilus)](docs/how-to.md#send-multiple-files-and-directories-directly-from-context-menu-on-ubuntu-using-nautilus)
-* [Send files directly from the context menu on Windows](docs/how-to.md#send-files-directly-from-context-menu-on-windows)
-* [Send directly from the "Share" menu on iOS](docs/how-to.md#send-directly-from-share-menu-on-ios)
-* [Send directly from the "Share" menu on Android](docs/how-to.md#send-directly-from-share-menu-on-android)
-* [Send directly via the command-line interface](docs/how-to.md#send-directly-via-command-line-interface)
-
-### Other Changes
-* Change your display name to easily differentiate your devices.
-* [Paste files/text and choose the recipient afterwards ](https://github.com/RobinLinus/snapdrop/pull/534)
+### Other changes
+* [Paste Mode](https://github.com/RobinLinus/snapdrop/pull/534)
* [Prevent devices from sleeping on file transfer](https://github.com/RobinLinus/snapdrop/pull/413)
-* Warn user before PairDrop is closed on file transfer
+* Warn user before PairDrop is closed on file transfer
* Open PairDrop on multiple tabs simultaneously (Thanks [@willstott101](https://github.com/willstott101))
-* [Video and audio preview](https://github.com/RobinLinus/snapdrop/pull/455) (Thanks [@victorwads](https://github.com/victorwads))
-* Switch theme back to auto/system after dark or light mode is on
+* [Video and Audio preview](https://github.com/RobinLinus/snapdrop/pull/455) (Thanks [@victorwads](https://github.com/victorwads))
* Node-only implementation (Thanks [@Bellisario](https://github.com/Bellisario))
-* Auto-restart on error (Thanks [@KaKi87](https://github.com/KaKi87))
+* Automatic restart on error (Thanks [@KaKi87](https://github.com/KaKi87))
* Lots of stability fixes (Thanks [@MWY001](https://github.com/MWY001) [@skiby7](https://github.com/skiby7) and [@willstott101](https://github.com/willstott101))
* To host PairDrop on your local network (e.g. on Raspberry Pi): [All peers connected with private IPs are discoverable by each other](https://github.com/RobinLinus/snapdrop/pull/558)
-* When hosting PairDrop yourself, you can [set your own STUN/TURN servers](docs/host-your-own.md#specify-stunturn-servers)
-* Translations.
-
+## Screenshots
+
-## Translate PairDrop on [Hosted Weblate](https://hosted.weblate.org/engage/pairdrop/)
-
-
-
+
-## Built with the following awesome technologies:
-* Vanilla HTML5 / JS ES6 / CSS 3 frontend
-* [WebRTC](http://webrtc.org/) / WebSockets
-* [Node.js](https://nodejs.org/en/) backend
-* [Progressive web app (PWA)](https://en.wikipedia.org/wiki/Progressive_web_app) unified functionality
-* [IndexedDB API](https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API) storage handling
-* [zip.js](https://gildas-lormeau.github.io/zip.js/) library
-* [cyrb53](https://github.com/bryc/code/blob/master/jshash/experimental/cyrb53.js) super-fast hash function
-* [NoSleep](https://github.com/richtr/NoSleep.js) display sleep, add wake lock ([MIT](licenses/MIT-NoSleep))
-* [heic2any](https://github.com/alexcorvi/heic2any) HEIC/HEIF to PNG/GIF/JPEG ([MIT](licenses/MIT-heic2any))
-* [Weblate](https://weblate.org/) web-based localization tool
-* [BrowserStack](https://www.browserstack.com/) This project is tested with BrowserStack
+
-[FAQ](docs/faq.md)
+## PairDrop is built with the following awesome technologies:
+* Vanilla HTML5 / ES6 / CSS3 frontend
+* [WebRTC](http://webrtc.org/) / [WebSockets](http://www.websocket.org/)
+* [NodeJS](https://nodejs.org/en/) backend
+* [Progressive Web App](https://wikipedia.org/wiki/Progressive_Web_App)
+* [IndexedDB API](https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API)
+* [zip.js](https://gildas-lormeau.github.io/zip.js/)
-[Host your own instance with Docker or Node.js](docs/host-your-own.md).
+Have any questions? Read our [FAQ](/docs/faq.md).
-## Support
+You can [host your own instance with Docker](/docs/host-your-own.md).
+
+
+## Support the Community
+PairDrop is free and always will be. Still, we have to pay for the domain.
+
+To contribute and support me:
-
+
-
-
-PairDrop is libre, and always will be. \
-If you find it useful and want to support free and open-source software, please consider donating using the button above. \
-I footed the bill for the domain and the server, and you can help create and maintain great software by supporting me. \
-Thank you very much for your contribution!
+Thanks a lot for supporting free and open software!
+
+To support the original Snapdrop and its creator go to [his GitHub page](https://github.com/RobinLinus/snapdrop).
+
+## How to contribute
-## Contributing
Feel free to [open an issue](https://github.com/schlagmichdoch/pairdrop/issues/new/choose) or a
-[pull request](https://github.com/schlagmichdoch/pairdrop/pulls), following the
-[Contributing Guidelines](CONTRIBUTING.md).
+[pull request](https://github.com/schlagmichdoch/pairdrop/pulls) but follow
+[Contributing Guidelines](/CONTRIBUTING.md).
diff --git a/dev/nginx-with-openssl.Dockerfile b/dev/nginx-with-openssl.Dockerfile
deleted file mode 100644
index 4752a53..0000000
--- a/dev/nginx-with-openssl.Dockerfile
+++ /dev/null
@@ -1,3 +0,0 @@
-FROM nginx:alpine
-
-RUN apk add --no-cache openssl
\ No newline at end of file
diff --git a/dev/nginx/default.conf b/dev/nginx/default.conf
deleted file mode 100644
index b67d0b2..0000000
--- a/dev/nginx/default.conf
+++ /dev/null
@@ -1,42 +0,0 @@
-server {
- listen 80;
-
- expires epoch;
-
- location / {
- proxy_connect_timeout 300;
- proxy_pass http://pairdrop:3000;
- proxy_set_header Connection "upgrade";
- proxy_set_header Upgrade $http_upgrade;
- }
-
- location /ca.crt {
- alias /etc/ssl/certs/pairdropCA.crt;
- }
-
- # To allow POST on static pages
- error_page 405 =200 $uri;
-}
-
-server {
- listen 443 ssl;
- http2 on;
- ssl_certificate /etc/ssl/certs/pairdrop-dev.crt;
- ssl_certificate_key /etc/ssl/certs/pairdrop-dev.key;
-
- expires epoch;
-
- location / {
- proxy_connect_timeout 300;
- proxy_pass http://pairdrop:3000;
- proxy_set_header Connection "upgrade";
- proxy_set_header Upgrade $http_upgrade;
- }
-
- location /ca.crt {
- alias /etc/ssl/certs/pairdropCA.crt;
- }
- # To allow POST on static pages
- error_page 405 =200 $uri;
-}
-
diff --git a/dev/openssl/create.sh b/dev/openssl/create.sh
deleted file mode 100644
index 4f70697..0000000
--- a/dev/openssl/create.sh
+++ /dev/null
@@ -1,9 +0,0 @@
-#!/bin/sh
-
-cnf_dir='/mnt/openssl/'
-certs_dir='/etc/ssl/certs/'
-openssl req -config ${cnf_dir}pairdropCA.cnf -new -x509 -days 1 -keyout ${certs_dir}pairdropCA.key -out ${certs_dir}pairdropCA.crt
-openssl req -config ${cnf_dir}pairdropCert.cnf -new -out /tmp/pairdrop-dev.csr -keyout ${certs_dir}pairdrop-dev.key
-openssl x509 -req -in /tmp/pairdrop-dev.csr -CA ${certs_dir}pairdropCA.crt -CAkey ${certs_dir}pairdropCA.key -CAcreateserial -extensions req_ext -extfile ${cnf_dir}pairdropCert.cnf -sha512 -days 1 -out ${certs_dir}pairdrop-dev.crt
-
-exec "$@"
diff --git a/dev/openssl/pairdropCA.cnf b/dev/openssl/pairdropCA.cnf
deleted file mode 100644
index 2ef6185..0000000
--- a/dev/openssl/pairdropCA.cnf
+++ /dev/null
@@ -1,26 +0,0 @@
-[ req ]
-default_bits = 2048
-default_md = sha256
-default_days = 1
-encrypt_key = no
-distinguished_name = subject
-x509_extensions = x509_ext
-string_mask = utf8only
-prompt = no
-
-[ subject ]
-organizationName = PairDrop
-OU = CA
-commonName = pairdrop-CA
-
-[ x509_ext ]
-subjectKeyIdentifier = hash
-authorityKeyIdentifier = keyid:always,issuer
-
-# You only need digitalSignature below. *If* you don't allow
-# RSA Key transport (i.e., you use ephemeral cipher suites), then
-# omit keyEncipherment because that's key transport.
-
-basicConstraints = critical, CA:TRUE, pathlen:0
-keyUsage = critical, digitalSignature, keyEncipherment, cRLSign, keyCertSign
-
diff --git a/dev/openssl/pairdropCert.cnf b/dev/openssl/pairdropCert.cnf
deleted file mode 100644
index 87202d8..0000000
--- a/dev/openssl/pairdropCert.cnf
+++ /dev/null
@@ -1,29 +0,0 @@
-[ req ]
-default_bits = 2048
-default_md = sha256
-default_days = 1
-encrypt_key = no
-distinguished_name = subject
-req_extensions = req_ext
-string_mask = utf8only
-prompt = no
-
-[ subject ]
-organizationName = PairDrop
-OU = Development
-
-# Use a friendly name here because it's presented to the user. The server's DNS
-# names are placed in Subject Alternate Names. Plus, DNS names here is deprecated
-# by both IETF and CA/Browser Forums. If you place a DNS name here, then you
-# must include the DNS name in the SAN too (otherwise, Chrome and others that
-# strictly follow the CA/Browser Baseline Requirements will fail).
-
-commonName = ${ENV::FQDN}
-
-[ req_ext ]
-subjectKeyIdentifier = hash
-basicConstraints = CA:FALSE
-keyUsage = digitalSignature, keyEncipherment
-subjectAltName = DNS:${ENV::FQDN}
-nsComment = "OpenSSL Generated Certificate"
-extendedKeyUsage = serverAuth
diff --git a/docker-compose-coturn.yml b/docker-compose-coturn.yml
deleted file mode 100644
index 9d0b0a8..0000000
--- a/docker-compose-coturn.yml
+++ /dev/null
@@ -1,31 +0,0 @@
-version: "3"
-services:
- pairdrop:
- image: "lscr.io/linuxserver/pairdrop:latest"
- container_name: pairdrop
- restart: unless-stopped
- volumes:
- - ./rtc_config.json:/home/node/app/rtc_config.json
- environment:
- - PUID=1000 # UID to run the application as
- - PGID=1000 # GID to run the application as
- - WS_FALLBACK=false # Set to true to enable websocket fallback if the peer to peer WebRTC connection is not available to the client.
- - RATE_LIMIT=false # Set to true to limit clients to 1000 requests per 5 min.
- - RTC_CONFIG=/home/node/app/rtc_config.json # Set to the path of a file that specifies the STUN/TURN servers.
- - DEBUG_MODE=false # Set to true to debug container and peer connections.
- - TZ=Etc/UTC # Time Zone
- ports:
- - "127.0.0.1:3000:3000" # Web UI. Change the port number before the last colon e.g. `127.0.0.1:9000:3000`
- coturn_server:
- image: "coturn/coturn"
- restart: unless-stopped
- volumes:
- - ./turnserver.conf:/etc/coturn/turnserver.conf
- - ./ssl/:/etc/coturn/ssl/
- ports:
- - "3478:3478"
- - "3478:3478/udp"
- - "5349:5349"
- - "5349:5349/udp"
- - "10000-20000:10000-20000/udp"
- # see guide at docs/host-your-own.md#coturn-and-pairdrop-via-docker-compose
\ No newline at end of file
diff --git a/docker-compose-dev.yml b/docker-compose-dev.yml
deleted file mode 100644
index 091366d..0000000
--- a/docker-compose-dev.yml
+++ /dev/null
@@ -1,34 +0,0 @@
-version: "3"
-services:
- pairdrop:
- build: .
- container_name: pairdrop
- restart: unless-stopped
- environment:
- - PUID=1000 # UID to run the application as
- - PGID=1000 # GID to run the application as
- - WS_FALLBACK=false # Set to true to enable websocket fallback if the peer to peer WebRTC connection is not available to the client.
- - RATE_LIMIT=false # Set to true to limit clients to 1000 requests per 5 min.
- - RTC_CONFIG=false # Set to the path of a file that specifies the STUN/TURN servers.
- - DEBUG_MODE=false # Set to true to debug container and peer connections.
- - TZ=Etc/UTC # Time Zone
- ports:
- - "127.0.0.1:3000:3000" # Web UI. Change the port number before the last colon e.g. `127.0.0.1:9000:3000`
- nginx:
- build:
- context: dev/
- dockerfile: nginx-with-openssl.Dockerfile
- image: "nginx-with-openssl"
- volumes:
- - ./public:/usr/share/nginx/html
- - ./dev/certs:/etc/ssl/certs
- - ./dev/openssl:/mnt/openssl
- - ./dev/nginx/default.conf:/etc/nginx/conf.d/default.conf
- ports:
- - "8080:80"
- - "8443:443"
- environment:
- - FQDN=localhost
- entrypoint: /mnt/openssl/create.sh
- command: ["nginx", "-g", "daemon off;"]
- restart: unless-stopped
\ No newline at end of file
diff --git a/docker-compose.yml b/docker-compose.yml
index 357ed85..5ff9305 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -1,16 +1,12 @@
version: "3"
services:
- pairdrop:
- image: "lscr.io/linuxserver/pairdrop:latest"
- container_name: pairdrop
+ node:
+ image: "node:lts-alpine"
+ user: "node"
+ working_dir: /home/node/app
+ volumes:
+ - ./:/home/node/app
+ command: ash -c "npm i && npm run start:prod"
restart: unless-stopped
- environment:
- - PUID=1000 # UID to run the application as
- - PGID=1000 # GID to run the application as
- - WS_FALLBACK=false # Set to true to enable websocket fallback if the peer to peer WebRTC connection is not available to the client.
- - RATE_LIMIT=false # Set to true to limit clients to 1000 requests per 5 min.
- - RTC_CONFIG=false # Set to the path of a file that specifies the STUN/TURN servers.
- - DEBUG_MODE=false # Set to true to debug container and peer connections.
- - TZ=Etc/UTC # Time Zone
ports:
- - "127.0.0.1:3000:3000" # Web UI. Change the port number before the last colon e.g. `127.0.0.1:9000:3000`
+ - "3000:3000"
diff --git a/docs/docker-swarm-usage.md b/docs/docker-swarm-usage.md
deleted file mode 100644
index 4d1abf2..0000000
--- a/docs/docker-swarm-usage.md
+++ /dev/null
@@ -1,47 +0,0 @@
-# Docker Swarm Usage
-
-## Healthcheck
-
-The [Docker Image](../Dockerfile) includes a health check with the following options:
-
-```
---interval=30s
-```
-> Specifies the time interval to run the health check. \
-> In this case, the health check is performed every 30 seconds.
-
-
-```
---timeout=10s
-```
-> Specifies the amount of time to wait for a response from the \"HEALTHCHECK\" command. \
-> If the response does not arrive within 10 seconds, the health check fails.
-
-
-```
---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.
-
-
-```
---retries=3
-```
-> Specifies the number of times Docker should retry the health check \
-> before considering the container to be unhealthy.
-
-
-
-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 fails.
-
-Overall, this \"HEALTHCHECK\" instruction is defining a health check process \
-that runs every 30 seconds, and waits up to 10 seconds for a response, \
-begins 5 seconds after the container is started, and retries up to 3 times. \
-The health check attempts to connect to http://localhost:3000/ \
-and will considers the container unhealthy if unable to connect.
-
diff --git a/docs/faq.md b/docs/faq.md
index c685658..2385a47 100644
--- a/docs/faq.md
+++ b/docs/faq.md
@@ -1,275 +1,85 @@
# Frequently Asked Questions
-
-
- Help! I can't install the PWA!
-
+### Instructions / Discussions
+* [Video Instructions](https://www.youtube.com/watch?v=4XN02GkcHUM) (Big thanks to [TheiTeckHq](https://www.youtube.com/channel/UC_DUzWMb8gZZnAbISQjmAfQ))
+* [idownloadblog](http://www.idownloadblog.com/2015/12/29/snapdrop/)
+* [thenextweb](http://thenextweb.com/insider/2015/12/27/snapdrop-is-a-handy-web-based-replacement-for-apples-fiddly-airdrop-file-transfer-tool/)
+* [winboard](http://www.winboard.org/artikel-ratgeber/6253-dateien-vom-desktop-pc-mit-anderen-plattformen-teilen-mit-snapdrop.html)
+* [免費資源網路社群](https://free.com.tw/snapdrop/)
+* [Hackernews](https://news.ycombinator.com/front?day=2020-12-24)
+* [Reddit](https://www.reddit.com/r/Android/comments/et4qny/snapdrop_is_a_free_open_source_cross_platform/)
+* [Producthunt](https://www.producthunt.com/posts/snapdrop)
-
+### Help! I can't install the PWA!
+if you are using a Chromium-based browser (Chrome, Edge, Brave, etc.), you can easily install PairDrop PWA on your desktop
+by clicking the install-button in the top-right corner while on [pairdrop.net](https://pairdrop.net).
-Here is a good guide on how to install PWAs on different platforms: \
-https://www.cdc.gov/niosh/mining/content/hearingloss/installPWA.html
+
-
-**Chromium-based browser on Desktop (Chrome, Edge, Vivaldi, Brave, etc.)** \
-Easily install PairDrop PWA on your desktop by clicking the install-button in the top-right corner while on [pairdrop.net](https://pairdrop.net).
-
-
-
-**Desktop Firefox** \
On Firefox, PWAs are installable via [this browser extensions](https://addons.mozilla.org/de/firefox/addon/pwas-for-firefox/)
-**Android** \
-PWAs are installable only by using Google Chrome or Samsung Browser:
-1. Visit [pairdrop.net](https://pairdrop.net)
-2. Click _Install_ on the installation pop-up or use the three-dot-menu and click on _Add to Home screen_
-3. Click _Add_ on the pop-up
-
-**iOS** \
-PWAs are installable only by using Safari:
-1. Visit [pairdrop.net](https://pairdrop.net)
-2. Click on the share icon
-3. Click _Add to Home Screen_
-4. Click _Add_ in the top right corner
-
-
-
-**Self-Hosted Instance?** \
-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 info.
-
-
-
-
-
-
-
- Shortcuts?
-
-
-
-
-Available shortcuts:
+### Are there any shortcuts?
+Sure!
- Send a message with `CTRL + ENTER`
-- Close all "Send" and "Pair" dialogs by pressing `Esc`.
-- Copy a received message to the clipboard with `CTRL/⌘ + C`.
-- Accept file-transfer requests with `Enter` and decline with `Esc`.
-
+- 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`.
-
+### When I receive images on iOS I cannot add them directly to the gallery?
+Apparently, iOS does not allow images shared from a website to be saved to the gallery directly.
+It simply does not offer the option for images shared from a website.
-
-
- How to save images directly to the gallery on iOS?
-
-
-
-
-~~Apparently, iOS does not allow images shared from a website to be saved to the gallery directly.~~
-~~It simply does not offer that option for images shared from a website.~~
-
-~~iOS Shortcuts saves the day:~~ \
+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/
-Update: \
-Apparently, this was only a bug that is fixed in recent iOS version (https://github.com/WebKit/WebKit/pull/13111). \
-If you use an older affected iOS version this might still be of use. \
-Luckily, you can now simply use `Save Image`/`Save X Images` 🎉
+### Is it possible to share files directly from the context / share menu?
+Yes it finally is!
+* [Share files directly from context menu on Windows](/docs/how-to.md#share-files-directly-from-context-menu-on-windows)
+* [Share directly from share menu on iOS](/docs/how-to.md#share-directly-from-share-menu-on-ios)
+* [Share directly from share menu on Android](/docs/how-to.md#share-directly-from-share-menu-on-android)
-
+### What about the connection? Is it a P2P-connection directly from device to device or is there any third-party-server?
+It uses a P2P connection if WebRTC is supported by the browser. WebRTC needs a Signaling Server, but it is only used to establish a connection and is not involved in the file transfer.
-
+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.
-
-
- Is it possible to send files or text directly from the "Context" or "Share" menu?
-
+### What about privacy? Will files be saved on third-party-servers?
+None of your files are ever sent to any server. Files are sent only between peers. PairDrop doesn't even use a database. If you are curious have a look [at the Server](https://github.com/schlagmichdoch/pairdrop/blob/master/index.js).
+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.
-Yes, it finally is.
-* [Send files directly from the "Context" menu on Windows](/docs/how-to.md#send-files-directly-from-context-menu-on-windows)
-* [Send directly from the "Share" menu on iOS](/docs/how-to.md#send-directly-from-share-menu-on-ios)
-* [Send directly from the "Share" menu on Android](/docs/how-to.md#send-directly-from-share-menu-on-android)
+### What about security? Are my files encrypted while being sent between the computers?
+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.
-
-
-
-
-
-
- Is it possible to send files or text directly via CLI?
-
-
-
-
-Yes.
-
-* [Send directly from a command-line interface](/docs/how-to.md#send-directly-via-command-line-interface)
-
-
-
-
-
-
-
- Are there any third-party Apps?
-
-
-
-
-These third-party apps are 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 :)
-
-
-
-
-
-
-
- What about the connection? Is it a P2P connection directly from device to device or is there any third-party-server?
-
-
-
-
-It uses a WebRTC peer-to-peer connection.
-WebRTC needs a signaling server that is only used to establish a connection.
-The server is not involved in the file transfer.
-
-If the devices are on the same network,
-none of your files are ever sent to any server.
-
-If your devices are paired and behind a NAT,
-the PairDrop TURN Server is used to route your files and messages.
-See the [Technical Documentation](technical-documentation.md#encryption-webrtc-stun-and-turn)
-to learn more about STUN, TURN and WebRTC.
-
-If you host your own instance
-and want to support devices that do not support WebRTC,
-you can [start the PairDrop instance with an activated WebSocket fallback](https://github.com/schlagmichdoch/PairDrop/blob/master/docs/host-your-own.md#websocket-fallback-for-vpn).
-
-
-
-
-
-
-
- What about privacy? Will files be saved on third-party servers?
-
-
-
-
-Files are sent directly between peers.
-PairDrop doesn't even use a database.
-If curious, study [the signaling server](https://github.com/schlagmichdoch/PairDrop/blob/master/server/ws-server.js).
-WebRTC encrypts the files in transit.
-
-If the devices are on the same network,
-none of your files are ever sent to any server.
-
-If your devices are paired and behind a NAT,
-the PairDrop TURN Server is used to route your files and messages.
-See the [Technical Documentation](technical-documentation.md#encryption-webrtc-stun-and-turn)
-to learn more about STUN, TURN and WebRTC.
-
-
-
-
-
-
-
- What about security? Are my files encrypted while sent between the computers?
-
-
-
-
-Yes. Your files are sent using WebRTC, encrypting them in transit.
-Still you have to trust the PairDrop server. To ensure the connection is secure and there is no [MITM](https://en.m.wikipedia.org/wiki/Man-in-the-middle_attack) there is a plan to make PairDrop
-zero trust by encrypting the signaling and implementing a verification process. See [issue #180](https://github.com/schlagmichdoch/PairDrop/issues/180) to keep updated.
-
-
-
-
-
-
-
- Transferring many files with paired devices takes too long
-
-
-
-
-Naturally, if traffic needs to be routed through the TURN server
-because your devices are behind different NATs, transfer speed decreases.
-
-You can open a hotspot on one of your devices to bridge the connection,
-which omits the need of the TURN server.
+### Transferring many files with paired devices takes too long
+Naturally, if traffic needs to be routed through the turn server transfer speed decreases.
+As a workaround you can open a hotspot on one of your devices to bridge the connection which makes transfers much faster.
- [How to open a hotspot on Windows](https://support.microsoft.com/en-us/windows/use-your-windows-pc-as-a-mobile-hotspot-c89b0fad-72d5-41e8-f7ea-406ad9036b85#WindowsVersion=Windows_11)
-- [How to open a hotspot on macOS](https://support.apple.com/guide/mac-help/share-internet-connection-mac-network-users-mchlp1540/mac)
+- [How to open a hotspot on Mac](https://support.apple.com/guide/mac-help/share-internet-connection-mac-network-users-mchlp1540/mac)
- [Library to open a hotspot on Linux](https://github.com/lakinduakash/linux-wifi-hotspot)
You can also use mobile hotspots on phones to do that.
-Then, all data should be sent directly between devices and not use your data plan.
+Then, all data should be sent directly between devices and your data plan should not be charged.
-
+### Why don't you implement feature xyz?
+Snapdrop and PairDrop are a study in radical simplicity. The user interface is insanely simple. Features are chosen very carefully because complexity grows quadratically since every feature potentially interferes with each other feature. We focus very narrowly on a single use case: instant file transfer.
+We are not trying to optimize for some edge-cases. We are optimizing the user flow of the average users. Don't be sad if we decline your feature request for the sake of simplicity.
-
+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).
-
-
- Why don't you implement feature xyz?
-
-
-
-Snapdrop and PairDrop are a study in radical simplicity.
-The user interface is insanely simple.
-Features are chosen very carefully because complexity grows quadratically
-since every feature potentially interferes with each other feature.
-We focus very narrowly on a single use case: instant file transfer.
-Not facilitating optimal edge-cases means better flow for average users.
-Don't be sad. We may decline your feature request for the sake of simplicity.
-
-Read *Insanely Simple: The Obsession that Drives Apple's Success*,
-and/or *Thinking, Fast and Slow* to learn more.
-
-
-
-
-
-
-
- PairDrop is awesome. How can I support it?
-
-
-
-
-* [Buy me a coffee](https://www.buymeacoffee.com/pairdrop) to pay for the domain and the server, and support libre software.
+### Snapdrop and PairDrop are awesome! How can I support them?
+* [Buy me a cover to support open source software](https://www.buymeacoffee.com/pairdrop)
* [File bugs, give feedback, submit suggestions](https://github.com/schlagmichdoch/pairdrop/issues)
* Share PairDrop on social media.
-* Fix bugs and create a pull request.
-* Do some security analysis and make suggestions.
-* Participate in [active discussions](https://github.com/schlagmichdoch/PairDrop/discussions)
+* Fix bugs and make a pull request.
+* Do security analysis and suggestions
-
-
-
-
-
- How does it work?
-
-
-
-
-[See here for info about the technical implementation](/docs/technical-documentation.md)
-
-
-
-
+### How does it work?
+[See here for Information about the Technical Implementation](/docs/technical-documentation.md)
[< Back](/README.md)
diff --git a/docs/host-your-own.md b/docs/host-your-own.md
index f846db7..e2cf6fe 100644
--- a/docs/host-your-own.md
+++ b/docs/host-your-own.md
@@ -1,159 +1,66 @@
# Deployment Notes
-
-## TURN server for Internet Transfer
-
-Beware that you have to host your own TURN server to enable transfers between different networks.
-
-Follow [this guide](https://gabrieltanner.org/blog/turn-server/) to either install coturn directly on your system (Step 1)
-or deploy it via Docker (Step 5).
-
-You can use the `docker-compose-coturn.yml` in this repository. See [Coturn and PairDrop via Docker Compose](#coturn-and-pairdrop-via-docker-compose).
-
-Alternatively, use a free, pre-configured TURN server like [OpenRelay](https://www.metered.ca/tools/openrelay/)
-
-
-
-## PairDrop via HTTPS
-
-On some browsers PairDrop must be served over TLS in order for some features to work properly.
-These may include:
-- Copying an incoming message via the 'copy' button
-- Installing PairDrop as PWA
-- Persistent pairing of devices
-- Changing of the display name
-- Notifications
-
-Naturally, this is also recommended to increase security.
-
-
-
-## Deployment with Docker
-
The easiest way to get PairDrop up and running is by using Docker.
-### Docker Image from Docker Hub
+## Deployment with Docker from Docker Hub
```bash
docker run -d --restart=unless-stopped --name=pairdrop -p 127.0.0.1:3000:3000 lscr.io/linuxserver/pairdrop
```
-> This image is hosted by [linuxserver.io](https://linuxserver.io). For more information visit https://hub.docker.com/r/linuxserver/pairdrop
-
-
-
-
-### Docker Image from GitHub Container Registry (ghcr.io)
-
-```bash
-docker run -d --restart=unless-stopped --name=pairdrop -p 127.0.0.1:3000:3000 ghcr.io/schlagmichdoch/pairdrop
-```
-
-
-
-
-### Docker Image self-built
-
-#### Build the image
-
-```bash
-docker build --pull . -f Dockerfile -t pairdrop
-```
-
-> A GitHub action is set up to do this step automatically at the release of new versions.
+> You must use a server proxy to set the X-Forwarded-For to prevent all clients from discovering each other (See [#HTTP-Server](#http-server)).
>
-> `--pull` ensures always the latest node image is used.
-
-#### Run the image
-
-```bash
-docker run -d --restart=unless-stopped --name=pairdrop -p 127.0.0.1:3000:3000 -it pairdrop
-```
-
-> You must use a server proxy to set the `X-Forwarded-For` header
-> to prevent all clients from discovering each other (See [#HTTP-Server](#http-server)).
->
-> To prevent bypassing the proxy by reaching the docker container directly,
-> `127.0.0.1` is specified in the run command.
-
-
-
-
-### Flags
+> To prevent bypassing the proxy and reach the docker container directly, `127.0.0.1` is specified in the run command.
+### Options / Flags
Set options by using the following flags in the `docker run` command:
#### Port
-
-```bash
+```
-p 127.0.0.1:8080:3000
```
-
-> Specify the port used by the docker image
->
+> Specify the port used by the docker image
> - 3000 -> `-p 127.0.0.1:3000:3000`
> - 8080 -> `-p 127.0.0.1:8080:3000`
-
-#### Set Environment Variables via Docker
-
-Environment Variables are set directly in the `docker run` command: \
-e.g. `docker run -p 127.0.0.1:3000:3000 -it pairdrop -e DEBUG_MODE="true"`
-
-Overview of available Environment Variables are found [here](#environment-variables).
-
-Example:
-```bash
-docker run -d \
- --name=pairdrop \
- --restart=unless-stopped \
- -p 127.0.0.1:3000:3000 \
- -e PUID=1000 \
- -e PGID=1000 \
- -e WS_SERVER=false \
- -e WS_FALLBACK=false \
- -e RTC_CONFIG=false \
- -e RATE_LIMIT=false \
- -e DEBUG_MODE=false \
- -e TZ=Etc/UTC \
- lscr.io/linuxserver/pairdrop
+#### Rate limiting requests
```
-
-
-
-## Deployment with Docker Compose
-
-Here's an example docker compose file:
-
-```yaml
-version: "3"
-services:
- pairdrop:
- image: "lscr.io/linuxserver/pairdrop:latest"
- container_name: pairdrop
- restart: unless-stopped
- environment:
- - PUID=1000 # UID to run the application as
- - PGID=1000 # GID to run the application as
- - WS_FALLBACK=false # Set to true to enable websocket fallback if the peer to peer WebRTC connection is not available to the client.
- - RATE_LIMIT=false # Set to true to limit clients to 1000 requests per 5 min.
- - RTC_CONFIG=false # Set to the path of a file that specifies the STUN/TURN servers.
- - DEBUG_MODE=false # Set to true to debug container and peer connections.
- - TZ=Etc/UTC # Time Zone
- ports:
- - "127.0.0.1:3000:3000" # Web UI
+-e RATE_LIMIT=true
```
+> Limits clients to 100 requests per 5 min
-Run the compose file with `docker compose up -d`.
-
-> You must use a server proxy to set the `X-Forwarded-For` header
-> to prevent all clients from discovering each other (See [#HTTP-Server](#http-server)).
+#### Websocket Fallback (for VPN)
+```
+-e WS_FALLBACK=true
+```
+> Provides PairDrop to clients with an included websocket fallback if the peer to peer WebRTC connection is not available to the client.
>
-> To prevent bypassing the proxy by reaching the Docker container
-> directly, `127.0.0.1` is specified in the `ports` argument.
+> This is not used on the official https://pairdrop.net, but you can activate it on your self-hosted instance.
+> This is especially useful if you connect to your instance via a VPN as most VPN services block WebRTC completely in order to hide your real IP address ([read more](https://privacysavvy.com/security/safe-browsing/disable-webrtc-chrome-firefox-safari-opera-edge/)).
+>
+> **Warning:** All traffic sent between devices using this fallback is routed through the server and therefor not peer to peer!
+> Beware that the traffic routed via this fallback is readable by the server. Only ever use this on instances you can trust.
+> Additionally, beware that all traffic using this fallback debits the servers data plan.
-## Deployment with Node.js
+## Deployment with Docker with self-built image
+### Build the image
+```bash
+docker build --pull . -f Dockerfile -t pairdrop
+```
+> A GitHub action is set up to do this step automatically.
+>
+> `--pull` ensures always the latest node image is used.
-Clone this repository and enter the folder
+### Run the image
+```bash
+docker run -d --restart=unless-stopped --name=pairdrop -p 127.0.0.1:3000:3000 -it pairdrop npm run start:prod
+```
+> You must use a server proxy to set the X-Forwarded-For to prevent all clients from discovering each other (See [#HTTP-Server](#http-server)).
+>
+> To prevent bypassing the proxy and reach the docker container directly, `127.0.0.1` is specified in the run command.
+>
+> To specify options replace `npm run start:prod` according to [the documentation above.](#options--flags)
+
+## Deployment with node
```bash
git clone https://github.com/schlagmichdoch/PairDrop.git && cd PairDrop
@@ -167,318 +74,90 @@ npm install
Start the server with:
+```bash
+node index.js
+```
+or
```bash
npm start
```
-> By default, the node server listens on port 3000.
+> Remember to check your IP Address using your OS command to see where you can access the server.
+> By default, the node server listens on port 3000.
-### Options / Flags
-
-These are some flags only reasonable when deploying via Node.js
-
+### Environment variables
#### Port
-
+On Unix based systems
```bash
-PORT=3000
+PORT=3010 npm start
```
+On Windows
+```bash
+$env:PORT=3010; npm start
+```
+> Specify the port PairDrop is running on. (Default: 3000)
-> Default: `3000`
->
-> Environment variable to specify the port used by the Node.js server \
-> e.g. `PORT=3010 npm start`
-
+### Options / Flags
#### Local Run
-
```bash
npm start -- --localhost-only
```
-
> Only allow connections from localhost.
+>
+> You must use a server proxy to set the X-Forwarded-For to prevent all clients from discovering each other (See [#HTTP-Server](#http-server)).
>
-> You must use a server proxy to set the `X-Forwarded-For` header
-> to prevent all clients from discovering each other (See [#HTTP-Server](#http-server)).
->
-> Use this when deploying PairDrop with node to prevent
-> bypassing the reverse proxy by reaching the Node.js server directly.
+> Use this when deploying PairDrop with node to prevent bypassing the proxy and reach the docker container directly.
#### Automatic restart on error
-
```bash
-npm start -- --auto-restart
+npm start -- --auto-restart
```
-
> Restarts server automatically on error
-#### Production (autostart and rate-limit)
+
+#### Rate limiting requests
+```bash
+npm start -- --rate-limit
+```
+> Limits clients to 100 requests per 5 min
+
+
+
+#### Websocket Fallback (for VPN)
+```bash
+npm start -- --include-ws-fallback
+```
+> Provides PairDrop to clients with an included websocket fallback if the peer to peer WebRTC connection is not available to the client.
+>
+> This is not used on the official https://pairdrop.net, but you can activate it on your self-hosted instance.
+> This is especially useful if you connect to your instance via a VPN as most VPN services block WebRTC completely in order to hide your real IP address ([read more](https://privacysavvy.com/security/safe-browsing/disable-webrtc-chrome-firefox-safari-opera-edge/)).
+>
+> **Warning:** All traffic sent between devices using this fallback is routed through the server and therefor not peer to peer!
+> Beware that the traffic routed via this fallback is readable by the server. Only ever use this on instances you can trust.
+> Additionally, beware that all traffic using this fallback debits the servers data plan.
+
+
+
+#### Production (autostart and rate-limit)
```bash
npm run start:prod
```
-> shortcut for `RATE_LIMIT=5 npm start -- --auto-restart`
-
-#### Production (autostart, rate-limit, localhost-only)
-
+#### Production (autostart, rate-limit, localhost-only and websocket fallback for VPN)
```bash
-npm run start:prod -- --localhost-only
+npm run start:prod -- --localhost-only --include-ws-fallback
```
-
-> To prevent connections to the node server from bypassing \
-> the proxy server you should always use "--localhost-only" on production.
-
-#### Set Environment Variables via Node.js
-
-To specify environment variables set them in the run command in front of `npm start`.
-The syntax is different on Unix and Windows.
-
-On Unix based systems
-
-```bash
-PORT=3000 RTC_CONFIG="rtc_config.json" npm start
-```
-
-On Windows
-
-```bash
-$env:PORT=3000 RTC_CONFIG="rtc_config.json"; npm start
-```
-
-Overview of available Environment Variables are found [here](#environment-variables).
-
-
-
-## Environment Variables
-
-### Debug Mode
-
-```bash
-DEBUG_MODE="true"
-```
-
-> Default: `false`
->
-> Logs the used environment variables for debugging.
->
-> Prints debugging information about the connecting peers IP addresses.
->
-> This is quite useful to check whether the [#HTTP-Server](#http-server)
-> is configured correctly, so the auto-discovery feature works correctly.
-> Otherwise, all clients discover each other mutually, independently of their network status.
->
-> If this flag is set to `"true"` each peer that connects to the PairDrop server will produce a log to STDOUT like this:
->
-> ```
-> ----DEBUGGING-PEER-IP-START----
-> remoteAddress: ::ffff:172.17.0.1
-> x-forwarded-for: 19.117.63.126
-> cf-connecting-ip: undefined
-> PairDrop uses: 19.117.63.126
-> IP is private: false
-> if IP is private, '127.0.0.1' is used instead
-> ----DEBUGGING-PEER-IP-END----
-> ```
->
-> If the IP address "PairDrop uses" matches the public IP address of the client device, everything is set up correctly. \
-> To find out the public IP address of the client device visit https://whatsmyip.com/.
->
-> To preserve your clients' privacy: \
-> **Never use this environment variable in production!**
-
-
-
-
-### Rate limiting requests
-
-```bash
-RATE_LIMIT=1
-```
-
-> Default: `false`
->
-> Limits clients to 1000 requests per 5 min
->
-> "If you are behind a proxy/load balancer (usually the case with most hosting services, e.g. Heroku, Bluemix, AWS ELB,
-> Render, Nginx, Cloudflare, Akamai, Fastly, Firebase Hosting, Rackspace LB, Riverbed Stingray, etc.), the IP address of
-> the request might be the IP of the load balancer/reverse proxy (making the rate limiter effectively a global one and
-> blocking all requests once the limit is reached) or undefined."
-> (See: https://express-rate-limit.mintlify.app/guides/troubleshooting-proxy-issues)
->
-> To find the correct number to use for this setting:
->
-> 1. Start PairDrop with `DEBUG_MODE=True` and `RATE_LIMIT=1`
-> 2. Make a `get` request to `/ip` of the PairDrop instance (e.g. `https://pairdrop-example.net/ip`)
-> 3. Check if the IP address returned in the response matches your public IP address (find out by visiting e.g. https://whatsmyip.com/)
-> 4. You have found the correct number if the IP addresses match. If not, then increase `RATE_LIMIT` by one and redo 1. - 4.
->
-> e.g. on Render you must use RATE_LIMIT=5
-
-
-
-
-### IPv6 Localization
-
-```bash
-IPV6_LOCALIZE=4
-```
-
-> Default: `false`
->
-> To enable Peer Auto-Discovery among IPv6 peers, you can specify a reduced number of segments \
-> of the client IPv6 address to be evaluated as the peer's IP. \
-> This can be especially useful when using Cloudflare as a proxy.
->
-> The flag must be set to an **integer** between `1` and `7`. \
-> The number represents the number of IPv6 [hextets](https://en.wikipedia.org/wiki/IPv6#Address_representation) \
-> to match the client IP against. The most common value would be `4`, \
-> which will group peers within the same `/64` subnet.
-
-
-
-
-### Websocket Fallback (for VPN)
-
-```bash
-WS_FALLBACK=true
-```
-
-> Default: `false`
->
-> Provides PairDrop to clients with an included websocket fallback \
-> if the peer to peer WebRTC connection is not available to the client.
->
-> This is not used on the official https://pairdrop.net website,
-> but you can activate it on your self-hosted instance.\
-> This is especially useful if you connect to your instance via a VPN (as most VPN services block WebRTC completely in
-> order to hide your real IP address). ([Read more here](https://privacysavvy.com/security/safe-browsing/disable-webrtc-chrome-firefox-safari-opera-edge/)).
->
-> **Warning:** \
-> All traffic sent between devices using this fallback
-> is routed through the server and therefor not peer to peer!
->
-> Beware that the traffic routed via this fallback is readable by the server. \
-> Only ever use this on instances you can trust.
->
-> Additionally, beware that all traffic using this fallback debits the servers data plan.
-
-
-
-
-### Specify STUN/TURN Servers
-
-```bash
-RTC_CONFIG="rtc_config.json"
-```
-
-> Default: `false`
->
-> Specify the STUN/TURN servers PairDrop clients use by setting \
-> `RTC_CONFIG` to a JSON file including the configuration. \
-> You can use `rtc_config_example.json` as a starting point.
->
-> To host your own TURN server you can follow this guide: https://gabrieltanner.org/blog/turn-server/
-> Alternatively, use a free, pre-configured TURN server like [OpenRelay](<[url](https://www.metered.ca/tools/openrelay/)>)
->
-> Default configuration:
->
-> ```json
-> {
-> "sdpSemantics": "unified-plan",
-> "iceServers": [
-> {
-> "urls": "stun:stun.l.google.com:19302"
-> }
-> ]
-> }
-> ```
-
-
-
-You can host an instance that uses another signaling server
-This can be useful if you don't want to trust the client files that are hosted on another instance but still want to connect to devices that use https://pairdrop.net.
-
-### Specify Signaling Server
-
-```bash
-SIGNALING_SERVER="pairdrop.net"
-```
-
-> Default: `false`
->
-> By default, clients connecting to your instance use the signaling server of your instance to connect to other devices.
->
-> By using `SIGNALING_SERVER`, you can host an instance that uses another signaling server.
->
-> This can be useful if you want to ensure the integrity of the client files and don't want to trust the client files that are hosted on another PairDrop instance but still want to connect to devices that use the other instance.
-> E.g. host your own client files under *pairdrop.your-domain.com* but use the official signaling server under *pairdrop.net*
-> This way devices connecting to *pairdrop.your-domain.com* and *pairdrop.net* can discover each other.
->
-> Beware that the version of your PairDrop server must be compatible with the version of the signaling server.
->
-> `SIGNALING_SERVER` must be a valid url without the protocol prefix.
-> Examples of valid values: `pairdrop.net`, `pairdrop.your-domain.com:3000`, `your-domain.com/pairdrop`
-
-
-
-### Customizable buttons for the _About PairDrop_ page
-
-```bash
-DONATION_BUTTON_ACTIVE=true
-DONATION_BUTTON_LINK="https://www.buymeacoffee.com/pairdrop"
-DONATION_BUTTON_TITLE="Buy me a coffee"
-TWITTER_BUTTON_ACTIVE=true
-TWITTER_BUTTON_LINK="https://twitter.com/account"
-TWITTER_BUTTON_TITLE="Find me on Twitter"
-MASTODON_BUTTON_ACTIVE=true
-MASTODON_BUTTON_LINK="https://mastodon.social/account"
-MASTODON_BUTTON_TITLE="Find me on Mastodon"
-BLUESKY_BUTTON_ACTIVE=true
-BLUESKY_BUTTON_LINK="https://bsky.app/profile/account"
-BLUESKY_BUTTON_TITLE="Find me on Bluesky"
-CUSTOM_BUTTON_ACTIVE=true
-CUSTOM_BUTTON_LINK="https://your-custom-social-network.net/account"
-CUSTOM_BUTTON_TITLE="Find me on this custom social network"
-PRIVACYPOLICY_BUTTON_ACTIVE=true
-PRIVACYPOLICY_BUTTON_LINK="https://link-to-your-privacy-policy.net"
-PRIVACYPOLICY_BUTTON_TITLE="Open our privacy policy"
-```
-
-> Default: unset
->
-> By default, clients will show the default button configuration: GitHub, BuyMeACoffee, Twitter, and FAQ on GitHub.
->
-> The GitHub and FAQ on GitHub buttons are essential, so they are always shown.
->
-> The other buttons can be customized:
->
-> * `*_BUTTON_ACTIVE`: set this to `true` to show a natively hidden button or to `false` to hide a normally shown button
-> * `*_BUTTON_LINK`: set this to any URL to overwrite the href attribute of the button
-> * `*_BUTTON_TITLE`: set this to overwrite the hover title of the button. This will prevent the title from being translated.
-
-
-
-## Healthcheck
-
-> The Docker Image hosted on `ghcr.io` and the self-built Docker Image include a healthcheck.
->
-> Read more about [Docker Swarm Usage](docker-swarm-usage.md#docker-swarm-usage).
-
-
+> To prevent connections to the node server from bypassing the proxy server you should always use "--localhost-only" on production.
## HTTP-Server
-
-When running PairDrop, the `X-Forwarded-For` header has to be set by a proxy. \
-Otherwise, all clients will be mutually visible.
-
-To check if your setup is configured correctly [use the environment variable `DEBUG_MODE="true"`](#debug-mode).
+When running PairDrop, the `X-Forwarded-For` header has to be set by a proxy. Otherwise, all clients will be mutually visible.
### Using nginx
-
#### Allow http and https requests
-
```
server {
listen 80;
@@ -512,7 +191,6 @@ server {
```
#### Automatic http to https redirect:
-
```
server {
listen 80;
@@ -541,169 +219,100 @@ server {
}
```
-
-
-
### Using Apache
-
install modules `proxy`, `proxy_http`, `mod_proxy_wstunnel`
-
-```bash
+```shell
a2enmod proxy
```
-
-```bash
+```shell
a2enmod proxy_http
```
+```shell
+a2enmod proxy_wstunnel
+```
-Create a new configuration file under `/etc/apache2/sites-available` (on Debian)
+Create a new configuration file under `/etc/apache2/sites-available` (on debian)
**pairdrop.conf**
-
-#### Allow HTTP and HTTPS requests
-
-```apacheconf
-
- ProxyPass / http://127.0.0.1:3000/ upgrade=websocket
+#### Allow http and https requests
+```
+
+ ProxyPass / http://127.0.0.1:3000/
+ RewriteEngine on
+ RewriteCond %{HTTP:Upgrade} websocket [NC]
+ RewriteCond %{HTTP:Connection} upgrade [NC]
+ RewriteRule ^/?(.*) "ws://127.0.0.1:3000/$1" [P,L]
-
- ProxyPass / https://127.0.0.1:3000/ upgrade=websocket
+
+ ProxyPass / https://127.0.0.1:3000/
+ RewriteEngine on
+ RewriteCond %{HTTP:Upgrade} websocket [NC]
+ RewriteCond %{HTTP:Connection} upgrade [NC]
+ RewriteRule ^/?(.*) "wws://127.0.0.1:3000/$1" [P,L]
```
-
-#### Automatic HTTP to HTTPS redirect:
-
-```apacheconf
-
- Redirect permanent / https://127.0.0.1:3000/
+#### Automatic http to https redirect:
+```
+
+ Redirect permanent / https://127.0.0.1:3000/
-
- ProxyPass / http://127.0.0.1:3000/ upgrade=websocket
+
+ ProxyPass / https://127.0.0.1:3000/
+ RewriteEngine on
+ RewriteCond %{HTTP:Upgrade} websocket [NC]
+ RewriteCond %{HTTP:Connection} upgrade [NC]
+ RewriteRule ^/?(.*) "wws://127.0.0.1:3000/$1" [P,L]
```
-
-Activate the new virtual host and reload Apache:
-
-```bash
+Activate the new virtual host and reload apache:
+```shell
a2ensite pairdrop
```
-
-```bash
+```shell
service apache2 reload
```
-
+# Local Development
+## Install
+All files needed for developing are available on the branch `dev`.
-## Coturn and PairDrop via Docker Compose
+First, [Install docker with docker-compose.](https://docs.docker.com/compose/install/)
-### Setup container
-To run coturn and PairDrop at once by using the `docker-compose-coturn.yml` with TURN over TLS enabled
-you need to follow these steps:
+Then, clone the repository and run docker-compose:
+```shell
+ git clone https://github.com/schlagmichdoch/PairDrop.git
-1. Generate or retrieve certificates for your `` (e.g. letsencrypt / certbot)
-2. Create `./ssl` folder: `mkdir ssl`
-3. Copy your ssl-certificates and the privkey to `./ssl`
-4. Restrict access to `./ssl`: `chown -R nobody:nogroup ./ssl`
-5. Create a dh-params file: `openssl dhparam -out ./ssl/dhparams.pem 4096`
-6. Copy `rtc_config_example.json` to `rtc_config.json`
-7. Copy `turnserver_example.conf` to `turnserver.conf`
-8. Change `` in both files to the domain where your PairDrop instance is running
-9. Change `username` and `password` in `turnserver.conf` and `rtc-config.json`
-10. To start the container including coturn run: \
- `docker compose -f docker-compose-coturn.yml up -d`
+ cd PairDrop
-
-
-#### Setup container
-To restart the container including coturn run: \
- `docker compose -f docker-compose-coturn.yml restart`
-
-
-
-#### Setup container
-To stop the container including coturn run: \
- `docker compose -f docker-compose-coturn.yml stop`
-
-
-
-### Firewall
-To run PairDrop including its own coturn-server you need to punch holes in the firewall. These ports must be opened additionally:
-- 3478 tcp/udp
-- 5349 tcp/udp
-- 10000:20000 tcp/udp
-
-
-
-## Local Development
-
-### Install
-
-All files needed for developing are available in the folder `./dev`.
-
-For convenience, there is also a docker compose file for developing:
-
-#### Developing with docker compose
-First, [Install docker with docker compose.](https://docs.docker.com/compose/install/)
-
-Then, clone the repository and run docker compose:
-
-```bash
-git clone https://github.com/schlagmichdoch/PairDrop.git && cd PairDrop
-```
-```bash
-docker compose -f docker-compose-dev.yml up --no-deps --build
+ git checkout dev
+
+ docker-compose up -d
```
+Now point your browser to `http://localhost:8080`.
-Now point your web browser to `http://localhost:8080`.
+- To restart the containers run `docker-compose restart`.
+- To stop the containers run `docker-compose stop`.
+- To debug the NodeJS server run `docker logs pairdrop_node_1`.
-- To debug the Node.js server, run `docker logs pairdrop`.
-- After changes to the code you have to rerun the `docker compose` command
-#### Testing PWA related features
+## Testing PWA related features
+PWAs require that the app is served under a correctly set up and trusted TLS endpoint.
-PWAs requires the app to be served under a correctly set up and trusted TLS endpoint.
+The nginx container creates a CA certificate and a website certificate for you. To correctly set the common name of the certificate, you need to change the FQDN environment variable in `docker/fqdn.env` to the fully qualified domain name of your workstation.
-The NGINX container creates a CA certificate and a website certificate for you.
-To correctly set the common name of the certificate,
-you need to change the FQDN environment variable in `docker-compose-dev.yml`
-to the fully qualified domain name of your workstation. (Default: localhost)
-
-If you want to test PWA features, you need to trust the CA of the certificate for your local deployment. \
-For your convenience, you can download the crt file from `http://:8080/ca.crt`. \
-Install that certificate to the trust store of your operating system. \
-
-##### Windows
-- Make sure to install it to the `Trusted Root Certification Authorities` store.
-
-##### macOS
-- Double-click the installed CA certificate in `Keychain Access`,
-- expand `Trust`, and select `Always Trust` for SSL.
-
-##### Firefox
-Firefox uses its own trust store. To install the CA:
-- point Firefox at `http://:8080/ca.crt` (Default: `http://localhost:8080/ca.crt`)
-- When prompted, select `Trust this CA to identify websites` and click _OK_.
-
-Alternatively:
-1. Download `ca.crt` from `http://:8080/ca.crt` (Default: `http://localhost:8080/ca.crt`)
-2. Go to `about:preferences#privacy` scroll down to `Security` and `Certificates` and click `View Certificates`
-3. Import the downloaded certificate file (step 1)
-
-##### Chrome
-- When using Chrome, you need to restart Chrome so it reloads the trust store (`chrome://restart`).
-- Additionally, after installing a new cert, you need to clear the Storage (DevTools → Application → Clear storage → Clear site data).
-
-##### Google Chrome
-- To skip the installation of the certificate, you can also open `chrome://flags/#unsafely-treat-insecure-origin-as-secure`
-- The feature `Insecure origins treated as secure` must be enabled and the list must include your PairDrop test instance. E.g.: `http://127.0.0.1:3000,https://127.0.0.1:8443`
+If you want to test PWA features, you need to trust the CA of the certificate for your local deployment. For your convenience, you can download the crt file from `http://:8080/ca.crt`. Install that certificate to the trust store of your operating system.
+- On Windows, make sure to install it to the `Trusted Root Certification Authorities` store.
+- On MacOS, double click the installed CA certificate in `Keychain Access`, expand `Trust`, and select `Always Trust` for SSL.
+- Firefox uses its own trust store. To install the CA, point Firefox at `http://:8080/ca.crt`. When prompted, select `Trust this CA to identify websites` and click OK.
+- When using Chrome, you need to restart Chrome so it reloads the trust store (`chrome://restart`). Additionally, after installing a new cert, you need to clear the Storage (DevTools -> Application -> Clear storage -> Clear site data).
Please note that the certificates (CA and webserver cert) expire after a day.
-Also, whenever you restart the NGINX Docker container new certificates are created.
+Also, whenever you restart the nginx docker, container new certificates are created.
-The site is served on `https://:8443` (Default: `https://localhost:8443`).
+The site is served on `https://:8443`.
[< Back](/README.md)
diff --git a/docs/how-to.md b/docs/how-to.md
index df6d3a1..18c0fbd 100644
--- a/docs/how-to.md
+++ b/docs/how-to.md
@@ -1,142 +1,41 @@
# How-To
+## Share files directly from context menu on Windows
+### Registering to open files with PairDrop
+The [File Handling API](https://learn.microsoft.com/en-us/microsoft-edge/progressive-web-apps-chromium/how-to/handle-files) is implemented
-## Send directly from share menu on iOS
-I created an iOS shortcut to send images, files, folder, URLs \
-or text directly from the share-menu
+This is still experimental and must be enabled via a flag **before** the PWA is installed to Windows.
+1. [Enabled feature in Edge](https://learn.microsoft.com/en-us/microsoft-edge/progressive-web-apps-chromium/how-to/handle-files#enable-the-file-handling-api)
+2. Install PairDrop by visiting https://pairdrop.net/ with the Edge browser and install it as described [here](faq.md#help--i-cant-install-the-pwa-).
+3. You are done! You can now send most files one at a time via PairDrop:
+
+ _context menu > Open with > PairDrop_
+
+[//]: # (Todo: add screenshots)
+
+### Sending multiple files to PairDrop
+Outstandingly, it is also possible to send multiple files to PairDrop via the context menu by adding PairDrop to the `Send to` menu:
+1. [Register PairDrop as file handler](#registering-to-open-files-with-pairdrop)
+2. Hit Windows Key+R, type: `shell:programs` and hit Enter.
+3. Copy the PairDrop shortcut from the directory
+4. Hit Windows Key+R, type: `shell:sendto` and hit Enter.
+5. Paste the copied shortcut into the directory
+6. You are done! You can now send multiple files (but no directories) directly via PairDrop:
+
+ _context menu > Send to > PairDrop_
+
+[//]: # (Todo: add screenshots)
+
+## Share directly from share menu on iOS
+I created an iOS shortcut to send images, files, folder, URLs or text directly from the share-menu
https://routinehub.co/shortcut/13990/
-[//]: # (Todo: Add screenshots)
+[//]: # (Todo: add doku with screenshots)
-
-## Send directly from share menu on Android
-The [Web Share Target API](https://developer.mozilla.org/en-US/docs/Web/Manifest/share_target) is implemented.
+## Share directly from share menu on Android
+The [Web Share Target API](https://developer.mozilla.org/en-US/docs/Web/Manifest/share_target) is implemented but not yet tested.
+When the PWA is installed, it should register itself to the share-menu of the device automatically.
-When the PWA is installed, it will register itself to the share-menu of the device automatically.
-
-
-
-## Send directly via command-line interface
-Send files or text with PairDrop via command-line interface. \
-This opens PairDrop in the default browser where you can choose the receiver.
-
-### Usage
-```bash
-pairdrop -h
-```
-```
-Send files or text with PairDrop via command-line interface.
-Current domain: https://pairdrop-dev.onrender.com/
-
-Usage:
-Open PairDrop: pairdrop
-Send files: pairdrop file1/directory1 (file2/directory2 file3/directory3 ...)
-Send text: pairdrop -t "text"
-Specify domain: pairdrop -d "https://pairdrop.net/"
-Show this help text: pairdrop (-h|--help)
-
-This pairdrop-cli version was released alongside v1.10.4
-```
-
-
-
-### Setup
-
-#### Linux / Mac
-1. Download the latest _pairdrop-cli.zip_ from the [releases page](https://github.com/schlagmichdoch/PairDrop/releases)
- ```shell
- wget "https://github.com/schlagmichdoch/PairDrop/releases/download/v1.11.2/pairdrop-cli.zip"
- ```
- or
- ```shell
- curl -LO "https://github.com/schlagmichdoch/PairDrop/releases/download/v1.11.2/pairdrop-cli.zip"
- ```
-2. Unzip the archive to a folder of your choice e.g. `/usr/share/pairdrop-cli/`
- ```shell
- sudo unzip pairdrop-cli.zip -d /usr/share/pairdrop-cli/
- ```
-3. Copy the file _.pairdrop-cli-config.example_ to _.pairdrop-cli-config_
- ```shell
- sudo cp /usr/share/pairdrop-cli/.pairdrop-cli-config.example /usr/share/pairdrop-cli/.pairdrop-cli-config
- ```
-4. Make the bash file _pairdrop_ executable
- ```shell
- sudo chmod +x /usr/share/pairdrop-cli/pairdrop
- ```
-5. Add a symlink to /usr/local/bin/ to include _pairdrop_ to _PATH_
- ```shell
- sudo ln -s /usr/share/pairdrop-cli/pairdrop /usr/local/bin/pairdrop
- ```
-
-
-
-#### Windows
-1. Download the latest _pairdrop-cli.zip_ from the [releases page](https://github.com/schlagmichdoch/PairDrop/releases)
-2. Put file in a preferred folder e.g. `C:\Program Files\pairdrop-cli`
-3. Inside this folder, copy the file _.pairdrop-cli-config.example_ to _.pairdrop-cli-config_
-4. Search for and open `Edit environment variables for your account`
-5. Click `Environment Variables…`
-6. Under _System Variables_ select `Path` and click _Edit..._
-7. Click _New_, insert the preferred folder (`C:\Program Files\pairdrop-cli`), click *OK* until all windows are closed
-8. Reopen Command prompt window
-
-**Requirements**
-
-As Windows cannot execute bash scripts natively, you need to install [Git Bash](https://gitforwindows.org/).
-
-Then, you can also use pairdrop-cli from the default Windows Command Prompt
-by using the shell file instead of the bash file which then itself executes
-_pairdrop-cli_ (the bash file) via the Git Bash.
-```shell
-pairdrop.sh -h
-```
-
-
-
-## Send multiple files and directories directly from context menu on Windows
-
-### Registering to open files with PairDrop
-It is possible to send multiple files with PairDrop via the context menu by adding pairdrop-cli to Windows `Send to` menu:
-1. Download the latest _pairdrop-cli.zip_ from the [releases page](https://github.com/schlagmichdoch/PairDrop/releases)
-2. Unzip the archive to a folder of your choice e.g. `C:\Program Files\pairdrop-cli\`
-3. Inside this folder, copy the file _.pairdrop-cli-config.example_ to _.pairdrop-cli-config_
-4. Copy the shortcut _send with PairDrop.lnk_
-5. Hit Windows Key+R, type: `shell:sendto` and hit Enter.
-6. Paste the copied shortcut into the directory
-7. Open the properties window of the shortcut and edit the link field to point to _send-with-pairdrop.ps1_ located in the folder you used in step 2: \
- `"C:\Program Files\PowerShell\7\pwsh.exe" -File "C:\Program Files\pairdrop-cli\send-with-pairdrop.ps1"`
-8. You are done! You can now send multiple files and directories directly via PairDrop:
-
- _context menu_ > _Send to_ > _PairDrop_
-
-##### Requirements
-As Windows cannot execute bash scripts natively, you need to install [Git Bash](https://gitforwindows.org/).
-
-
-
-## Send multiple files and directories directly from context menu on Ubuntu using Nautilus
-
-### Registering to open files with PairDrop
-It is possible to send multiple files with PairDrop via the context menu by adding pairdrop-cli to Nautilus `Scripts` menu:
-1. Register _pairdrop_ as executable via [guide above](#linux).
-2. Copy the shell file _send-with-pairdrop_ to `~/.local/share/nautilus/scripts/` to include it in the context menu
- ```shell
- cp /usr/share/pairdrop-cli/send-with-pairdrop ~/.local/share/nautilus/scripts/
- ```
-3. Make the shell file _send-with-pairdrop_ executable
- ```shell
- chmod +x ~/.local/share/nautilus/scripts/send-with-pairdrop
- ```
-4. You are done! You can now send multiple files and directories directly via PairDrop:
-
- _context menu_ > _Scripts_ > _send-with-pairdrop_
-
-
-
-## File Handling API
-The [File Handling API](https://learn.microsoft.com/en-us/microsoft-edge/progressive-web-apps-chromium/how-to/handle-files)
-was implemented, but it was removed as default file associations were overwritten ([#17](https://github.com/schlagmichdoch/PairDrop/issues/17),
-[#116](https://github.com/schlagmichdoch/PairDrop/issues/116) [#190](https://github.com/schlagmichdoch/PairDrop/issues/190))
-and it only worked with explicitly specified file types and couldn't handle directories at all.
+Please test this feature and create an issue if it does not work.
[< Back](/README.md)
diff --git a/docs/pairdrop_screenshot_mobile.gif b/docs/pairdrop_screenshot_mobile.gif
index 1eb6eb1..1813720 100644
Binary files a/docs/pairdrop_screenshot_mobile.gif and b/docs/pairdrop_screenshot_mobile.gif differ
diff --git a/docs/technical-documentation.md b/docs/technical-documentation.md
index b8783c0..bf050ae 100644
--- a/docs/technical-documentation.md
+++ b/docs/technical-documentation.md
@@ -3,80 +3,48 @@
Encryption is mandatory for WebRTC connections and completely done by the browser itself.
-When the peers are first connecting, \
-a channel is created by exchanging their signaling info. \
-This signaling information includes some sort of public key \
-and is specific to the clients IP address. \
-That is what the STUN Server is used for: \
-it simply returns your public IP address \
-as you only know your local ip address \
+When the peers are first connecting, a channel is created by exchanging their signaling information.
+This signaling information includes some sort of public key and is specific to the clients ip address.
+That is what the STUN Server is used for: it simply returns your public IP address as you only know your local ip address
if behind a NAT (router).
-The transfer of the signaling info is done by the \
-PairDrop / Snapdrop server using secure websockets. \
-After that the channel itself is completely peer-to-peer \
-and all info can only be decrypted by the receiver. \
-When the two peers are on the same network \
-or when they are not behind any NAT system \
-(which they are always for classic \
-Snapdrop and for not paired users on PairDrop) \
-the files are send directly peer-to-peer.
+The transfer of the signaling information is done by the PairDrop / Snapdrop server using secure websockets.
+After that the channel itself is completely peer-2-peer and all information can only be decrypted by the receiver.
+When the two peers are on the same network or when they are not behind any NAT system (which they are always for classic
+Snapdrop and for not paired users on PairDrop) the files are send directly peer to peer.
-When a user is behind a NAT (behind a router) \
-the contents are channeled through a TURN server. \
-But again, the contents send via the channel \
-can only be decrypted by the receiver. \
-So a rogue TURN server could only \
-see that there is a connection, but not what is sent. \
-Obviously, connections which are channeled through a TURN server \
-are not as fast as peer-to-peer.
+When a user is behind a NAT (behind a router) the contents are channeled through a TURN server.
+But again, the contents send via the channel can only be decrypted by the receiver. So a rogue TURN server could only
+see that there is a connection, but not what is sent. Obviously, connections which are channeled through a TURN server
+are not as fast as peer to peer.
-The selection whether a TURN server is needed \
-or not is also done automatically by the web browser. \
-It simply iterated through the configured \
-RTC iceServers and checks what works. \
-Only if the STUN server is not sufficient, \
+The selection whether a TURN server is needed or not is also done automatically by the browser.
+It simply iterated through the configured RTC iceServers and checks what works. Only if the STUN server is not sufficient,
the TURN server is used.

_Diagram created by wowza.com_
-Good thing: if your device has an IPv6 address \
-it is uniquely reachable by that address. \
-As I understand it, when both devices are using \
-IPv6 addresses there is no need for a TURN server in any scenario.
+Good thing: if your device has an IPv6 address it is uniquely reachable by that address. As I understand it, when both devices are using IPv6 addresses there is no need for a TURN server in any scenario.
-Learn more by reading https://www.wowza.com/blog/webrtc-encryption-and-security \
-which gives a good insight into STUN, TURN and WebRTC.
+To learn more take a look at https://www.wowza.com/blog/webrtc-encryption-and-security which gives a good insight into stun, turn and webrtc
## Device Pairing
The pairing functionality uses the [IndexedDB API](https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API).
-It works by creating long secrets that are served \
-by the server to the initiating and requesting pair peer, \
-when the inserted key is correct. \
-These long secrets are then saved to an \
-indexedDB database in the web browser. \
-IndexedDB is somewhat the successor of localStorage \
-as saved data is shared between all tabs. \
-It goes one step further by making the data persistent \
-and available offline if implemented to a PWA.
+It works by creating long secrets that are served by the server to the initiating and requesting pair peer,
+when the inserted key is correct. These long secrets are then saved to an indexedDB database in the browser.
+IndexedDB is somewhat the successor of localStorage as saved data is shared between all tabs.
+It goes one step further by making the data persistent and available offline if implemented to a PWA.
-All secrets a client has saved to its database \
-are sent to the PairDrop server. \
-Peers with a common secret are discoverable \
-to each other analog to peers with the same \
-IP address are discoverable by each other.
+All secrets a client has saved to its database are send to the PairDrop server. Peers with a common secret are discoverable
+to each other analog to peers with the same ip-address are discoverable to each other.
-What I really like about this approach (and the reason I implemented it) \
-is that devices on the same network are always \
-visible regardless whether any devices are paired or not. \
-The main user flow is never obstructed. \
-Paired devices are simply shown additionally. \
-This makes it in my idea better than the idea of \
-using a room system as [discussed here](https://github.com/RobinLinus/snapdrop/pull/214).
+What I really like about this approach, and the reason why I implemented it, is that devices on the same network are always
+visible regardless whether any devices are paired or not. The main user flow is never obstructed. Paired devices are simply
+shown additionally. This makes it in my idea better than the idea of using a room system as [discussed here](https://github.com/RobinLinus/snapdrop/pull/214).
[< Back](/README.md)
diff --git a/index.js b/index.js
new file mode 100644
index 0000000..31fbca9
--- /dev/null
+++ b/index.js
@@ -0,0 +1,615 @@
+const process = require('process')
+const crypto = require('crypto')
+const {spawn} = require('child_process')
+
+// Handle SIGINT
+process.on('SIGINT', () => {
+ console.info("SIGINT Received, exiting...")
+ process.exit(0)
+})
+
+// Handle SIGTERM
+process.on('SIGTERM', () => {
+ console.info("SIGTERM Received, exiting...")
+ process.exit(0)
+})
+
+// Handle APP ERRORS
+process.on('uncaughtException', (error, origin) => {
+ console.log('----- Uncaught exception -----')
+ console.log(error)
+ console.log('----- Exception origin -----')
+ console.log(origin)
+})
+process.on('unhandledRejection', (reason, promise) => {
+ console.log('----- Unhandled Rejection at -----')
+ console.log(promise)
+ console.log('----- Reason -----')
+ console.log(reason)
+})
+
+if (process.argv.includes('--auto-restart')) {
+ process.on(
+ 'uncaughtException',
+ () => {
+ process.once(
+ 'exit',
+ () => spawn(
+ process.argv.shift(),
+ process.argv,
+ {
+ cwd: process.cwd(),
+ detached: true,
+ stdio: 'inherit'
+ }
+ )
+ );
+ process.exit();
+ }
+ );
+}
+
+const express = require('express');
+const RateLimit = require('express-rate-limit');
+const http = require('http');
+
+const app = express();
+
+if (process.argv.includes('--rate-limit')) {
+ const limiter = RateLimit({
+ windowMs: 5 * 60 * 1000, // 5 minutes
+ max: 1000, // Limit each IP to 1000 requests per `window` (here, per 5 minutes)
+ message: 'Too many requests from this IP Address, please try again after 5 minutes.',
+ standardHeaders: true, // Return rate limit info in the `RateLimit-*` headers
+ legacyHeaders: false, // Disable the `X-RateLimit-*` headers
+ })
+
+ app.use(limiter);
+ // ensure correct client ip and not the ip of the reverse proxy is used for rate limiting on render.com
+ // see https://github.com/express-rate-limit/express-rate-limit#troubleshooting-proxy-issues
+ app.set('trust proxy', 5);
+}
+
+if (process.argv.includes('--include-ws-fallback')) {
+ app.use(express.static('public_included_ws_fallback'));
+} else {
+ app.use(express.static('public'));
+}
+
+app.use(function(req, res) {
+ res.redirect('/');
+});
+
+app.get('/', (req, res) => {
+ res.sendFile('index.html');
+});
+
+const server = http.createServer(app);
+const port = process.env.PORT || 3000;
+
+if (process.argv.includes('--localhost-only')) {
+ server.listen(port, '127.0.0.1');
+} else {
+ server.listen(port);
+}
+
+const parser = require('ua-parser-js');
+const { uniqueNamesGenerator, animals, colors } = require('unique-names-generator');
+
+class PairDropServer {
+
+ constructor() {
+ const WebSocket = require('ws');
+ this._wss = new WebSocket.Server({ server });
+ this._wss.on('connection', (socket, request) => this._onConnection(new Peer(socket, request)));
+
+ this._rooms = {};
+ this._roomSecrets = {};
+
+ console.log('PairDrop is running on port', port);
+ }
+
+ _onConnection(peer) {
+ this._joinRoom(peer);
+ peer.socket.on('message', message => this._onMessage(peer, message));
+ peer.socket.onerror = e => console.error(e);
+ this._keepAlive(peer);
+
+ // send displayName
+ this._send(peer, {
+ type: 'display-name',
+ message: {
+ displayName: peer.name.displayName,
+ deviceName: peer.name.deviceName,
+ peerId: peer.id
+ }
+ });
+ }
+
+ _onMessage(sender, message) {
+ // Try to parse message
+ try {
+ message = JSON.parse(message);
+ } catch (e) {
+ return; // TODO: handle malformed JSON
+ }
+
+ switch (message.type) {
+ case 'disconnect':
+ this._onDisconnect(sender);
+ break;
+ case 'pong':
+ sender.lastBeat = Date.now();
+ break;
+ case 'room-secrets':
+ this._onRoomSecrets(sender, message);
+ break;
+ case 'room-secret-deleted':
+ this._onRoomSecretDeleted(sender, message);
+ break;
+ case 'room-secrets-cleared':
+ this._onRoomSecretsCleared(sender, message);
+ break;
+ case 'pair-device-initiate':
+ this._onPairDeviceInitiate(sender);
+ break;
+ case 'pair-device-join':
+ this._onPairDeviceJoin(sender, message);
+ break;
+ case 'pair-device-cancel':
+ this._onPairDeviceCancel(sender);
+ break;
+ case 'resend-peers':
+ this._notifyPeers(sender);
+ break;
+ case 'signal':
+ default:
+ this._signalAndRelay(sender, message);
+ }
+ }
+
+ _signalAndRelay(sender, message) {
+ const room = message.roomType === 'ip' ? sender.ip : message.roomSecret;
+
+ // relay message to recipient
+ if (message.to && Peer.isValidUuid(message.to) && this._rooms[room]) {
+ const recipient = this._rooms[room][message.to];
+ delete message.to;
+ // add sender
+ message.sender = {
+ id: sender.id,
+ rtcSupported: sender.rtcSupported
+ };
+ this._send(recipient, message);
+ }
+ }
+
+ _onDisconnect(sender) {
+ this._leaveRoom(sender, 'ip', '', true);
+ this._leaveAllSecretRooms(sender, true);
+ this._removeRoomKey(sender.roomKey);
+ sender.roomKey = null;
+ }
+
+ _onRoomSecrets(sender, message) {
+ const roomSecrets = message.roomSecrets.filter(roomSecret => {
+ return /^[\x00-\x7F]{64}$/.test(roomSecret);
+ })
+ this._joinSecretRooms(sender, roomSecrets);
+ }
+
+ _onRoomSecretDeleted(sender, message) {
+ this._deleteSecretRoom(sender, message.roomSecret)
+ }
+
+ _onRoomSecretsCleared(sender, message) {
+ for (let i = 0; i= 47 && r <= 57 || r >= 64 && r <= 90 || r >= 97 && r <= 122;
+ });
+ string += String.fromCharCode.apply(String, arr);
+ }
+ return string.substring(0, length)
+ }
+
+ _onPairDeviceInitiate(sender) {
+ let roomSecret = this.getRandomString(64);
+ let roomKey = this._createRoomKey(sender, roomSecret);
+ if (sender.roomKey) this._removeRoomKey(sender.roomKey);
+ sender.roomKey = roomKey;
+ this._send(sender, {
+ type: 'pair-device-initiated',
+ roomSecret: roomSecret,
+ roomKey: roomKey
+ });
+ this._joinRoom(sender, 'secret', roomSecret);
+ }
+
+ _onPairDeviceJoin(sender, message) {
+ if (sender.roomKeyRate >= 10) {
+ this._send(sender, { type: 'pair-device-join-key-rate-limit' });
+ return;
+ }
+ sender.roomKeyRate += 1;
+ setTimeout(_ => sender.roomKeyRate -= 1, 10000);
+ if (!this._roomSecrets[message.roomKey] || sender.id === this._roomSecrets[message.roomKey].creator.id) {
+ this._send(sender, { type: 'pair-device-join-key-invalid' });
+ return;
+ }
+ const roomSecret = this._roomSecrets[message.roomKey].roomSecret;
+ const creator = this._roomSecrets[message.roomKey].creator;
+ this._removeRoomKey(message.roomKey);
+ this._send(sender, {
+ type: 'pair-device-joined',
+ roomSecret: roomSecret,
+ peerId: creator.id
+ });
+ this._send(creator, {
+ type: 'pair-device-joined',
+ roomSecret: roomSecret,
+ peerId: sender.id
+ });
+ this._joinRoom(sender, 'secret', roomSecret);
+ this._removeRoomKey(sender.roomKey);
+ }
+
+ _onPairDeviceCancel(sender) {
+ if (sender.roomKey) {
+ this._send(sender, {
+ type: 'pair-device-canceled',
+ roomKey: sender.roomKey,
+ });
+ this._removeRoomKey(sender.roomKey);
+ }
+ }
+
+ _createRoomKey(creator, roomSecret) {
+ let roomKey;
+ do {
+ // get randomInt until keyRoom not occupied
+ roomKey = crypto.randomInt(1000000, 1999999).toString().substring(1); // include numbers with leading 0s
+ } while (roomKey in this._roomSecrets)
+
+ this._roomSecrets[roomKey] = {
+ roomSecret: roomSecret,
+ creator: creator
+ }
+
+ return roomKey;
+ }
+
+ _removeRoomKey(roomKey) {
+ if (roomKey in this._roomSecrets) {
+ this._roomSecrets[roomKey].creator.roomKey = null
+ delete this._roomSecrets[roomKey];
+ }
+ }
+
+ _joinRoom(peer, roomType = 'ip', roomSecret = '') {
+ const room = roomType === 'ip' ? peer.ip : roomSecret;
+
+ // if room doesn't exist, create it
+ if (!this._rooms[room]) {
+ this._rooms[room] = {};
+ }
+
+ this._notifyPeers(peer, roomType, roomSecret);
+
+ // add peer to room
+ this._rooms[room][peer.id] = peer;
+ // add secret to peer
+ if (roomType === 'secret') {
+ peer.addRoomSecret(roomSecret);
+ }
+ }
+
+ _leaveRoom(peer, roomType = 'ip', roomSecret = '', disconnect = false) {
+ const room = roomType === 'ip' ? peer.ip : roomSecret;
+
+ if (!this._rooms[room] || !this._rooms[room][peer.id]) return;
+ this._cancelKeepAlive(this._rooms[room][peer.id]);
+
+ // delete the peer
+ delete this._rooms[room][peer.id];
+
+ if (roomType === 'ip') {
+ peer.socket.terminate();
+ }
+
+ //if room is empty, delete the room
+ if (!Object.keys(this._rooms[room]).length) {
+ delete this._rooms[room];
+ } else {
+ // notify all other peers
+ for (const otherPeerId in this._rooms[room]) {
+ const otherPeer = this._rooms[room][otherPeerId];
+ this._send(otherPeer, {
+ type: 'peer-left',
+ peerId: peer.id,
+ roomType: roomType,
+ roomSecret: roomSecret,
+ disconnect: disconnect
+ });
+ }
+ }
+ //remove secret from peer
+ if (roomType === 'secret') {
+ peer.removeRoomSecret(roomSecret);
+ }
+ }
+
+ _notifyPeers(peer, roomType = 'ip', roomSecret = '') {
+ const room = roomType === 'ip' ? peer.ip : roomSecret;
+ if (!this._rooms[room]) return;
+
+ // notify all other peers
+ for (const otherPeerId in this._rooms[room]) {
+ if (otherPeerId === peer.id) continue;
+ const otherPeer = this._rooms[room][otherPeerId];
+ this._send(otherPeer, {
+ type: 'peer-joined',
+ peer: peer.getInfo(),
+ roomType: roomType,
+ roomSecret: roomSecret
+ });
+ }
+
+ // notify peer about the other peers
+ const otherPeers = [];
+ for (const otherPeerId in this._rooms[room]) {
+ if (otherPeerId === peer.id) continue;
+ otherPeers.push(this._rooms[room][otherPeerId].getInfo());
+ }
+
+ this._send(peer, {
+ type: 'peers',
+ peers: otherPeers,
+ roomType: roomType,
+ roomSecret: roomSecret
+ });
+ }
+
+ _joinSecretRooms(peer, roomSecrets) {
+ for (let i=0; i 2 * timeout) {
+ this._leaveRoom(peer);
+ this._leaveAllSecretRooms(peer);
+ return;
+ }
+
+ this._send(peer, { type: 'ping' });
+
+ peer.timerId = setTimeout(() => this._keepAlive(peer), timeout);
+ }
+
+ _cancelKeepAlive(peer) {
+ if (peer && peer.timerId) {
+ clearTimeout(peer.timerId);
+ }
+ }
+}
+
+
+
+class Peer {
+
+ constructor(socket, request) {
+ // set socket
+ this.socket = socket;
+
+ // set remote ip
+ this._setIP(request);
+
+ // set peer id
+ this._setPeerId(request)
+
+ // is WebRTC supported ?
+ this.rtcSupported = request.url.indexOf('webrtc') > -1;
+
+ // set name
+ this._setName(request);
+
+ // for keepalive
+ this.timerId = 0;
+ this.lastBeat = Date.now();
+
+ this.roomSecrets = [];
+ this.roomKey = null;
+ this.roomKeyRate = 0;
+ }
+
+ _setIP(request) {
+ if (request.headers['cf-connecting-ip']) {
+ this.ip = request.headers['cf-connecting-ip'].split(/\s*,\s*/)[0];
+ } else if (request.headers['x-forwarded-for']) {
+ this.ip = request.headers['x-forwarded-for'].split(/\s*,\s*/)[0];
+ } else {
+ this.ip = request.connection.remoteAddress;
+ }
+
+ // remove the prefix used for IPv4-translated addresses
+ if (this.ip.substring(0,7) === "::ffff:")
+ this.ip = this.ip.substring(7);
+
+ // IPv4 and IPv6 use different values to refer to localhost
+ // put all peers on the same network as the server into the same room as well
+ if (this.ip === '::1' || this.ipIsPrivate(this.ip)) {
+ this.ip = '127.0.0.1';
+ }
+ }
+
+ ipIsPrivate(ip) {
+ // if ip is IPv4
+ if (!ip.includes(":")) {
+ // 10.0.0.0 - 10.255.255.255 || 172.16.0.0 - 172.31.255.255 || 192.168.0.0 - 192.168.255.255
+ return /^(10)\.(.*)\.(.*)\.(.*)$/.test(ip) || /^(172)\.(1[6-9]|2[0-9]|3[0-1])\.(.*)\.(.*)$/.test(ip) || /^(192)\.(168)\.(.*)\.(.*)$/.test(ip)
+ }
+
+ // else: ip is IPv6
+ const firstWord = ip.split(":").find(el => !!el); //get first not empty word
+
+ // The original IPv6 Site Local addresses (fec0::/10) are deprecated. Range: fec0 - feff
+ if (/^fe[c-f][0-f]$/.test(firstWord))
+ return true;
+
+ // These days Unique Local Addresses (ULA) are used in place of Site Local.
+ // Range: fc00 - fcff
+ else if (/^fc[0-f]{2}$/.test(firstWord))
+ return true;
+
+ // Range: fd00 - fcff
+ else if (/^fd[0-f]{2}$/.test(firstWord))
+ return true;
+
+ // Link local addresses (prefixed with fe80) are not routable
+ else if (firstWord === "fe80")
+ return true;
+
+ // Discard Prefix
+ else if (firstWord === "100")
+ return true;
+
+ // Any other IP address is not Unique Local Address (ULA)
+ return false;
+ }
+
+ _setPeerId(request) {
+ let peer_id = new URL(request.url, "http://server").searchParams.get("peer_id");
+ if (peer_id && Peer.isValidUuid(peer_id)) {
+ this.id = peer_id;
+ } else {
+ this.id = crypto.randomUUID();
+ }
+ }
+
+ toString() {
+ return ``
+ }
+
+ _setName(req) {
+ let ua = parser(req.headers['user-agent']);
+
+
+ let deviceName = '';
+
+ if (ua.os && ua.os.name) {
+ deviceName = ua.os.name.replace('Mac OS', 'Mac') + ' ';
+ }
+
+ if (ua.device.model) {
+ deviceName += ua.device.model;
+ } else {
+ deviceName += ua.browser.name;
+ }
+
+ if(!deviceName)
+ deviceName = 'Unknown Device';
+
+ const displayName = uniqueNamesGenerator({
+ length: 2,
+ separator: ' ',
+ dictionaries: [colors, animals],
+ style: 'capital',
+ seed: this.id.hashCode()
+ })
+
+ this.name = {
+ model: ua.device.model,
+ os: ua.os.name,
+ browser: ua.browser.name,
+ type: ua.device.type,
+ deviceName,
+ displayName
+ };
+ }
+
+ getInfo() {
+ return {
+ id: this.id,
+ name: this.name,
+ rtcSupported: this.rtcSupported
+ }
+ }
+
+ static isValidUuid(uuid) {
+ return /^([0-9]|[a-f]){8}-(([0-9]|[a-f]){4}-){3}([0-9]|[a-f]){12}$/.test(uuid);
+ }
+
+ addRoomSecret(roomSecret) {
+ if (!(roomSecret in this.roomSecrets)) {
+ this.roomSecrets.push(roomSecret);
+ }
+ }
+
+ removeRoomSecret(roomSecret) {
+ if (roomSecret in this.roomSecrets) {
+ delete this.roomSecrets[roomSecret];
+ }
+ }
+}
+
+Object.defineProperty(String.prototype, 'hashCode', {
+ value: function() {
+ var hash = 0, i, chr;
+ for (i = 0; i < this.length; i++) {
+ chr = this.charCodeAt(i);
+ hash = ((hash << 5) - hash) + chr;
+ hash |= 0; // Convert to 32bit integer
+ }
+ return hash;
+ }
+});
+
+new PairDropServer();
diff --git a/licenses/BSD_3-Clause-zip-js b/licenses/BSD_3-Clause-zip-js
deleted file mode 100644
index 4f2de22..0000000
--- a/licenses/BSD_3-Clause-zip-js
+++ /dev/null
@@ -1,28 +0,0 @@
-BSD 3-Clause License
-
-Copyright (c) 2023, Gildas Lormeau
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are met:
-
-1. Redistributions of source code must retain the above copyright notice, this
- list of conditions and the following disclaimer.
-
-2. Redistributions in binary form must reproduce the above copyright notice,
- this list of conditions and the following disclaimer in the documentation
- and/or other materials provided with the distribution.
-
-3. Neither the name of the copyright holder nor the names of its
- contributors may be used to endorse or promote products derived from
- this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
\ No newline at end of file
diff --git a/licenses/MIT-NoSleep b/licenses/MIT-NoSleep
deleted file mode 100644
index fa1f83a..0000000
--- a/licenses/MIT-NoSleep
+++ /dev/null
@@ -1,22 +0,0 @@
-The MIT License (MIT)
-
-Copyright (c) Rich Tibbett
-
-Permission is hereby granted, free of charge, to any person obtaining
-a copy of this software and associated documentation files (the
-"Software"), to deal in the Software without restriction, including
-without limitation the rights to use, copy, modify, merge, publish,
-distribute, sublicense, and/or sell copies of the Software, and to
-permit persons to whom the Software is furnished to do so, subject to
-the following conditions:
-
-The above copyright notice and this permission notice shall be
-included in all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
\ No newline at end of file
diff --git a/licenses/MIT-heic2any b/licenses/MIT-heic2any
deleted file mode 100644
index 5d07689..0000000
--- a/licenses/MIT-heic2any
+++ /dev/null
@@ -1,22 +0,0 @@
-MIT License
-
-Copyright (c) 2020 Alex Corvi
-
-Permission is hereby granted, free of charge, to any person obtaining
-a copy of this software and associated documentation files (the
-"Software"), to deal in the Software without restriction, including
-without limitation the rights to use, copy, modify, merge, publish,
-distribute, sublicense, and/or sell copies of the Software, and to
-permit persons to whom the Software is furnished to do so, subject to
-the following conditions:
-
-The above copyright notice and this permission notice shall be
-included in all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
\ No newline at end of file
diff --git a/package-lock.json b/package-lock.json
index be60a08..c765441 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,19 +1,19 @@
{
"name": "pairdrop",
- "version": "1.11.2",
- "lockfileVersion": 3,
+ "version": "1.0.0",
+ "lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "pairdrop",
- "version": "1.11.2",
+ "version": "1.0.0",
"license": "ISC",
"dependencies": {
"express": "^4.18.2",
- "express-rate-limit": "^7.1.5",
- "ua-parser-js": "^1.0.37",
+ "express-rate-limit": "^6.7.0",
+ "ua-parser-js": "^1.0.33",
"unique-names-generator": "^4.3.0",
- "ws": "^8.16.0"
+ "ws": "^8.12.0"
},
"engines": {
"node": ">=15"
@@ -37,20 +37,20 @@
"integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg=="
},
"node_modules/body-parser": {
- "version": "1.20.3",
- "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz",
- "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==",
+ "version": "1.20.1",
+ "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz",
+ "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==",
"dependencies": {
"bytes": "3.1.2",
- "content-type": "~1.0.5",
+ "content-type": "~1.0.4",
"debug": "2.6.9",
"depd": "2.0.0",
"destroy": "1.2.0",
"http-errors": "2.0.0",
"iconv-lite": "0.4.24",
"on-finished": "2.4.1",
- "qs": "6.13.0",
- "raw-body": "2.5.2",
+ "qs": "6.11.0",
+ "raw-body": "2.5.1",
"type-is": "~1.6.18",
"unpipe": "1.0.0"
},
@@ -67,28 +67,13 @@
"node": ">= 0.8"
}
},
- "node_modules/call-bind-apply-helpers": {
+ "node_modules/call-bind": {
"version": "1.0.2",
- "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
- "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
+ "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz",
+ "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==",
"dependencies": {
- "es-errors": "^1.3.0",
- "function-bind": "^1.1.2"
- },
- "engines": {
- "node": ">= 0.4"
- }
- },
- "node_modules/call-bound": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.3.tgz",
- "integrity": "sha512-YTd+6wGlNlPxSuri7Y6X8tY2dmm12UMH66RpKMhiX6rsk5wXXnYgbUcOt8kiS31/AjfoTOvCsE+w8nZQLQnzHA==",
- "dependencies": {
- "call-bind-apply-helpers": "^1.0.1",
- "get-intrinsic": "^1.2.6"
- },
- "engines": {
- "node": ">= 0.4"
+ "function-bind": "^1.1.1",
+ "get-intrinsic": "^1.0.2"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
@@ -106,17 +91,17 @@
}
},
"node_modules/content-type": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz",
- "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==",
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz",
+ "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/cookie": {
- "version": "0.7.1",
- "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz",
- "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==",
+ "version": "0.5.0",
+ "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz",
+ "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==",
"engines": {
"node": ">= 0.6"
}
@@ -151,59 +136,19 @@
"npm": "1.2.8000 || >= 1.4.16"
}
},
- "node_modules/dunder-proto": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
- "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
- "dependencies": {
- "call-bind-apply-helpers": "^1.0.1",
- "es-errors": "^1.3.0",
- "gopd": "^1.2.0"
- },
- "engines": {
- "node": ">= 0.4"
- }
- },
"node_modules/ee-first": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
"integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="
},
"node_modules/encodeurl": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz",
- "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==",
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
+ "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==",
"engines": {
"node": ">= 0.8"
}
},
- "node_modules/es-define-property": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
- "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
- "engines": {
- "node": ">= 0.4"
- }
- },
- "node_modules/es-errors": {
- "version": "1.3.0",
- "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
- "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
- "engines": {
- "node": ">= 0.4"
- }
- },
- "node_modules/es-object-atoms": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
- "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
- "dependencies": {
- "es-errors": "^1.3.0"
- },
- "engines": {
- "node": ">= 0.4"
- }
- },
"node_modules/escape-html": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
@@ -218,36 +163,36 @@
}
},
"node_modules/express": {
- "version": "4.21.2",
- "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz",
- "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==",
+ "version": "4.18.2",
+ "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz",
+ "integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==",
"dependencies": {
"accepts": "~1.3.8",
"array-flatten": "1.1.1",
- "body-parser": "1.20.3",
+ "body-parser": "1.20.1",
"content-disposition": "0.5.4",
"content-type": "~1.0.4",
- "cookie": "0.7.1",
+ "cookie": "0.5.0",
"cookie-signature": "1.0.6",
"debug": "2.6.9",
"depd": "2.0.0",
- "encodeurl": "~2.0.0",
+ "encodeurl": "~1.0.2",
"escape-html": "~1.0.3",
"etag": "~1.8.1",
- "finalhandler": "1.3.1",
+ "finalhandler": "1.2.0",
"fresh": "0.5.2",
"http-errors": "2.0.0",
- "merge-descriptors": "1.0.3",
+ "merge-descriptors": "1.0.1",
"methods": "~1.1.2",
"on-finished": "2.4.1",
"parseurl": "~1.3.3",
- "path-to-regexp": "0.1.12",
+ "path-to-regexp": "0.1.7",
"proxy-addr": "~2.0.7",
- "qs": "6.13.0",
+ "qs": "6.11.0",
"range-parser": "~1.2.1",
"safe-buffer": "5.2.1",
- "send": "0.19.0",
- "serve-static": "1.16.2",
+ "send": "0.18.0",
+ "serve-static": "1.15.0",
"setprototypeof": "1.2.0",
"statuses": "2.0.1",
"type-is": "~1.6.18",
@@ -256,33 +201,26 @@
},
"engines": {
"node": ">= 0.10.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/express"
}
},
"node_modules/express-rate-limit": {
- "version": "7.5.0",
- "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-7.5.0.tgz",
- "integrity": "sha512-eB5zbQh5h+VenMPM3fh+nw1YExi5nMr6HUCR62ELSP11huvxm/Uir1H1QEyTkk5QX6A58pX6NmaTMceKZ0Eodg==",
+ "version": "6.7.0",
+ "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-6.7.0.tgz",
+ "integrity": "sha512-vhwIdRoqcYB/72TK3tRZI+0ttS8Ytrk24GfmsxDXK9o9IhHNO5bXRiXQSExPQ4GbaE5tvIS7j1SGrxsuWs+sGA==",
"engines": {
- "node": ">= 16"
- },
- "funding": {
- "url": "https://github.com/sponsors/express-rate-limit"
+ "node": ">= 12.9.0"
},
"peerDependencies": {
- "express": "^4.11 || 5 || ^5.0.0-beta.1"
+ "express": "^4 || ^5"
}
},
"node_modules/finalhandler": {
- "version": "1.3.1",
- "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz",
- "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==",
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz",
+ "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==",
"dependencies": {
"debug": "2.6.9",
- "encodeurl": "~2.0.0",
+ "encodeurl": "~1.0.2",
"escape-html": "~1.0.3",
"on-finished": "2.4.1",
"parseurl": "~1.3.3",
@@ -310,63 +248,38 @@
}
},
"node_modules/function-bind": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
- "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
+ "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="
},
"node_modules/get-intrinsic": {
- "version": "1.3.0",
- "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
- "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.3.tgz",
+ "integrity": "sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A==",
"dependencies": {
- "call-bind-apply-helpers": "^1.0.2",
- "es-define-property": "^1.0.1",
- "es-errors": "^1.3.0",
- "es-object-atoms": "^1.1.1",
- "function-bind": "^1.1.2",
- "get-proto": "^1.0.1",
- "gopd": "^1.2.0",
- "has-symbols": "^1.1.0",
- "hasown": "^2.0.2",
- "math-intrinsics": "^1.1.0"
- },
- "engines": {
- "node": ">= 0.4"
+ "function-bind": "^1.1.1",
+ "has": "^1.0.3",
+ "has-symbols": "^1.0.3"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
- "node_modules/get-proto": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
- "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
+ "node_modules/has": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
+ "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
"dependencies": {
- "dunder-proto": "^1.0.1",
- "es-object-atoms": "^1.0.0"
+ "function-bind": "^1.1.1"
},
"engines": {
- "node": ">= 0.4"
- }
- },
- "node_modules/gopd": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
- "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
+ "node": ">= 0.4.0"
}
},
"node_modules/has-symbols": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
- "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
+ "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==",
"engines": {
"node": ">= 0.4"
},
@@ -374,17 +287,6 @@
"url": "https://github.com/sponsors/ljharb"
}
},
- "node_modules/hasown": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
- "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
- "dependencies": {
- "function-bind": "^1.1.2"
- },
- "engines": {
- "node": ">= 0.4"
- }
- },
"node_modules/http-errors": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz",
@@ -424,14 +326,6 @@
"node": ">= 0.10"
}
},
- "node_modules/math-intrinsics": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
- "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
- "engines": {
- "node": ">= 0.4"
- }
- },
"node_modules/media-typer": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
@@ -441,12 +335,9 @@
}
},
"node_modules/merge-descriptors": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz",
- "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==",
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
+ "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w=="
},
"node_modules/methods": {
"version": "1.1.2",
@@ -500,12 +391,9 @@
}
},
"node_modules/object-inspect": {
- "version": "1.13.4",
- "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz",
- "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==",
- "engines": {
- "node": ">= 0.4"
- },
+ "version": "1.12.2",
+ "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz",
+ "integrity": "sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==",
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
@@ -530,9 +418,9 @@
}
},
"node_modules/path-to-regexp": {
- "version": "0.1.12",
- "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz",
- "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ=="
+ "version": "0.1.7",
+ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
+ "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ=="
},
"node_modules/proxy-addr": {
"version": "2.0.7",
@@ -547,11 +435,11 @@
}
},
"node_modules/qs": {
- "version": "6.13.0",
- "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz",
- "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==",
+ "version": "6.11.0",
+ "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz",
+ "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==",
"dependencies": {
- "side-channel": "^1.0.6"
+ "side-channel": "^1.0.4"
},
"engines": {
"node": ">=0.6"
@@ -569,9 +457,9 @@
}
},
"node_modules/raw-body": {
- "version": "2.5.2",
- "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz",
- "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==",
+ "version": "2.5.1",
+ "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz",
+ "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==",
"dependencies": {
"bytes": "3.1.2",
"http-errors": "2.0.0",
@@ -607,9 +495,9 @@
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
},
"node_modules/send": {
- "version": "0.19.0",
- "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz",
- "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==",
+ "version": "0.18.0",
+ "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz",
+ "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==",
"dependencies": {
"debug": "2.6.9",
"depd": "2.0.0",
@@ -629,28 +517,20 @@
"node": ">= 0.8.0"
}
},
- "node_modules/send/node_modules/encodeurl": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
- "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==",
- "engines": {
- "node": ">= 0.8"
- }
- },
"node_modules/send/node_modules/ms": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
},
"node_modules/serve-static": {
- "version": "1.16.2",
- "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz",
- "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==",
+ "version": "1.15.0",
+ "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz",
+ "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==",
"dependencies": {
- "encodeurl": "~2.0.0",
+ "encodeurl": "~1.0.2",
"escape-html": "~1.0.3",
"parseurl": "~1.3.3",
- "send": "0.19.0"
+ "send": "0.18.0"
},
"engines": {
"node": ">= 0.8.0"
@@ -662,68 +542,13 @@
"integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="
},
"node_modules/side-channel": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz",
- "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==",
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz",
+ "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==",
"dependencies": {
- "es-errors": "^1.3.0",
- "object-inspect": "^1.13.3",
- "side-channel-list": "^1.0.0",
- "side-channel-map": "^1.0.1",
- "side-channel-weakmap": "^1.0.2"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/side-channel-list": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz",
- "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==",
- "dependencies": {
- "es-errors": "^1.3.0",
- "object-inspect": "^1.13.3"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/side-channel-map": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz",
- "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==",
- "dependencies": {
- "call-bound": "^1.0.2",
- "es-errors": "^1.3.0",
- "get-intrinsic": "^1.2.5",
- "object-inspect": "^1.13.3"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/side-channel-weakmap": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz",
- "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==",
- "dependencies": {
- "call-bound": "^1.0.2",
- "es-errors": "^1.3.0",
- "get-intrinsic": "^1.2.5",
- "object-inspect": "^1.13.3",
- "side-channel-map": "^1.0.1"
- },
- "engines": {
- "node": ">= 0.4"
+ "call-bind": "^1.0.0",
+ "get-intrinsic": "^1.0.2",
+ "object-inspect": "^1.9.0"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
@@ -758,9 +583,9 @@
}
},
"node_modules/ua-parser-js": {
- "version": "1.0.40",
- "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-1.0.40.tgz",
- "integrity": "sha512-z6PJ8Lml+v3ichVojCiB8toQJBuwR42ySM4ezjXIqXK3M0HczmKQ3LF4rhU55PfD99KEEXQG6yb7iOMyvYuHew==",
+ "version": "1.0.33",
+ "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-1.0.33.tgz",
+ "integrity": "sha512-RqshF7TPTE0XLYAqmjlu5cLLuGdKrNu9O1KLA/qp39QtbZwuzwv1dT46DZSopoUMsYgXpB3Cv8a03FI8b74oFQ==",
"funding": [
{
"type": "opencollective",
@@ -769,15 +594,8 @@
{
"type": "paypal",
"url": "https://paypal.me/faisalman"
- },
- {
- "type": "github",
- "url": "https://github.com/sponsors/faisalman"
}
],
- "bin": {
- "ua-parser-js": "script/cli.js"
- },
"engines": {
"node": "*"
}
@@ -815,9 +633,9 @@
}
},
"node_modules/ws": {
- "version": "8.18.1",
- "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.1.tgz",
- "integrity": "sha512-RKW2aJZMXeMxVpnZ6bck+RswznaxmzdULiBr6KY7XkTnW8uvt0iT9H5DkHUChXrc+uurzwa0rVI16n/Xzjdz1w==",
+ "version": "8.12.0",
+ "resolved": "https://registry.npmjs.org/ws/-/ws-8.12.0.tgz",
+ "integrity": "sha512-kU62emKIdKVeEIOIKVegvqpXMSTAMLJozpHZaJNDYqBjzlSYXQGviYwN1osDLJ9av68qHd4a2oSjd7yD4pacig==",
"engines": {
"node": ">=10.0.0"
},
@@ -834,5 +652,453 @@
}
}
}
+ },
+ "dependencies": {
+ "accepts": {
+ "version": "1.3.8",
+ "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
+ "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==",
+ "requires": {
+ "mime-types": "~2.1.34",
+ "negotiator": "0.6.3"
+ }
+ },
+ "array-flatten": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
+ "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg=="
+ },
+ "body-parser": {
+ "version": "1.20.1",
+ "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz",
+ "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==",
+ "requires": {
+ "bytes": "3.1.2",
+ "content-type": "~1.0.4",
+ "debug": "2.6.9",
+ "depd": "2.0.0",
+ "destroy": "1.2.0",
+ "http-errors": "2.0.0",
+ "iconv-lite": "0.4.24",
+ "on-finished": "2.4.1",
+ "qs": "6.11.0",
+ "raw-body": "2.5.1",
+ "type-is": "~1.6.18",
+ "unpipe": "1.0.0"
+ }
+ },
+ "bytes": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
+ "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg=="
+ },
+ "call-bind": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz",
+ "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==",
+ "requires": {
+ "function-bind": "^1.1.1",
+ "get-intrinsic": "^1.0.2"
+ }
+ },
+ "content-disposition": {
+ "version": "0.5.4",
+ "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
+ "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==",
+ "requires": {
+ "safe-buffer": "5.2.1"
+ }
+ },
+ "content-type": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz",
+ "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA=="
+ },
+ "cookie": {
+ "version": "0.5.0",
+ "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz",
+ "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw=="
+ },
+ "cookie-signature": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
+ "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ=="
+ },
+ "debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "requires": {
+ "ms": "2.0.0"
+ }
+ },
+ "depd": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
+ "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw=="
+ },
+ "destroy": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz",
+ "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg=="
+ },
+ "ee-first": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
+ "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="
+ },
+ "encodeurl": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
+ "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w=="
+ },
+ "escape-html": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
+ "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow=="
+ },
+ "etag": {
+ "version": "1.8.1",
+ "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
+ "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg=="
+ },
+ "express": {
+ "version": "4.18.2",
+ "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz",
+ "integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==",
+ "requires": {
+ "accepts": "~1.3.8",
+ "array-flatten": "1.1.1",
+ "body-parser": "1.20.1",
+ "content-disposition": "0.5.4",
+ "content-type": "~1.0.4",
+ "cookie": "0.5.0",
+ "cookie-signature": "1.0.6",
+ "debug": "2.6.9",
+ "depd": "2.0.0",
+ "encodeurl": "~1.0.2",
+ "escape-html": "~1.0.3",
+ "etag": "~1.8.1",
+ "finalhandler": "1.2.0",
+ "fresh": "0.5.2",
+ "http-errors": "2.0.0",
+ "merge-descriptors": "1.0.1",
+ "methods": "~1.1.2",
+ "on-finished": "2.4.1",
+ "parseurl": "~1.3.3",
+ "path-to-regexp": "0.1.7",
+ "proxy-addr": "~2.0.7",
+ "qs": "6.11.0",
+ "range-parser": "~1.2.1",
+ "safe-buffer": "5.2.1",
+ "send": "0.18.0",
+ "serve-static": "1.15.0",
+ "setprototypeof": "1.2.0",
+ "statuses": "2.0.1",
+ "type-is": "~1.6.18",
+ "utils-merge": "1.0.1",
+ "vary": "~1.1.2"
+ }
+ },
+ "express-rate-limit": {
+ "version": "6.7.0",
+ "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-6.7.0.tgz",
+ "integrity": "sha512-vhwIdRoqcYB/72TK3tRZI+0ttS8Ytrk24GfmsxDXK9o9IhHNO5bXRiXQSExPQ4GbaE5tvIS7j1SGrxsuWs+sGA==",
+ "requires": {}
+ },
+ "finalhandler": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz",
+ "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==",
+ "requires": {
+ "debug": "2.6.9",
+ "encodeurl": "~1.0.2",
+ "escape-html": "~1.0.3",
+ "on-finished": "2.4.1",
+ "parseurl": "~1.3.3",
+ "statuses": "2.0.1",
+ "unpipe": "~1.0.0"
+ }
+ },
+ "forwarded": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
+ "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow=="
+ },
+ "fresh": {
+ "version": "0.5.2",
+ "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
+ "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q=="
+ },
+ "function-bind": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
+ "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="
+ },
+ "get-intrinsic": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.3.tgz",
+ "integrity": "sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A==",
+ "requires": {
+ "function-bind": "^1.1.1",
+ "has": "^1.0.3",
+ "has-symbols": "^1.0.3"
+ }
+ },
+ "has": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
+ "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
+ "requires": {
+ "function-bind": "^1.1.1"
+ }
+ },
+ "has-symbols": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
+ "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A=="
+ },
+ "http-errors": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz",
+ "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==",
+ "requires": {
+ "depd": "2.0.0",
+ "inherits": "2.0.4",
+ "setprototypeof": "1.2.0",
+ "statuses": "2.0.1",
+ "toidentifier": "1.0.1"
+ }
+ },
+ "iconv-lite": {
+ "version": "0.4.24",
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
+ "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
+ "requires": {
+ "safer-buffer": ">= 2.1.2 < 3"
+ }
+ },
+ "inherits": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
+ },
+ "ipaddr.js": {
+ "version": "1.9.1",
+ "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
+ "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g=="
+ },
+ "media-typer": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
+ "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ=="
+ },
+ "merge-descriptors": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
+ "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w=="
+ },
+ "methods": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
+ "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w=="
+ },
+ "mime": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
+ "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg=="
+ },
+ "mime-db": {
+ "version": "1.52.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
+ "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="
+ },
+ "mime-types": {
+ "version": "2.1.35",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
+ "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
+ "requires": {
+ "mime-db": "1.52.0"
+ }
+ },
+ "ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
+ },
+ "negotiator": {
+ "version": "0.6.3",
+ "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
+ "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg=="
+ },
+ "object-inspect": {
+ "version": "1.12.2",
+ "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz",
+ "integrity": "sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ=="
+ },
+ "on-finished": {
+ "version": "2.4.1",
+ "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
+ "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==",
+ "requires": {
+ "ee-first": "1.1.1"
+ }
+ },
+ "parseurl": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
+ "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ=="
+ },
+ "path-to-regexp": {
+ "version": "0.1.7",
+ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
+ "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ=="
+ },
+ "proxy-addr": {
+ "version": "2.0.7",
+ "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
+ "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==",
+ "requires": {
+ "forwarded": "0.2.0",
+ "ipaddr.js": "1.9.1"
+ }
+ },
+ "qs": {
+ "version": "6.11.0",
+ "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz",
+ "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==",
+ "requires": {
+ "side-channel": "^1.0.4"
+ }
+ },
+ "range-parser": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
+ "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg=="
+ },
+ "raw-body": {
+ "version": "2.5.1",
+ "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz",
+ "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==",
+ "requires": {
+ "bytes": "3.1.2",
+ "http-errors": "2.0.0",
+ "iconv-lite": "0.4.24",
+ "unpipe": "1.0.0"
+ }
+ },
+ "safe-buffer": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
+ "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="
+ },
+ "safer-buffer": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
+ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
+ },
+ "send": {
+ "version": "0.18.0",
+ "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz",
+ "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==",
+ "requires": {
+ "debug": "2.6.9",
+ "depd": "2.0.0",
+ "destroy": "1.2.0",
+ "encodeurl": "~1.0.2",
+ "escape-html": "~1.0.3",
+ "etag": "~1.8.1",
+ "fresh": "0.5.2",
+ "http-errors": "2.0.0",
+ "mime": "1.6.0",
+ "ms": "2.1.3",
+ "on-finished": "2.4.1",
+ "range-parser": "~1.2.1",
+ "statuses": "2.0.1"
+ },
+ "dependencies": {
+ "ms": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
+ }
+ }
+ },
+ "serve-static": {
+ "version": "1.15.0",
+ "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz",
+ "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==",
+ "requires": {
+ "encodeurl": "~1.0.2",
+ "escape-html": "~1.0.3",
+ "parseurl": "~1.3.3",
+ "send": "0.18.0"
+ }
+ },
+ "setprototypeof": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
+ "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="
+ },
+ "side-channel": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz",
+ "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==",
+ "requires": {
+ "call-bind": "^1.0.0",
+ "get-intrinsic": "^1.0.2",
+ "object-inspect": "^1.9.0"
+ }
+ },
+ "statuses": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
+ "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ=="
+ },
+ "toidentifier": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
+ "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA=="
+ },
+ "type-is": {
+ "version": "1.6.18",
+ "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
+ "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
+ "requires": {
+ "media-typer": "0.3.0",
+ "mime-types": "~2.1.24"
+ }
+ },
+ "ua-parser-js": {
+ "version": "1.0.33",
+ "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-1.0.33.tgz",
+ "integrity": "sha512-RqshF7TPTE0XLYAqmjlu5cLLuGdKrNu9O1KLA/qp39QtbZwuzwv1dT46DZSopoUMsYgXpB3Cv8a03FI8b74oFQ=="
+ },
+ "unique-names-generator": {
+ "version": "4.7.1",
+ "resolved": "https://registry.npmjs.org/unique-names-generator/-/unique-names-generator-4.7.1.tgz",
+ "integrity": "sha512-lMx9dX+KRmG8sq6gulYYpKWZc9RlGsgBR6aoO8Qsm3qvkSJ+3rAymr+TnV8EDMrIrwuFJ4kruzMWM/OpYzPoow=="
+ },
+ "unpipe": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
+ "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ=="
+ },
+ "utils-merge": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
+ "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA=="
+ },
+ "vary": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
+ "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg=="
+ },
+ "ws": {
+ "version": "8.12.0",
+ "resolved": "https://registry.npmjs.org/ws/-/ws-8.12.0.tgz",
+ "integrity": "sha512-kU62emKIdKVeEIOIKVegvqpXMSTAMLJozpHZaJNDYqBjzlSYXQGviYwN1osDLJ9av68qHd4a2oSjd7yD4pacig==",
+ "requires": {}
+ }
}
}
diff --git a/package.json b/package.json
index e4032f6..416905c 100644
--- a/package.json
+++ b/package.json
@@ -1,21 +1,20 @@
{
"name": "pairdrop",
- "version": "1.11.2",
- "type": "module",
+ "version": "1.0.0",
"description": "",
- "main": "server/index.js",
+ "main": "index.js",
"scripts": {
- "start": "node server/index.js",
- "start:prod": "node server/index.js --rate-limit --auto-restart"
+ "start": "node index.js",
+ "start:prod": "node index.js --rate-limit --auto-restart"
},
"author": "",
"license": "ISC",
"dependencies": {
"express": "^4.18.2",
- "express-rate-limit": "^7.1.5",
- "ua-parser-js": "^1.0.37",
+ "express-rate-limit": "^6.7.0",
+ "ua-parser-js": "^1.0.33",
"unique-names-generator": "^4.3.0",
- "ws": "^8.16.0"
+ "ws": "^8.12.0"
},
"engines": {
"node": ">=15"
diff --git a/pairdrop-cli/.gitignore b/pairdrop-cli/.gitignore
deleted file mode 100644
index e75a6be..0000000
--- a/pairdrop-cli/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-.pairdrop-cli-config
\ No newline at end of file
diff --git a/pairdrop-cli/.pairdrop-cli-config.example b/pairdrop-cli/.pairdrop-cli-config.example
deleted file mode 100644
index 163b9e1..0000000
--- a/pairdrop-cli/.pairdrop-cli-config.example
+++ /dev/null
@@ -1 +0,0 @@
-DOMAIN=https://pairdrop.net/
\ No newline at end of file
diff --git a/pairdrop-cli/pairdrop b/pairdrop-cli/pairdrop
deleted file mode 100644
index e4c662a..0000000
--- a/pairdrop-cli/pairdrop
+++ /dev/null
@@ -1,382 +0,0 @@
-#!/bin/bash
-set -e
-
-# PairDrop version when this file was last changed
-version="v1.10.4"
-
-############################################################
-# Help #
-############################################################
-help()
-{
- # Display Help
- echo "Send files or text with PairDrop via command-line interface."
- echo "Current domain: ${DOMAIN}"
- echo
- echo "Usage:"
- echo -e "Open PairDrop:\t\t$(basename "$0")"
- echo -e "Send files:\t\t$(basename "$0") file1/directory1 (file2/directory2 file3/directory3 ...)"
- echo -e "Send text:\t\t$(basename "$0") -t \"text\""
- echo -e "Specify domain:\t\t$(basename "$0") -d \"https://pairdrop.net/\""
- echo -e "Show this help text:\t$(basename "$0") (-h|--help)"
- echo
- echo "This pairdrop-cli version was released alongside ${version}"
-}
-
-openPairDrop()
-{
- url="$DOMAIN"
- if [[ -n $params ]];then
- url="${url}?${params}"
- fi
- if [[ -n $hash ]];then
- url="${url}#${hash}"
- fi
-
- echo "PairDrop is opening at $DOMAIN"
- if [[ $OS == "Windows" ]];then
- start "$url"
- elif [[ $OS == "Mac" ]];then
- open "$url"
- elif [[ $OS == "WSL" || $OS == "WSL2" ]];then
- powershell.exe /c "Start-Process ${url}"
- else
- xdg-open "$url" > /dev/null 2>&1
- fi
-
-
- exit
-
-}
-
-setOs()
-{
- unameOut=$(uname -a)
- case "${unameOut}" in
- *Microsoft*) OS="WSL";; #must be first since Windows subsystem for linux will have Linux in the name too
- *microsoft*) OS="WSL2";; #WARNING: My v2 uses ubuntu 20.4 at the moment slightly different name may not always work
- Linux*) OS="Linux";;
- Darwin*) OS="Mac";;
- CYGWIN*) OS="Cygwin";;
- MINGW*) OS="Windows";;
- *Msys) OS="Windows";;
- *) OS="UNKNOWN:${unameOut}"
- esac
-}
-
-specifyDomain()
-{
- [[ ! $1 = http* ]] || [[ ! $1 = */ ]] && echo "Incorrect format. Specify domain like https://pairdrop.net/" && exit
- echo "DOMAIN=${1}" > "$config_path"
- echo -e "Domain is now set to:\n$1\n"
-}
-
-sendText()
-{
- params="base64text=hash"
- hash=$(echo -n "${OPTARG}" | base64)
-
- if [[ $(echo -n "$hash" | wc -m) -gt 32600 ]];then
- params="base64text=paste"
- if [[ $OS == "Windows" || $OS == "WSL" || $OS == "WSL2" ]];then
- echo -n "$hash" | clip.exe
- elif [[ $OS == "Mac" ]];then
- echo -n "$hash" | pbcopy
- else
- (echo -n "$hash" | xclip) || echo "You need to install xclip for sending bigger files from cli"
- fi
- hash=
- fi
-
- openPairDrop
- exit
-}
-
-escapePSPath()
-{
- local path=$1
-
- # escape '[' and ']' with grave accent (`) character
- pathPS=${path//[/\`[}
- pathPS=${pathPS//]/\`]}
- # escape single quote (') with another single quote (')
- pathPS=${pathPS//\'/\'\'}
-
- # Convert GitHub bash path "/i/path" to Windows path "I:/path"
- if [[ $pathPS == /* ]]; then
- # Remove preceding slash
- pathPS="${pathPS#/}"
- # Convert drive letter to uppercase
- driveLetter=$(echo "${pathPS::1}" | tr '[:lower:]' '[:upper:]')
- # Put together absolute path as used in Windows
- pathPS="${driveLetter}:${pathPS:1}"
- fi
-
- echo "$pathPS"
-}
-
-sendFiles()
-{
- params="base64zip=hash"
- workingDir="$(pwd)"
- tmpDir="/tmp/pairdrop-cli-temp/"
- tmpDirPS="\$env:TEMP/pairdrop-cli-temp/"
-
- index=0
- directoryBaseNamesUnix=()
- directoryPathsUnix=()
- filePathsUnix=()
- directoryCount=0
- fileCount=0
- pathsPS=""
-
- #create tmp folder if it does not exist already
- if [[ ! -d "$tmpDir" ]]; then
- mkdir "$tmpDir"
- fi
-
- for arg in "$@"; do
- echo "$arg"
- [[ ! -e "$arg" ]] && echo "The given path $arg does not exist." && exit
-
- # Remove trailing slash from directory
- arg="${arg%/}"
-
- # get absolute path and basename of file/directory
- absolutePath=$(realpath "$arg")
- baseName=$(basename "$absolutePath")
- directoryPath=$(dirname "$absolutePath")
-
- if [[ -d $absolutePath ]]; then
- # is directory
- ((directoryCount+=1))
- # add basename and directory path to arrays
- directoryBaseNamesUnix+=("$baseName")
- directoryPathsUnix+=("$directoryPath")
- else
- # is file
- ((fileCount+=1))
- absolutePathUnix=$absolutePath
- # append new path and separate paths with space
- filePathsUnix+=("$absolutePathUnix")
- fi
-
- # Prepare paths for PowerShell on Windows
- if [[ $OS == "Windows" ]];then
- absolutePathPS=$(escapePSPath "$absolutePath")
-
- # append new path and separate paths with commas
- pathsPS+="'${absolutePathPS}', "
- fi
-
- # set fileNames on first loop
- if [[ $index == 0 ]]; then
- baseNameU=${baseName// /_}
-
- # Prevent baseNameU being empty for hidden files by removing the preceding dot
- if [[ $baseNameU == .* ]]; then
- baseNameU=${baseNameU#.*}
- fi
-
- # only use trunk of basename "document.txt" -> "document"
- baseNameTrunk=${baseNameU%.*}
-
- # remove all special characters
- zipName=${baseNameTrunk//[^a-zA-Z0-9_]/}
-
- zipToSendAbs="${tmpDir}${zipName}_pairdrop.zip"
- wrapperZipAbs="${tmpDir}${zipName}_pairdrop_wrapper.zip"
-
- if [[ $OS == "Windows" ]];then
- zipToSendAbsPS="${tmpDirPS}${zipName}_pairdrop.zip"
- wrapperZipAbsPS="${tmpDirPS}${zipName}_pairdrop_wrapper.zip"
- fi
- fi
-
- ((index+=1)) # somehow ((index++)) stops the script
- done
-
- # Prepare paths for PowerShell on Windows
- if [[ $OS == "Windows" ]];then
- # remove trailing comma
- pathsPS=${pathsPS%??}
- fi
-
- echo "Preparing ${fileCount} files and ${directoryCount} directories..."
-
- # if arguments include files only -> zip files once so files it is unzipped by sending browser
- # if arguments include directories -> wrap first zip in a second wrapper zip so that after unzip by sending browser a zip file is sent to receiver
- #
- # Preferred zip structure:
- # pairdrop "d1/d2/d3/f1" "../../d4/d5/d6/f2" "d7/" "../d8/" "f5"
- # zip structure: pairdrop.zip
- # |-f1
- # |-f2
- # |-d7/
- # |-d8/
- # |-f5
- # -> truncate (relative) paths but keep directories
-
- [[ -e "$zipToSendAbs" ]] && echo "Cannot overwrite $zipToSendAbs. Please remove first." && exit
-
- if [[ $OS == "Windows" ]];then
- # Powershell does preferred zip structure natively
- powershell.exe -Command "Compress-Archive -Path ${pathsPS} -DestinationPath ${zipToSendAbsPS}"
- else
- # Workaround needed to create preferred zip structure on unix systems
- # Create zip file with all single files by junking the path
- if [[ $fileCount != 0 ]]; then
- zip -q -b /tmp/ -j -0 -r "$zipToSendAbs" "${filePathsUnix[@]}"
- fi
-
- # Add directories recursively to zip file
- index=0
- while [[ $index < $directoryCount ]]; do
- # workaround to keep directory name but junk the rest of the paths
-
- # cd to path above directory
- cd "${directoryPathsUnix[index]}"
-
- # add directory to zip without junking the path
- zip -q -b /tmp/ -0 -u -r "$zipToSendAbs" "${directoryBaseNamesUnix[index]}"
-
- # cd back to working directory
- cd "$workingDir"
-
- ((index+=1)) # somehow ((index++)) stops the script
- done
- fi
-
- # If directories are included send as zip
- # -> Create additional zip wrapper which will be unzipped by the sending browser
- if [[ "$directoryCount" != 0 ]]; then
- echo "Bundle as ZIP file..."
-
- # Prevent filename from being absolute zip path by "cd"ing to directory before zipping
- zipToSendDirectory=$(dirname "$zipToSendAbs")
- zipToSendBaseName=$(basename "$zipToSendAbs")
-
- cd "$zipToSendDirectory"
-
- [[ -e "$wrapperZipAbs" ]] && echo "Cannot overwrite $wrapperZipAbs. Please remove first." && exit
-
- if [[ $OS == "Windows" ]];then
- powershell.exe -Command "Compress-Archive -Path ${zipToSendBaseName} -DestinationPath ${wrapperZipAbsPS} -CompressionLevel Optimal"
- else
- zip -q -b /tmp/ -0 "$wrapperZipAbs" "$zipToSendBaseName"
- fi
- cd "$workingDir"
-
- # remove inner zip file and set wrapper as zipToSend (do not differentiate between OS as this is done via Git Bash on Windows)
- rm "$zipToSendAbs"
-
- zipToSendAbs=$wrapperZipAbs
- fi
-
- # base64 encode zip file
- if [[ $OS == "Mac" ]];then
- hash=$(base64 -i "$zipToSendAbs")
- else
- hash=$(base64 -w 0 "$zipToSendAbs")
- fi
-
- # remove zip file (do not differentiate between OS as this is done via Git Bash on Windows)
- rm "$zipToSendAbs"
-
- if [[ $(echo -n "$hash" | wc -m) -gt 1000 ]];then
- params="base64zip=paste"
-
- # Copy $hash to clipboard
- if [[ $OS == "Windows" || $OS == "WSL" || $OS == "WSL2" ]];then
- echo -n "$hash" | clip.exe
- elif [[ $OS == "Mac" ]];then
- echo -n "$hash" | pbcopy
- elif [ -n "$WAYLAND_DISPLAY" ]; then
- # Wayland
- if ! command -v wl-copy &> /dev/null; then
- echo -e "You need to install 'wl-copy' to send bigger filePathsUnix from cli"
- echo "Try: sudo apt install wl-clipboard"
- exit 1
- fi
- # Workaround to prevent use of Pipe which has a max letter limit
- echo -n "$hash" > /tmp/pairdrop-cli-temp/pairdrop_hash_temp
- wl-copy < /tmp/pairdrop-cli-temp/pairdrop_hash_temp
- rm /tmp/pairdrop-cli-temp/pairdrop_hash_temp
- else
- # X11
- if ! command -v xclip &> /dev/null; then
- echo -e "You need to install 'xclip' to send bigger filePathsUnix from cli"
- echo "Try: sudo apt install xclip"
- exit 1
- fi
- echo -n "$hash" | xclip -sel c
- fi
- hash=
- fi
-
- openPairDrop
- exit
-}
-
-############################################################
-############################################################
-# Main program #
-############################################################
-############################################################
-script_path="$( cd -- "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )"
-
-pushd . > '/dev/null';
-script_path="${BASH_SOURCE[0]:-$0}";
-
-while [ -h "$script_path" ];
-do
- cd "$( dirname -- "$script_path"; )";
- script_path="$( readlink -f -- "$script_path"; )";
-done
-
-cd "$( dirname -- "$script_path"; )" > '/dev/null';
-script_path="$( pwd; )";
-popd > '/dev/null';
-
-config_path="${script_path}/.pairdrop-cli-config"
-
-# If config file does not exist, try to create it. If it fails log error message and exit
-[ ! -f "$config_path" ] &&
- specifyDomain "https://pairdrop.net/" &&
- [ ! -f "$config_path" ] &&
- echo "Could not create config file. Add 'DOMAIN=https://pairdrop.net/' to a file called .pairdrop-cli-config in the same file as this 'pairdrop' bash file (${script_path})" &&
- exit
-
-# Read config variables
-export "$(grep -v '^#' "$config_path" | xargs)"
-
-setOs
-
-############################################################
-# Process the input options. Add options as needed. #
-############################################################
-# Get the options
-# open PairDrop if no options are given
-[[ $# -eq 0 ]] && openPairDrop && exit
-
-# display help and exit if first argument is "--help" or more than 2 arguments are given
-[ "$1" == "--help" ] && help && exit
-
-while getopts "d:ht:*" option; do
- case $option in
- d) # specify domain - show help and exit if too many arguments
- [[ $# -gt 2 ]] && help && exit
- specifyDomain "$2"
- exit;;
- t) # Send text - show help and exit if too many arguments
- [[ $# -gt 2 ]] && help && exit
- sendText
- exit;;
- h | ?) # display help and exit
- help
- exit;;
- esac
-done
-
-# Send file(s)
-sendFiles "$@"
diff --git a/pairdrop-cli/pairdrop.sh b/pairdrop-cli/pairdrop.sh
deleted file mode 100644
index 17d3672..0000000
--- a/pairdrop-cli/pairdrop.sh
+++ /dev/null
@@ -1,6 +0,0 @@
-#!/bin/bash
-parent_path=$( cd "$(dirname "${BASH_SOURCE[0]}")" || exit ; pwd -P )
-
-cd "$parent_path" || exit
-
-./pairdrop "$@"
\ No newline at end of file
diff --git a/pairdrop-cli/send with PairDrop.lnk b/pairdrop-cli/send with PairDrop.lnk
deleted file mode 100644
index 14d8fa4..0000000
Binary files a/pairdrop-cli/send with PairDrop.lnk and /dev/null differ
diff --git a/pairdrop-cli/send-with-pairdrop b/pairdrop-cli/send-with-pairdrop
deleted file mode 100644
index d99797d..0000000
--- a/pairdrop-cli/send-with-pairdrop
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-
-# Initialize an array
-lines=()
-
-# Read each line into the array
-while IFS= read -r line; do
- lines+=("$line")
-done <<< "$NAUTILUS_SCRIPT_SELECTED_FILE_PATHS"
-
-# Get the length of the array
-length=${#lines[@]}
-
-# Remove the last entry
-unset 'lines[length-1]'
-
-pairdrop "${lines[@]}"
\ No newline at end of file
diff --git a/pairdrop-cli/send-with-pairdrop.ps1 b/pairdrop-cli/send-with-pairdrop.ps1
deleted file mode 100644
index cfb11b0..0000000
--- a/pairdrop-cli/send-with-pairdrop.ps1
+++ /dev/null
@@ -1,3 +0,0 @@
-$scriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
-
-& "$scriptDir\pairdrop.sh" $args
\ No newline at end of file
diff --git a/public/fonts/OpenSans/OFL.txt b/public/fonts/OpenSans/OFL.txt
deleted file mode 100644
index 9b448d4..0000000
--- a/public/fonts/OpenSans/OFL.txt
+++ /dev/null
@@ -1,93 +0,0 @@
-Copyright 2020 The Open Sans Project Authors (https://github.com/googlefonts/opensans)
-
-This Font Software is licensed under the SIL Open Font License, Version 1.1.
-This license is copied below, and is also available with a FAQ at:
-http://scripts.sil.org/OFL
-
-
------------------------------------------------------------
-SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
------------------------------------------------------------
-
-PREAMBLE
-The goals of the Open Font License (OFL) are to stimulate worldwide
-development of collaborative font projects, to support the font creation
-efforts of academic and linguistic communities, and to provide a free and
-open framework in which fonts may be shared and improved in partnership
-with others.
-
-The OFL allows the licensed fonts to be used, studied, modified and
-redistributed freely as long as they are not sold by themselves. The
-fonts, including any derivative works, can be bundled, embedded,
-redistributed and/or sold with any software provided that any reserved
-names are not used by derivative works. The fonts and derivatives,
-however, cannot be released under any other type of license. The
-requirement for fonts to remain under this license does not apply
-to any document created using the fonts or their derivatives.
-
-DEFINITIONS
-"Font Software" refers to the set of files released by the Copyright
-Holder(s) under this license and clearly marked as such. This may
-include source files, build scripts and documentation.
-
-"Reserved Font Name" refers to any names specified as such after the
-copyright statement(s).
-
-"Original Version" refers to the collection of Font Software components as
-distributed by the Copyright Holder(s).
-
-"Modified Version" refers to any derivative made by adding to, deleting,
-or substituting -- in part or in whole -- any of the components of the
-Original Version, by changing formats or by porting the Font Software to a
-new environment.
-
-"Author" refers to any designer, engineer, programmer, technical
-writer or other person who contributed to the Font Software.
-
-PERMISSION & CONDITIONS
-Permission is hereby granted, free of charge, to any person obtaining
-a copy of the Font Software, to use, study, copy, merge, embed, modify,
-redistribute, and sell modified and unmodified copies of the Font
-Software, subject to the following conditions:
-
-1) Neither the Font Software nor any of its individual components,
-in Original or Modified Versions, may be sold by itself.
-
-2) Original or Modified Versions of the Font Software may be bundled,
-redistributed and/or sold with any software, provided that each copy
-contains the above copyright notice and this license. These can be
-included either as stand-alone text files, human-readable headers or
-in the appropriate machine-readable metadata fields within text or
-binary files as long as those fields can be easily viewed by the user.
-
-3) No Modified Version of the Font Software may use the Reserved Font
-Name(s) unless explicit written permission is granted by the corresponding
-Copyright Holder. This restriction only applies to the primary font name as
-presented to the users.
-
-4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
-Software shall not be used to promote, endorse or advertise any
-Modified Version, except to acknowledge the contribution(s) of the
-Copyright Holder(s) and the Author(s) or with their explicit written
-permission.
-
-5) The Font Software, modified or unmodified, in part or in whole,
-must be distributed entirely under this license, and must not be
-distributed under any other license. The requirement for fonts to
-remain under this license does not apply to any document created
-using the Font Software.
-
-TERMINATION
-This license becomes null and void if any of the above conditions are
-not met.
-
-DISCLAIMER
-THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
-MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
-OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
-COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
-INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
-DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
-OTHER DEALINGS IN THE FONT SOFTWARE.
diff --git a/public/fonts/OpenSans/OpenSans-Italic-VariableFont_wdth,wght.ttf b/public/fonts/OpenSans/OpenSans-Italic-VariableFont_wdth,wght.ttf
deleted file mode 100644
index 5bda9cc..0000000
Binary files a/public/fonts/OpenSans/OpenSans-Italic-VariableFont_wdth,wght.ttf and /dev/null differ
diff --git a/public/fonts/OpenSans/OpenSans-VariableFont_wdth,wght.ttf b/public/fonts/OpenSans/OpenSans-VariableFont_wdth,wght.ttf
deleted file mode 100644
index e4142bf..0000000
Binary files a/public/fonts/OpenSans/OpenSans-VariableFont_wdth,wght.ttf and /dev/null differ
diff --git a/public/fonts/OpenSans/README.txt b/public/fonts/OpenSans/README.txt
deleted file mode 100644
index 2548322..0000000
--- a/public/fonts/OpenSans/README.txt
+++ /dev/null
@@ -1,100 +0,0 @@
-Open Sans Variable Font
-=======================
-
-This download contains Open Sans as both variable fonts and static fonts.
-
-Open Sans is a variable font with these axes:
- wdth
- wght
-
-This means all the styles are contained in these files:
- OpenSans-VariableFont_wdth,wght.ttf
- OpenSans-Italic-VariableFont_wdth,wght.ttf
-
-If your app fully supports variable fonts, you can now pick intermediate styles
-that aren’t available as static fonts. Not all apps support variable fonts, and
-in those cases you can use the static font files for Open Sans:
- static/OpenSans_Condensed-Light.ttf
- static/OpenSans_Condensed-Regular.ttf
- static/OpenSans_Condensed-Medium.ttf
- static/OpenSans_Condensed-SemiBold.ttf
- static/OpenSans_Condensed-Bold.ttf
- static/OpenSans_Condensed-ExtraBold.ttf
- static/OpenSans_SemiCondensed-Light.ttf
- static/OpenSans_SemiCondensed-Regular.ttf
- static/OpenSans_SemiCondensed-Medium.ttf
- static/OpenSans_SemiCondensed-SemiBold.ttf
- static/OpenSans_SemiCondensed-Bold.ttf
- static/OpenSans_SemiCondensed-ExtraBold.ttf
- static/OpenSans-Light.ttf
- static/OpenSans-Regular.ttf
- static/OpenSans-Medium.ttf
- static/OpenSans-SemiBold.ttf
- static/OpenSans-Bold.ttf
- static/OpenSans-ExtraBold.ttf
- static/OpenSans_Condensed-LightItalic.ttf
- static/OpenSans_Condensed-Italic.ttf
- static/OpenSans_Condensed-MediumItalic.ttf
- static/OpenSans_Condensed-SemiBoldItalic.ttf
- static/OpenSans_Condensed-BoldItalic.ttf
- static/OpenSans_Condensed-ExtraBoldItalic.ttf
- static/OpenSans_SemiCondensed-LightItalic.ttf
- static/OpenSans_SemiCondensed-Italic.ttf
- static/OpenSans_SemiCondensed-MediumItalic.ttf
- static/OpenSans_SemiCondensed-SemiBoldItalic.ttf
- static/OpenSans_SemiCondensed-BoldItalic.ttf
- static/OpenSans_SemiCondensed-ExtraBoldItalic.ttf
- static/OpenSans-LightItalic.ttf
- static/OpenSans-Italic.ttf
- static/OpenSans-MediumItalic.ttf
- static/OpenSans-SemiBoldItalic.ttf
- static/OpenSans-BoldItalic.ttf
- static/OpenSans-ExtraBoldItalic.ttf
-
-Get started
------------
-
-1. Install the font files you want to use
-
-2. Use your app's font picker to view the font family and all the
-available styles
-
-Learn more about variable fonts
--------------------------------
-
- https://developers.google.com/web/fundamentals/design-and-ux/typography/variable-fonts
- https://variablefonts.typenetwork.com
- https://medium.com/variable-fonts
-
-In desktop apps
-
- https://theblog.adobe.com/can-variable-fonts-illustrator-cc
- https://helpx.adobe.com/nz/photoshop/using/fonts.html#variable_fonts
-
-Online
-
- https://developers.google.com/fonts/docs/getting_started
- https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Fonts/Variable_Fonts_Guide
- https://developer.microsoft.com/en-us/microsoft-edge/testdrive/demos/variable-fonts
-
-Installing fonts
-
- MacOS: https://support.apple.com/en-us/HT201749
- Linux: https://www.google.com/search?q=how+to+install+a+font+on+gnu%2Blinux
- Windows: https://support.microsoft.com/en-us/help/314960/how-to-install-or-remove-a-font-in-windows
-
-Android Apps
-
- https://developers.google.com/fonts/docs/android
- https://developer.android.com/guide/topics/ui/look-and-feel/downloadable-fonts
-
-License
--------
-Please read the full license text (OFL.txt) to understand the permissions,
-restrictions and requirements for usage, redistribution, and modification.
-
-You can use them in your products & projects – print or digital,
-commercial or otherwise.
-
-This isn't legal advice, please consider consulting a lawyer and see the full
-license for all details.
diff --git a/public/fonts/OpenSans/static/OpenSans-Bold.ttf b/public/fonts/OpenSans/static/OpenSans-Bold.ttf
deleted file mode 100644
index 4a5bc39..0000000
Binary files a/public/fonts/OpenSans/static/OpenSans-Bold.ttf and /dev/null differ
diff --git a/public/fonts/OpenSans/static/OpenSans-BoldItalic.ttf b/public/fonts/OpenSans/static/OpenSans-BoldItalic.ttf
deleted file mode 100644
index 8878a3e..0000000
Binary files a/public/fonts/OpenSans/static/OpenSans-BoldItalic.ttf and /dev/null differ
diff --git a/public/fonts/OpenSans/static/OpenSans-ExtraBold.ttf b/public/fonts/OpenSans/static/OpenSans-ExtraBold.ttf
deleted file mode 100644
index 5dfb66c..0000000
Binary files a/public/fonts/OpenSans/static/OpenSans-ExtraBold.ttf and /dev/null differ
diff --git a/public/fonts/OpenSans/static/OpenSans-ExtraBoldItalic.ttf b/public/fonts/OpenSans/static/OpenSans-ExtraBoldItalic.ttf
deleted file mode 100644
index d266998..0000000
Binary files a/public/fonts/OpenSans/static/OpenSans-ExtraBoldItalic.ttf and /dev/null differ
diff --git a/public/fonts/OpenSans/static/OpenSans-Italic.ttf b/public/fonts/OpenSans/static/OpenSans-Italic.ttf
deleted file mode 100644
index e84f9ee..0000000
Binary files a/public/fonts/OpenSans/static/OpenSans-Italic.ttf and /dev/null differ
diff --git a/public/fonts/OpenSans/static/OpenSans-Light.ttf b/public/fonts/OpenSans/static/OpenSans-Light.ttf
deleted file mode 100644
index cf8e0c7..0000000
Binary files a/public/fonts/OpenSans/static/OpenSans-Light.ttf and /dev/null differ
diff --git a/public/fonts/OpenSans/static/OpenSans-LightItalic.ttf b/public/fonts/OpenSans/static/OpenSans-LightItalic.ttf
deleted file mode 100644
index d913f35..0000000
Binary files a/public/fonts/OpenSans/static/OpenSans-LightItalic.ttf and /dev/null differ
diff --git a/public/fonts/OpenSans/static/OpenSans-Medium.ttf b/public/fonts/OpenSans/static/OpenSans-Medium.ttf
deleted file mode 100644
index a76d4ce..0000000
Binary files a/public/fonts/OpenSans/static/OpenSans-Medium.ttf and /dev/null differ
diff --git a/public/fonts/OpenSans/static/OpenSans-MediumItalic.ttf b/public/fonts/OpenSans/static/OpenSans-MediumItalic.ttf
deleted file mode 100644
index 5599691..0000000
Binary files a/public/fonts/OpenSans/static/OpenSans-MediumItalic.ttf and /dev/null differ
diff --git a/public/fonts/OpenSans/static/OpenSans-Regular.ttf b/public/fonts/OpenSans/static/OpenSans-Regular.ttf
deleted file mode 100644
index 29e9e60..0000000
Binary files a/public/fonts/OpenSans/static/OpenSans-Regular.ttf and /dev/null differ
diff --git a/public/fonts/OpenSans/static/OpenSans-SemiBold.ttf b/public/fonts/OpenSans/static/OpenSans-SemiBold.ttf
deleted file mode 100644
index a571167..0000000
Binary files a/public/fonts/OpenSans/static/OpenSans-SemiBold.ttf and /dev/null differ
diff --git a/public/fonts/OpenSans/static/OpenSans-SemiBoldItalic.ttf b/public/fonts/OpenSans/static/OpenSans-SemiBoldItalic.ttf
deleted file mode 100644
index a7d2323..0000000
Binary files a/public/fonts/OpenSans/static/OpenSans-SemiBoldItalic.ttf and /dev/null differ
diff --git a/public/fonts/OpenSans/static/OpenSans_Condensed-Bold.ttf b/public/fonts/OpenSans/static/OpenSans_Condensed-Bold.ttf
deleted file mode 100644
index 90d25e5..0000000
Binary files a/public/fonts/OpenSans/static/OpenSans_Condensed-Bold.ttf and /dev/null differ
diff --git a/public/fonts/OpenSans/static/OpenSans_Condensed-BoldItalic.ttf b/public/fonts/OpenSans/static/OpenSans_Condensed-BoldItalic.ttf
deleted file mode 100644
index 9fefa96..0000000
Binary files a/public/fonts/OpenSans/static/OpenSans_Condensed-BoldItalic.ttf and /dev/null differ
diff --git a/public/fonts/OpenSans/static/OpenSans_Condensed-ExtraBold.ttf b/public/fonts/OpenSans/static/OpenSans_Condensed-ExtraBold.ttf
deleted file mode 100644
index ec9e308..0000000
Binary files a/public/fonts/OpenSans/static/OpenSans_Condensed-ExtraBold.ttf and /dev/null differ
diff --git a/public/fonts/OpenSans/static/OpenSans_Condensed-ExtraBoldItalic.ttf b/public/fonts/OpenSans/static/OpenSans_Condensed-ExtraBoldItalic.ttf
deleted file mode 100644
index f4a2648..0000000
Binary files a/public/fonts/OpenSans/static/OpenSans_Condensed-ExtraBoldItalic.ttf and /dev/null differ
diff --git a/public/fonts/OpenSans/static/OpenSans_Condensed-Italic.ttf b/public/fonts/OpenSans/static/OpenSans_Condensed-Italic.ttf
deleted file mode 100644
index 451059e..0000000
Binary files a/public/fonts/OpenSans/static/OpenSans_Condensed-Italic.ttf and /dev/null differ
diff --git a/public/fonts/OpenSans/static/OpenSans_Condensed-Light.ttf b/public/fonts/OpenSans/static/OpenSans_Condensed-Light.ttf
deleted file mode 100644
index 9823525..0000000
Binary files a/public/fonts/OpenSans/static/OpenSans_Condensed-Light.ttf and /dev/null differ
diff --git a/public/fonts/OpenSans/static/OpenSans_Condensed-LightItalic.ttf b/public/fonts/OpenSans/static/OpenSans_Condensed-LightItalic.ttf
deleted file mode 100644
index d1f9808..0000000
Binary files a/public/fonts/OpenSans/static/OpenSans_Condensed-LightItalic.ttf and /dev/null differ
diff --git a/public/fonts/OpenSans/static/OpenSans_Condensed-Medium.ttf b/public/fonts/OpenSans/static/OpenSans_Condensed-Medium.ttf
deleted file mode 100644
index 50a9f70..0000000
Binary files a/public/fonts/OpenSans/static/OpenSans_Condensed-Medium.ttf and /dev/null differ
diff --git a/public/fonts/OpenSans/static/OpenSans_Condensed-MediumItalic.ttf b/public/fonts/OpenSans/static/OpenSans_Condensed-MediumItalic.ttf
deleted file mode 100644
index e24fdca..0000000
Binary files a/public/fonts/OpenSans/static/OpenSans_Condensed-MediumItalic.ttf and /dev/null differ
diff --git a/public/fonts/OpenSans/static/OpenSans_Condensed-Regular.ttf b/public/fonts/OpenSans/static/OpenSans_Condensed-Regular.ttf
deleted file mode 100644
index 3aa5d46..0000000
Binary files a/public/fonts/OpenSans/static/OpenSans_Condensed-Regular.ttf and /dev/null differ
diff --git a/public/fonts/OpenSans/static/OpenSans_Condensed-SemiBold.ttf b/public/fonts/OpenSans/static/OpenSans_Condensed-SemiBold.ttf
deleted file mode 100644
index 1b98bc4..0000000
Binary files a/public/fonts/OpenSans/static/OpenSans_Condensed-SemiBold.ttf and /dev/null differ
diff --git a/public/fonts/OpenSans/static/OpenSans_Condensed-SemiBoldItalic.ttf b/public/fonts/OpenSans/static/OpenSans_Condensed-SemiBoldItalic.ttf
deleted file mode 100644
index 318828d..0000000
Binary files a/public/fonts/OpenSans/static/OpenSans_Condensed-SemiBoldItalic.ttf and /dev/null differ
diff --git a/public/fonts/OpenSans/static/OpenSans_SemiCondensed-Bold.ttf b/public/fonts/OpenSans/static/OpenSans_SemiCondensed-Bold.ttf
deleted file mode 100644
index dc2168f..0000000
Binary files a/public/fonts/OpenSans/static/OpenSans_SemiCondensed-Bold.ttf and /dev/null differ
diff --git a/public/fonts/OpenSans/static/OpenSans_SemiCondensed-BoldItalic.ttf b/public/fonts/OpenSans/static/OpenSans_SemiCondensed-BoldItalic.ttf
deleted file mode 100644
index 36818ec..0000000
Binary files a/public/fonts/OpenSans/static/OpenSans_SemiCondensed-BoldItalic.ttf and /dev/null differ
diff --git a/public/fonts/OpenSans/static/OpenSans_SemiCondensed-ExtraBold.ttf b/public/fonts/OpenSans/static/OpenSans_SemiCondensed-ExtraBold.ttf
deleted file mode 100644
index 64b8c41..0000000
Binary files a/public/fonts/OpenSans/static/OpenSans_SemiCondensed-ExtraBold.ttf and /dev/null differ
diff --git a/public/fonts/OpenSans/static/OpenSans_SemiCondensed-ExtraBoldItalic.ttf b/public/fonts/OpenSans/static/OpenSans_SemiCondensed-ExtraBoldItalic.ttf
deleted file mode 100644
index 09f3851..0000000
Binary files a/public/fonts/OpenSans/static/OpenSans_SemiCondensed-ExtraBoldItalic.ttf and /dev/null differ
diff --git a/public/fonts/OpenSans/static/OpenSans_SemiCondensed-Italic.ttf b/public/fonts/OpenSans/static/OpenSans_SemiCondensed-Italic.ttf
deleted file mode 100644
index 690ce39..0000000
Binary files a/public/fonts/OpenSans/static/OpenSans_SemiCondensed-Italic.ttf and /dev/null differ
diff --git a/public/fonts/OpenSans/static/OpenSans_SemiCondensed-Light.ttf b/public/fonts/OpenSans/static/OpenSans_SemiCondensed-Light.ttf
deleted file mode 100644
index 443bc12..0000000
Binary files a/public/fonts/OpenSans/static/OpenSans_SemiCondensed-Light.ttf and /dev/null differ
diff --git a/public/fonts/OpenSans/static/OpenSans_SemiCondensed-LightItalic.ttf b/public/fonts/OpenSans/static/OpenSans_SemiCondensed-LightItalic.ttf
deleted file mode 100644
index b804514..0000000
Binary files a/public/fonts/OpenSans/static/OpenSans_SemiCondensed-LightItalic.ttf and /dev/null differ
diff --git a/public/fonts/OpenSans/static/OpenSans_SemiCondensed-Medium.ttf b/public/fonts/OpenSans/static/OpenSans_SemiCondensed-Medium.ttf
deleted file mode 100644
index 8c143cd..0000000
Binary files a/public/fonts/OpenSans/static/OpenSans_SemiCondensed-Medium.ttf and /dev/null differ
diff --git a/public/fonts/OpenSans/static/OpenSans_SemiCondensed-MediumItalic.ttf b/public/fonts/OpenSans/static/OpenSans_SemiCondensed-MediumItalic.ttf
deleted file mode 100644
index d4564c9..0000000
Binary files a/public/fonts/OpenSans/static/OpenSans_SemiCondensed-MediumItalic.ttf and /dev/null differ
diff --git a/public/fonts/OpenSans/static/OpenSans_SemiCondensed-Regular.ttf b/public/fonts/OpenSans/static/OpenSans_SemiCondensed-Regular.ttf
deleted file mode 100644
index 8130446..0000000
Binary files a/public/fonts/OpenSans/static/OpenSans_SemiCondensed-Regular.ttf and /dev/null differ
diff --git a/public/fonts/OpenSans/static/OpenSans_SemiCondensed-SemiBold.ttf b/public/fonts/OpenSans/static/OpenSans_SemiCondensed-SemiBold.ttf
deleted file mode 100644
index 99b6069..0000000
Binary files a/public/fonts/OpenSans/static/OpenSans_SemiCondensed-SemiBold.ttf and /dev/null differ
diff --git a/public/fonts/OpenSans/static/OpenSans_SemiCondensed-SemiBoldItalic.ttf b/public/fonts/OpenSans/static/OpenSans_SemiCondensed-SemiBoldItalic.ttf
deleted file mode 100644
index c89a1cf..0000000
Binary files a/public/fonts/OpenSans/static/OpenSans_SemiCondensed-SemiBoldItalic.ttf and /dev/null differ
diff --git a/public/images/android-chrome-192x192-maskable.png b/public/images/android-chrome-192x192-maskable.png
index 7a70b20..f0e9245 100644
Binary files a/public/images/android-chrome-192x192-maskable.png and b/public/images/android-chrome-192x192-maskable.png differ
diff --git a/public/images/android-chrome-192x192.png b/public/images/android-chrome-192x192.png
index 217e355..0bdca51 100644
Binary files a/public/images/android-chrome-192x192.png and b/public/images/android-chrome-192x192.png differ
diff --git a/public/images/android-chrome-512x512-maskable.png b/public/images/android-chrome-512x512-maskable.png
index b0e9ee5..cdda606 100644
Binary files a/public/images/android-chrome-512x512-maskable.png and b/public/images/android-chrome-512x512-maskable.png differ
diff --git a/public/images/android-chrome-512x512.png b/public/images/android-chrome-512x512.png
index 0dc0647..b01e679 100644
Binary files a/public/images/android-chrome-512x512.png and b/public/images/android-chrome-512x512.png differ
diff --git a/public/images/apple-touch-icon.png b/public/images/apple-touch-icon.png
index 6a96c53..0a32878 100644
Binary files a/public/images/apple-touch-icon.png and b/public/images/apple-touch-icon.png differ
diff --git a/public/images/favicon-96x96-notification.png b/public/images/favicon-96x96-notification.png
index 34b2e51..d3407c3 100644
Binary files a/public/images/favicon-96x96-notification.png and b/public/images/favicon-96x96-notification.png differ
diff --git a/public/images/favicon-96x96.png b/public/images/favicon-96x96.png
index b644235..b8a5746 100644
Binary files a/public/images/favicon-96x96.png and b/public/images/favicon-96x96.png differ
diff --git a/public/images/logo_blue_512x512.png b/public/images/logo_blue_512x512.png
index b0e9ee5..41d13fc 100644
Binary files a/public/images/logo_blue_512x512.png and b/public/images/logo_blue_512x512.png differ
diff --git a/public/images/logo_transparent_128x128.png b/public/images/logo_transparent_128x128.png
new file mode 100644
index 0000000..c276efe
Binary files /dev/null and b/public/images/logo_transparent_128x128.png differ
diff --git a/public/images/logo_transparent_512x512.png b/public/images/logo_transparent_512x512.png
new file mode 100644
index 0000000..367e24f
Binary files /dev/null and b/public/images/logo_transparent_512x512.png differ
diff --git a/public/images/logo_transparent_white_512x512.png b/public/images/logo_transparent_white_512x512.png
index 2c9d046..37589b6 100644
Binary files a/public/images/logo_transparent_white_512x512.png and b/public/images/logo_transparent_white_512x512.png differ
diff --git a/public/images/logo_white_512x512.png b/public/images/logo_white_512x512.png
new file mode 100644
index 0000000..d7750d9
Binary files /dev/null and b/public/images/logo_white_512x512.png differ
diff --git a/public/images/mstile-150x150.png b/public/images/mstile-150x150.png
index 945d874..6380e32 100644
Binary files a/public/images/mstile-150x150.png and b/public/images/mstile-150x150.png differ
diff --git a/public/images/pairdrop_screenshot_mobile_1.png b/public/images/pairdrop_screenshot_mobile_1.png
index 42aae56..d93aafb 100644
Binary files a/public/images/pairdrop_screenshot_mobile_1.png and b/public/images/pairdrop_screenshot_mobile_1.png differ
diff --git a/public/images/pairdrop_screenshot_mobile_2.png b/public/images/pairdrop_screenshot_mobile_2.png
index c472c33..51ace10 100644
Binary files a/public/images/pairdrop_screenshot_mobile_2.png and b/public/images/pairdrop_screenshot_mobile_2.png differ
diff --git a/public/images/pairdrop_screenshot_mobile_3.png b/public/images/pairdrop_screenshot_mobile_3.png
index 0a61028..57ad15a 100644
Binary files a/public/images/pairdrop_screenshot_mobile_3.png and b/public/images/pairdrop_screenshot_mobile_3.png differ
diff --git a/public/images/pairdrop_screenshot_mobile_4.png b/public/images/pairdrop_screenshot_mobile_4.png
index 4b442d5..d5811ad 100644
Binary files a/public/images/pairdrop_screenshot_mobile_4.png and b/public/images/pairdrop_screenshot_mobile_4.png differ
diff --git a/public/images/pairdrop_screenshot_mobile_5.png b/public/images/pairdrop_screenshot_mobile_5.png
index 5d0682f..d205fd9 100644
Binary files a/public/images/pairdrop_screenshot_mobile_5.png and b/public/images/pairdrop_screenshot_mobile_5.png differ
diff --git a/public/images/pairdrop_screenshot_mobile_6.png b/public/images/pairdrop_screenshot_mobile_6.png
index c4a1e1d..23c06ae 100644
Binary files a/public/images/pairdrop_screenshot_mobile_6.png and b/public/images/pairdrop_screenshot_mobile_6.png differ
diff --git a/public/images/pairdrop_screenshot_mobile_7.png b/public/images/pairdrop_screenshot_mobile_7.png
index b1dc8ad..c32980a 100644
Binary files a/public/images/pairdrop_screenshot_mobile_7.png and b/public/images/pairdrop_screenshot_mobile_7.png differ
diff --git a/public/images/pairdrop_screenshot_mobile_8.png b/public/images/pairdrop_screenshot_mobile_8.png
deleted file mode 100644
index 7532227..0000000
Binary files a/public/images/pairdrop_screenshot_mobile_8.png and /dev/null differ
diff --git a/public/index.html b/public/index.html
index 70eda5b..08eed7d 100644
--- a/public/index.html
+++ b/public/index.html
@@ -5,18 +5,17 @@
- PairDrop | Transfer Files Cross-Platform. No Setup, No Signup.
-
+ PairDrop
+
-
-
+
-
+
@@ -29,806 +28,305 @@
-
-
-
-
+
-
-
+
+
-