feat(deploy): add install script and untrack share.config
Package the service for setup on a new machine: - install.sh generates a token, writes share.config (mode 600, token stored as a SHA-256 hash) and installs the systemd user unit from share.service.template - share.config is now gitignored, with share.config.example as the tracked template — the config holds the secret and must not be in the repo - warn at startup when token is empty, since that means open access - SETUP.md documents install, verification and security posture
This commit is contained in:
@@ -1,3 +1,5 @@
|
|||||||
.certs/
|
.certs/
|
||||||
__pycache__/
|
__pycache__/
|
||||||
*.pyc
|
*.pyc
|
||||||
|
share.config
|
||||||
|
share-deploy*.zip
|
||||||
|
|||||||
@@ -4,23 +4,35 @@ Local network file sharing via web page. Drag & drop, Ctrl+V paste, download.
|
|||||||
|
|
||||||
No dependencies — Python 3 stdlib only.
|
No dependencies — Python 3 stdlib only.
|
||||||
|
|
||||||
|
Setting this up on a new machine? See [SETUP.md](SETUP.md).
|
||||||
|
|
||||||
## Quick start
|
## Quick start
|
||||||
|
|
||||||
```
|
```
|
||||||
|
./install.sh # generates a token, installs + starts the systemd service
|
||||||
|
```
|
||||||
|
|
||||||
|
Or run it in the foreground without installing anything:
|
||||||
|
|
||||||
|
```
|
||||||
|
cp share.config.example share.config # then set `token` in it
|
||||||
python3 share.py
|
python3 share.py
|
||||||
```
|
```
|
||||||
|
|
||||||
Open `http://localhost:8888` in a browser. The network address is printed at startup.
|
Open `https://localhost:3001` in a browser. The network address is printed at
|
||||||
|
startup. The cert is self-signed, so expect a browser warning on first visit.
|
||||||
|
|
||||||
## Options
|
## Options
|
||||||
|
|
||||||
|
CLI flags override `share.config`.
|
||||||
|
|
||||||
```
|
```
|
||||||
python3 share.py -p 3001 -d ~/Downloads/shared --ttl 7
|
python3 share.py -p 3001 -d ~/Downloads/shared --ttl 7
|
||||||
```
|
```
|
||||||
|
|
||||||
| Flag | Default | Description |
|
| Flag | Default | Description |
|
||||||
|------|---------|-------------|
|
|------|---------|-------------|
|
||||||
| `-p`, `--port` | `8888` | Port |
|
| `-p`, `--port` | `3001` | Port |
|
||||||
| `-d`, `--dir` | `~/Downloads/shared` | Upload directory |
|
| `-d`, `--dir` | `~/Downloads/shared` | Upload directory |
|
||||||
| `--ttl` | `7` | File lifetime in days (0 = keep forever) |
|
| `--ttl` | `7` | File lifetime in days (0 = keep forever) |
|
||||||
| `--token` | | Auth token (empty = open access) |
|
| `--token` | | Auth token (empty = open access) |
|
||||||
@@ -29,7 +41,11 @@ python3 share.py -p 3001 -d ~/Downloads/shared --ttl 7
|
|||||||
|
|
||||||
## Authentication
|
## Authentication
|
||||||
|
|
||||||
Set `token` in `share.config` or pass `--token`. All endpoints require a valid token.
|
Set `token` in `share.config` or pass `--token`. All endpoints require a valid
|
||||||
|
token. An empty `token` means open access to everyone on the network.
|
||||||
|
|
||||||
|
`share.config` is gitignored because it holds the secret — `share.config.example`
|
||||||
|
is the tracked template.
|
||||||
|
|
||||||
The token can be stored as plain text or as a SHA-256 hash:
|
The token can be stored as plain text or as a SHA-256 hash:
|
||||||
|
|
||||||
@@ -45,7 +61,9 @@ If the value is a 64-character hex string, it is treated as a hash. Otherwise it
|
|||||||
|
|
||||||
## systemd (Arch)
|
## systemd (Arch)
|
||||||
|
|
||||||
Service file: `~/.config/systemd/user/share.service`
|
Installed by `./install.sh` from `share.service.template` to
|
||||||
|
`~/.config/systemd/user/share.service`. Restart after editing `share.config` —
|
||||||
|
it is only read at startup.
|
||||||
|
|
||||||
```
|
```
|
||||||
systemctl --user enable --now share # start + autostart
|
systemctl --user enable --now share # start + autostart
|
||||||
|
|||||||
@@ -0,0 +1,152 @@
|
|||||||
|
# Deploying Share on a new machine
|
||||||
|
|
||||||
|
Instructions for a fresh Claude Code session (or a human) bringing this service up
|
||||||
|
from a clean clone. Read this top to bottom before running anything.
|
||||||
|
|
||||||
|
## What this is
|
||||||
|
|
||||||
|
A LAN file-sharing web app: drag & drop upload, clipboard paste, download, delete,
|
||||||
|
auto-expiry of old files. Python 3 stdlib only — no pip install, no virtualenv.
|
||||||
|
|
||||||
|
It serves **HTTPS with a self-signed certificate**. That is not optional decoration:
|
||||||
|
the browser Clipboard API (`navigator.clipboard.write`) only works on a secure
|
||||||
|
origin, and the paste/copy features depend on it.
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
|
||||||
|
- Python **3.10+** (the code uses `X | None` annotations evaluated at runtime)
|
||||||
|
- `openssl` on PATH (used once, to generate the self-signed cert)
|
||||||
|
- Linux with systemd user units for the service install; without systemd it still
|
||||||
|
runs fine in the foreground
|
||||||
|
|
||||||
|
## Install
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git clone <repo-url> ~/projects/share
|
||||||
|
cd ~/projects/share
|
||||||
|
./install.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
That will:
|
||||||
|
|
||||||
|
1. check python/openssl versions,
|
||||||
|
2. generate a random token, hash it, and write `share.config` (mode 600),
|
||||||
|
3. install `~/.config/systemd/user/share.service` pointing at wherever you cloned,
|
||||||
|
4. `enable --now` the service and confirm it actually came up.
|
||||||
|
|
||||||
|
**The generated token is printed once, at the end. Save it.** Only its SHA-256 hash
|
||||||
|
is stored, so it cannot be recovered afterwards — losing it means re-running with
|
||||||
|
`--force` to set a new one.
|
||||||
|
|
||||||
|
### Setting your own secret
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./install.sh --token 'my-own-secret'
|
||||||
|
```
|
||||||
|
|
||||||
|
Or after the fact, without re-running the installer:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python3 share.py hash 'my-own-secret' # prints the SHA-256 hash
|
||||||
|
# paste that hash as the `token = ` value in share.config
|
||||||
|
systemctl --user restart share
|
||||||
|
```
|
||||||
|
|
||||||
|
Both forms are equivalent — `share.config` never holds the plaintext. A 64-char hex
|
||||||
|
value is treated as an already-hashed token; anything else is hashed at startup
|
||||||
|
(convenient, but then the plaintext sits on disk — prefer the hash).
|
||||||
|
|
||||||
|
### Other install options
|
||||||
|
|
||||||
|
| Flag | Effect |
|
||||||
|
|------|--------|
|
||||||
|
| `--token S` | use secret `S` instead of generating one |
|
||||||
|
| `--port N` | listen port (default `3001`) |
|
||||||
|
| `--dir P` | upload directory (default `~/Downloads/shared`) |
|
||||||
|
| `--ttl N` | file lifetime in days, `0` = keep forever |
|
||||||
|
| `--san IP` | extra IP in the cert SAN; repeatable |
|
||||||
|
| `--lang ru\|en` | UI language |
|
||||||
|
| `--no-service` | write config only, skip systemd |
|
||||||
|
| `--force` | overwrite an existing `share.config` |
|
||||||
|
|
||||||
|
Re-running `install.sh` without `--force` keeps the existing config, so it is safe
|
||||||
|
to run again just to reinstall the unit file.
|
||||||
|
|
||||||
|
## Verify it works
|
||||||
|
|
||||||
|
```bash
|
||||||
|
systemctl --user status share
|
||||||
|
curl -sk -o /dev/null -w '%{http_code}\n' https://localhost:3001/files
|
||||||
|
# → 401 (no token: correct, auth is on)
|
||||||
|
curl -sk -H "Authorization: Bearer YOUR_TOKEN" https://localhost:3001/files
|
||||||
|
# → [] (empty file list: correct)
|
||||||
|
```
|
||||||
|
|
||||||
|
A `401` from the first call is the success signal — it means the token gate is
|
||||||
|
active. A `200` there means `token` is empty in `share.config` and **anyone on the
|
||||||
|
network can read and write your files**.
|
||||||
|
|
||||||
|
Then open `https://<lan-ip>:3001` from a phone or laptop on the same network. The
|
||||||
|
browser will show a certificate warning; that is expected for a self-signed cert —
|
||||||
|
accept it once. Enter the token on the login page; it is stored in an `HttpOnly`
|
||||||
|
cookie for a year.
|
||||||
|
|
||||||
|
## Networking notes
|
||||||
|
|
||||||
|
- The server binds `0.0.0.0`, so it is reachable from the whole LAN.
|
||||||
|
- The cert's SAN list gets `127.0.0.1` plus the auto-detected LAN IP. If you reach
|
||||||
|
the box by another address (a router doing NAT, a second interface, a static IP
|
||||||
|
that differs from the detected one), pass it with `--san` or the cert will not
|
||||||
|
validate for that address.
|
||||||
|
- Changing SANs later: delete `.certs/` and restart — the cert regenerates.
|
||||||
|
- Open the port in the firewall if one is active (`ufw allow 3001/tcp`, or the
|
||||||
|
`firewalld`/`iptables` equivalent).
|
||||||
|
- The service starts on **login**, not boot. For a headless box that should serve
|
||||||
|
without anyone logging in: `sudo loginctl enable-linger $USER`.
|
||||||
|
|
||||||
|
## Managing the service
|
||||||
|
|
||||||
|
```bash
|
||||||
|
systemctl --user restart share # after editing share.config
|
||||||
|
systemctl --user stop share
|
||||||
|
systemctl --user disable --now share
|
||||||
|
journalctl --user -u share -f # live logs
|
||||||
|
```
|
||||||
|
|
||||||
|
`share.config` is read only at startup — every config change needs a restart.
|
||||||
|
|
||||||
|
## Running without systemd
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python3 share.py # foreground, reads share.config
|
||||||
|
python3 share.py -p 4000 --ttl 0 # CLI flags override share.config
|
||||||
|
```
|
||||||
|
|
||||||
|
## Security posture — know what this is
|
||||||
|
|
||||||
|
This is a LAN convenience tool, not a hardened public service. Deploy accordingly:
|
||||||
|
|
||||||
|
- The token is a single shared bearer secret. Everyone who has it has full
|
||||||
|
read/write/delete access to the upload directory. There are no user accounts.
|
||||||
|
- An empty `token` disables auth completely. Never leave it empty.
|
||||||
|
- The cert is self-signed, so clients cannot distinguish it from a
|
||||||
|
man-in-the-middle cert on first use. Fine on a home LAN, not fine on a hostile
|
||||||
|
network.
|
||||||
|
- Do not port-forward this to the internet. It is designed for a trusted LAN.
|
||||||
|
- `share.config` holds the secret hash and is gitignored — keep it that way. Copy
|
||||||
|
it between machines out of band, never through the repo.
|
||||||
|
|
||||||
|
## Files
|
||||||
|
|
||||||
|
| Path | Role |
|
||||||
|
|------|------|
|
||||||
|
| `share.py` | entry point, startup wiring, `hash` subcommand |
|
||||||
|
| `server.py` | HTTP handler, routing, auth, upload/download |
|
||||||
|
| `config.py` | config file + CLI arg loading, constants |
|
||||||
|
| `cert.py` | self-signed cert generation, LAN IP detection |
|
||||||
|
| `cleanup.py` | background thread expiring files past `ttl` |
|
||||||
|
| `static/index.html` | the entire frontend, single file |
|
||||||
|
| `share.config.example` | tracked template — copy to `share.config` |
|
||||||
|
| `share.config` | **gitignored**, holds the token hash |
|
||||||
|
| `share.service.template` | tracked systemd unit template |
|
||||||
|
| `install.sh` | the setup script described above |
|
||||||
Executable
+146
@@ -0,0 +1,146 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Share — setup on a new machine.
|
||||||
|
#
|
||||||
|
# ./install.sh generate a random token, install + start service
|
||||||
|
# ./install.sh --token mysecret use your own token
|
||||||
|
# ./install.sh --port 4000 --dir ~/box override port / upload dir
|
||||||
|
# ./install.sh --san 192.168.0.1 extra IP in the TLS cert SAN (repeatable)
|
||||||
|
# ./install.sh --no-service write config only, don't touch systemd
|
||||||
|
#
|
||||||
|
# Re-running is safe: an existing share.config is kept unless you pass --force.
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
CONFIG="$DIR/share.config"
|
||||||
|
UNIT_DIR="$HOME/.config/systemd/user"
|
||||||
|
|
||||||
|
TOKEN="" PORT="" UPLOAD_DIR="" LANG_OPT="" TTL=""
|
||||||
|
SANS=() SERVICE=1 FORCE=0
|
||||||
|
|
||||||
|
die() { printf '\033[31merror:\033[0m %s\n' "$1" >&2; exit 1; }
|
||||||
|
info() { printf ' %s\n' "$1"; }
|
||||||
|
|
||||||
|
while [[ $# -gt 0 ]]; do
|
||||||
|
case "$1" in
|
||||||
|
--token) TOKEN="${2:-}"; shift 2 ;;
|
||||||
|
--port) PORT="${2:-}"; shift 2 ;;
|
||||||
|
--dir) UPLOAD_DIR="${2:-}"; shift 2 ;;
|
||||||
|
--ttl) TTL="${2:-}"; shift 2 ;;
|
||||||
|
--lang) LANG_OPT="${2:-}"; shift 2 ;;
|
||||||
|
--san) SANS+=("${2:-}"); shift 2 ;;
|
||||||
|
--no-service) SERVICE=0; shift ;;
|
||||||
|
--force) FORCE=1; shift ;;
|
||||||
|
-h|--help) sed -n '2,10p' "${BASH_SOURCE[0]}" | sed 's/^# \?//'; exit 0 ;;
|
||||||
|
*) die "unknown option: $1" ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
# ── Dependencies ──────────────────────────────────────────────────────────────
|
||||||
|
PYTHON="$(command -v python3)" || die "python3 not found"
|
||||||
|
"$PYTHON" -c 'import sys; sys.exit(0 if sys.version_info >= (3, 10) else 1)' \
|
||||||
|
|| die "python3 >= 3.10 required (found $("$PYTHON" -V 2>&1))"
|
||||||
|
command -v openssl >/dev/null || die "openssl not found — needed to generate the TLS cert"
|
||||||
|
|
||||||
|
# ── Token ─────────────────────────────────────────────────────────────────────
|
||||||
|
GENERATED=0
|
||||||
|
if [[ -z "$TOKEN" ]]; then
|
||||||
|
TOKEN="$("$PYTHON" -c 'import secrets; print(secrets.token_urlsafe(24))')"
|
||||||
|
GENERATED=1
|
||||||
|
fi
|
||||||
|
TOKEN_HASH="$("$PYTHON" -c 'import hashlib,sys; print(hashlib.sha256(sys.argv[1].encode()).hexdigest())' "$TOKEN")"
|
||||||
|
|
||||||
|
# ── Config ────────────────────────────────────────────────────────────────────
|
||||||
|
KEPT=0
|
||||||
|
if [[ -f "$CONFIG" && $FORCE -eq 0 ]]; then
|
||||||
|
KEPT=1
|
||||||
|
info "share.config exists — keeping it (pass --force to overwrite)"
|
||||||
|
else
|
||||||
|
[[ -f "$DIR/share.config.example" ]] || die "share.config.example missing"
|
||||||
|
cp "$DIR/share.config.example" "$CONFIG"
|
||||||
|
|
||||||
|
set_opt() { # set_opt <key> <value> — replace the first bare "key =" line
|
||||||
|
local key="$1" val="$2"
|
||||||
|
"$PYTHON" - "$CONFIG" "$key" "$val" <<'PY'
|
||||||
|
import re, sys
|
||||||
|
path, key, val = sys.argv[1], sys.argv[2], sys.argv[3]
|
||||||
|
text = open(path).read()
|
||||||
|
text, n = re.subn(rf'(?m)^{re.escape(key)}\s*=.*$', f'{key} = {val}', text, count=1)
|
||||||
|
if n == 0:
|
||||||
|
raise SystemExit(f'key not found in config template: {key}')
|
||||||
|
open(path, 'w').write(text)
|
||||||
|
PY
|
||||||
|
}
|
||||||
|
|
||||||
|
set_opt token "$TOKEN_HASH"
|
||||||
|
[[ -n "$PORT" ]] && set_opt port "$PORT"
|
||||||
|
[[ -n "$UPLOAD_DIR" ]] && set_opt dir "$UPLOAD_DIR"
|
||||||
|
[[ -n "$TTL" ]] && set_opt ttl "$TTL"
|
||||||
|
[[ -n "$LANG_OPT" ]] && set_opt lang "$LANG_OPT"
|
||||||
|
[[ ${#SANS[@]} -gt 0 ]] && set_opt san "${SANS[*]}"
|
||||||
|
chmod 600 "$CONFIG"
|
||||||
|
info "wrote share.config (token stored as SHA-256 hash)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
EFF_PORT="$("$PYTHON" -c '
|
||||||
|
import configparser, sys
|
||||||
|
cp = configparser.ConfigParser(); cp.read(sys.argv[1]); print(cp.get("server", "port", fallback="3001"))' "$CONFIG")"
|
||||||
|
|
||||||
|
# ── systemd user service ──────────────────────────────────────────────────────
|
||||||
|
if [[ $SERVICE -eq 1 ]]; then
|
||||||
|
if ! command -v systemctl >/dev/null; then
|
||||||
|
info "systemctl not found — skipping service install"
|
||||||
|
SERVICE=0
|
||||||
|
else
|
||||||
|
mkdir -p "$UNIT_DIR"
|
||||||
|
sed -e "s|__DIR__|$DIR|g" -e "s|__PYTHON__|$PYTHON|g" \
|
||||||
|
-e '/^;;/d' "$DIR/share.service.template" > "$UNIT_DIR/share.service"
|
||||||
|
systemctl --user daemon-reload
|
||||||
|
systemctl --user enable share.service
|
||||||
|
# restart, not `enable --now`: --now is a no-op on an already-running service,
|
||||||
|
# so a re-run would leave the old unit/config in effect.
|
||||||
|
systemctl --user restart share.service
|
||||||
|
info "installed $UNIT_DIR/share.service and (re)started it"
|
||||||
|
# Give it a moment to bind the port / fail loudly.
|
||||||
|
for _ in 1 2 3 4 5 6 7 8 9 10; do
|
||||||
|
systemctl --user is-active --quiet share.service && break
|
||||||
|
sleep 0.3
|
||||||
|
done
|
||||||
|
if ! systemctl --user is-active --quiet share.service; then
|
||||||
|
systemctl --user status share.service --no-pager -n 20 || true
|
||||||
|
die "service failed to start — see the status output above"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ── Summary ───────────────────────────────────────────────────────────────────
|
||||||
|
IP="$("$PYTHON" -c '
|
||||||
|
import socket
|
||||||
|
try:
|
||||||
|
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM); s.connect(("8.8.8.8", 80))
|
||||||
|
print(s.getsockname()[0]); s.close()
|
||||||
|
except OSError:
|
||||||
|
print("127.0.0.1")')"
|
||||||
|
|
||||||
|
echo
|
||||||
|
printf '\033[32m Share is set up.\033[0m\n\n'
|
||||||
|
info "Local: https://localhost:$EFF_PORT"
|
||||||
|
info "Network: https://$IP:$EFF_PORT"
|
||||||
|
echo
|
||||||
|
if [[ $KEPT -eq 1 ]]; then
|
||||||
|
info "Token: unchanged — the existing share.config was kept."
|
||||||
|
info " To set a new one: ./install.sh --force --token 'newsecret'"
|
||||||
|
echo
|
||||||
|
elif [[ $GENERATED -eq 1 ]]; then
|
||||||
|
printf '\033[33m Token (generated — save it now, it is not stored in plaintext):\033[0m\n\n'
|
||||||
|
printf ' %s\n\n' "$TOKEN"
|
||||||
|
else
|
||||||
|
info "Token: the one you passed via --token"
|
||||||
|
echo
|
||||||
|
fi
|
||||||
|
info "The cert is self-signed — the browser will warn on first visit. That is expected."
|
||||||
|
if [[ $SERVICE -eq 1 ]]; then
|
||||||
|
info "Logs: journalctl --user -u share -f"
|
||||||
|
info "Autostart at boot without login: sudo loginctl enable-linger $USER"
|
||||||
|
else
|
||||||
|
info "Run manually: python3 $DIR/share.py"
|
||||||
|
fi
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
[server]
|
|
||||||
port = 3001
|
|
||||||
dir = ~/Downloads/shared
|
|
||||||
ttl = 7
|
|
||||||
san =
|
|
||||||
lang = ru
|
|
||||||
refresh = 5
|
|
||||||
token = 755b7c9ca04986c0dd496353e9d94ce713f2df0657b9b4008cb4d617679e103f
|
|
||||||
per_page = 10,25,50,100
|
|
||||||
|
|
||||||
[internal]
|
|
||||||
secs_per_day = 86400
|
|
||||||
cleanup_interval = 3600
|
|
||||||
ssl_handshake_timeout = 5
|
|
||||||
sniff_size = 8192
|
|
||||||
chunk_size = 65536
|
|
||||||
cert_days = 3650
|
|
||||||
cert_key_bits = 2048
|
|
||||||
toast_ms = 2500
|
|
||||||
progress_hide_ms = 1200
|
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
; Copy to share.config and edit. share.config is gitignored — it holds the secret.
|
||||||
|
; cp share.config.example share.config
|
||||||
|
; Or let install.sh do it for you.
|
||||||
|
|
||||||
|
[server]
|
||||||
|
; Port to listen on (all interfaces, 0.0.0.0)
|
||||||
|
port = 3001
|
||||||
|
|
||||||
|
; Upload directory — created at startup if missing
|
||||||
|
dir = ~/Downloads/shared
|
||||||
|
|
||||||
|
; File lifetime in days (0 = keep forever)
|
||||||
|
ttl = 7
|
||||||
|
|
||||||
|
; Extra IPs to put in the TLS cert SAN, space-separated.
|
||||||
|
; 127.0.0.1 and the detected LAN IP are always included.
|
||||||
|
; Add the router IP here if you reach the box through it, e.g. 192.168.0.1
|
||||||
|
san =
|
||||||
|
|
||||||
|
; UI language: ru | en
|
||||||
|
lang = ru
|
||||||
|
|
||||||
|
; File list auto-refresh interval, seconds
|
||||||
|
refresh = 5
|
||||||
|
|
||||||
|
; Auth token. REQUIRED — an empty value means open access to anyone on the LAN.
|
||||||
|
; Store the SHA-256 hash, not the plaintext:
|
||||||
|
; python3 share.py hash mysecret
|
||||||
|
; A 64-char hex value is treated as a hash; anything else is hashed at startup.
|
||||||
|
token =
|
||||||
|
|
||||||
|
; Page-size options offered in the UI
|
||||||
|
per_page = 10,25,50,100
|
||||||
|
|
||||||
|
; Explicit TLS cert/key. Leave empty to auto-generate a self-signed pair in .certs/
|
||||||
|
cert =
|
||||||
|
key =
|
||||||
|
|
||||||
|
; Values below are implementation constants — no need to touch them.
|
||||||
|
[internal]
|
||||||
|
secs_per_day = 86400
|
||||||
|
cleanup_interval = 3600
|
||||||
|
ssl_handshake_timeout = 5
|
||||||
|
sniff_size = 8192
|
||||||
|
chunk_size = 65536
|
||||||
|
cert_days = 3650
|
||||||
|
cert_key_bits = 2048
|
||||||
|
toast_ms = 2500
|
||||||
|
progress_hide_ms = 1200
|
||||||
@@ -45,6 +45,9 @@ def main():
|
|||||||
ctx.load_cert_chain(cert, key)
|
ctx.load_cert_chain(cert, key)
|
||||||
|
|
||||||
token = conf["token"]
|
token = conf["token"]
|
||||||
|
if not token:
|
||||||
|
print("\n WARNING: no token set — anyone on the network has full access.")
|
||||||
|
print(" Set `token` in share.config (see SETUP.md) or pass --token.")
|
||||||
# If token looks like a SHA-256 hash, pass it as pre-hashed
|
# If token looks like a SHA-256 hash, pass it as pre-hashed
|
||||||
is_hash = len(token) == 64 and all(c in "0123456789abcdef" for c in token)
|
is_hash = len(token) == 64 and all(c in "0123456789abcdef" for c in token)
|
||||||
server = ShareServer(("0.0.0.0", port), Handler, upload_dir=upload_dir, html=html, ssl_ctx=ctx, token=token, token_is_hash=is_hash)
|
server = ShareServer(("0.0.0.0", port), Handler, upload_dir=upload_dir, html=html, ssl_ctx=ctx, token=token, token_is_hash=is_hash)
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
;; Template — install.sh substitutes __DIR__ and __PYTHON__ and installs the
|
||||||
|
;; result to ~/.config/systemd/user/share.service. Do not install this file directly.
|
||||||
|
[Unit]
|
||||||
|
Description=Share — local file sharing
|
||||||
|
After=network-online.target
|
||||||
|
Wants=network-online.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
; Unbuffered — otherwise Python block-buffers stdout when it is not a tty and the
|
||||||
|
; startup banner / warnings never reach journalctl.
|
||||||
|
Environment=PYTHONUNBUFFERED=1
|
||||||
|
ExecStart=__PYTHON__ __DIR__/share.py
|
||||||
|
WorkingDirectory=__DIR__
|
||||||
|
Restart=on-failure
|
||||||
|
RestartSec=3
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=default.target
|
||||||
Reference in New Issue
Block a user