Extract all magic numbers into share.config
Unified config file with [server] (user-facing: port, dir, ttl, lang, etc.) and [internal] (constants: timeouts, buffer sizes, cert params, UI timings). Replaces both DEFAULTS dict and hardcoded constants. JS constants (toast_ms, progress_hide_ms) passed through CFG injection. Old share.conf no longer used. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,19 @@
|
||||
[server]
|
||||
port = 3001
|
||||
dir = ~/Downloads/shared
|
||||
ttl = 7
|
||||
san =
|
||||
lang = ru
|
||||
refresh = 5
|
||||
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
|
||||
@@ -24,7 +24,7 @@ def cleanup_loop(upload_dir: Path, max_age: int):
|
||||
if max_age <= 0:
|
||||
return
|
||||
while True:
|
||||
time.sleep(3600)
|
||||
time.sleep(CLEANUP_INTERVAL)
|
||||
now = time.time()
|
||||
try:
|
||||
entries = list(upload_dir.iterdir())
|
||||
@@ -68,9 +68,9 @@ def ensure_cert(cert_dir: Path, extra_ips: list[str] | None = None) -> tuple[Pat
|
||||
san = ",".join([f"IP:{ip}" for ip in sorted(ips)] + ["DNS:localhost"])
|
||||
|
||||
subprocess.run([
|
||||
"openssl", "req", "-x509", "-newkey", "rsa:2048",
|
||||
"openssl", "req", "-x509", "-newkey", f"rsa:{CERT_KEY_BITS}",
|
||||
"-keyout", str(key), "-out", str(cert),
|
||||
"-days", "3650", "-nodes",
|
||||
"-days", str(CERT_DAYS), "-nodes",
|
||||
"-subj", "/CN=Share",
|
||||
"-addext", f"subjectAltName={san}",
|
||||
], check=True, capture_output=True)
|
||||
@@ -80,6 +80,32 @@ def ensure_cert(cert_dir: Path, extra_ips: list[str] | None = None) -> tuple[Pat
|
||||
|
||||
BASE_DIR = Path(__file__).resolve().parent
|
||||
|
||||
# ── Config ──
|
||||
|
||||
_cp = configparser.ConfigParser()
|
||||
_cp.read_dict({
|
||||
"server": {
|
||||
"port": "3001", "dir": str(Path.home() / "Downloads" / "shared"),
|
||||
"ttl": "7", "san": "", "lang": "ru", "refresh": "5",
|
||||
"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",
|
||||
},
|
||||
})
|
||||
_cp.read(BASE_DIR / "share.config")
|
||||
|
||||
SECS_PER_DAY = _cp.getint("internal", "secs_per_day")
|
||||
CLEANUP_INTERVAL = _cp.getint("internal", "cleanup_interval")
|
||||
SSL_HANDSHAKE_TIMEOUT = _cp.getint("internal", "ssl_handshake_timeout")
|
||||
SNIFF_SIZE = _cp.getint("internal", "sniff_size")
|
||||
CHUNK_SIZE = _cp.getint("internal", "chunk_size")
|
||||
CERT_DAYS = _cp.getint("internal", "cert_days")
|
||||
CERT_KEY_BITS = _cp.getint("internal", "cert_key_bits")
|
||||
|
||||
def _file_type(path: Path) -> str:
|
||||
ct = mimetypes.guess_type(path.name)[0] or ''
|
||||
if ct.startswith('image/'):
|
||||
@@ -88,7 +114,7 @@ def _file_type(path: Path) -> str:
|
||||
return 'text'
|
||||
try:
|
||||
with path.open('rb') as f:
|
||||
return 'text' if b'\x00' not in f.read(8192) else 'other'
|
||||
return 'text' if b'\x00' not in f.read(SNIFF_SIZE) else 'other'
|
||||
except OSError:
|
||||
return 'other'
|
||||
|
||||
@@ -106,7 +132,7 @@ class ShareServer(http.server.ThreadingHTTPServer):
|
||||
def get_request(self):
|
||||
client, addr = self.socket.accept()
|
||||
if self.ssl_ctx:
|
||||
client.settimeout(5)
|
||||
client.settimeout(SSL_HANDSHAKE_TIMEOUT)
|
||||
try:
|
||||
client = self.ssl_ctx.wrap_socket(client, server_side=True)
|
||||
except (ssl.SSLError, OSError):
|
||||
@@ -195,7 +221,7 @@ class Handler(http.server.BaseHTTPRequestHandler):
|
||||
self.send_header("Connection", "close")
|
||||
self.end_headers()
|
||||
with safe.open("rb") as f:
|
||||
while chunk := f.read(65536):
|
||||
while chunk := f.read(CHUNK_SIZE):
|
||||
self.wfile.write(chunk)
|
||||
|
||||
def _route_upload(self, _prefix):
|
||||
@@ -252,24 +278,9 @@ class Handler(http.server.BaseHTTPRequestHandler):
|
||||
# ── Main ──
|
||||
|
||||
|
||||
DEFAULTS = {
|
||||
"port": "3001",
|
||||
"dir": str(Path.home() / "Downloads" / "shared"),
|
||||
"ttl": "7",
|
||||
"san": "",
|
||||
"lang": "ru",
|
||||
"refresh": "5",
|
||||
"per_page": "10,25,50,100",
|
||||
}
|
||||
|
||||
|
||||
def load_config() -> dict:
|
||||
"""Load config: defaults ← config file ← CLI args."""
|
||||
cfg_path = BASE_DIR / "share.conf"
|
||||
cp = configparser.ConfigParser()
|
||||
cp.read_dict({"share": DEFAULTS})
|
||||
cp.read(cfg_path)
|
||||
conf = dict(cp["share"])
|
||||
"""Load config: share.config defaults ← CLI args."""
|
||||
conf = dict(_cp["server"])
|
||||
|
||||
p = argparse.ArgumentParser(description="Share — local file sharing server")
|
||||
p.add_argument("-p", "--port", type=int)
|
||||
@@ -292,7 +303,7 @@ def main():
|
||||
|
||||
port = int(conf["port"])
|
||||
upload_dir = Path(conf["dir"]).expanduser().resolve()
|
||||
max_age = int(conf["ttl"]) * 86400
|
||||
max_age = int(conf["ttl"]) * SECS_PER_DAY
|
||||
san_list = conf["san"].split() if conf["san"] else None
|
||||
|
||||
upload_dir.mkdir(parents=True, exist_ok=True)
|
||||
@@ -304,6 +315,8 @@ def main():
|
||||
"lang": conf["lang"],
|
||||
"refresh": int(conf["refresh"]),
|
||||
"perPage": [int(x) for x in conf["per_page"].split(",")],
|
||||
"toastMs": _cp.getint("internal", "toast_ms"),
|
||||
"progressHideMs": _cp.getint("internal", "progress_hide_ms"),
|
||||
})
|
||||
cfg_tag = f"<script>const CFG={cfg_json}</script>".encode()
|
||||
html = (BASE_DIR / "static" / "index.html").read_bytes()
|
||||
|
||||
+4
-2
@@ -194,6 +194,8 @@ const _cfg = typeof CFG !== 'undefined' ? CFG : {};
|
||||
const defaultLang = _cfg.lang || 'ru';
|
||||
const REFRESH_MS = (_cfg.refresh || 5) * 1000;
|
||||
const PER_PAGE_OPTS = _cfg.perPage || [10, 25, 50, 100];
|
||||
const TOAST_MS = _cfg.toastMs || 2500;
|
||||
const PROGRESS_HIDE_MS = _cfg.progressHideMs || 1200;
|
||||
|
||||
/* ── i18n ── */
|
||||
|
||||
@@ -266,7 +268,7 @@ function toast(msg) {
|
||||
toastEl.textContent = msg;
|
||||
toastEl.classList.add('show');
|
||||
clearTimeout(toastTimer);
|
||||
toastTimer = setTimeout(() => toastEl.classList.remove('show'), 2500);
|
||||
toastTimer = setTimeout(() => toastEl.classList.remove('show'), TOAST_MS);
|
||||
}
|
||||
|
||||
function fmtSize(b) {
|
||||
@@ -371,7 +373,7 @@ function upload(fileList) {
|
||||
xhr.onload = () => {
|
||||
done();
|
||||
pfill.style.width = '100%';
|
||||
setTimeout(() => { $('progress').style.display = 'none'; pfill.style.width = '0' }, 1200);
|
||||
setTimeout(() => { $('progress').style.display = 'none'; pfill.style.width = '0' }, PROGRESS_HIDE_MS);
|
||||
if (xhr.status === 200) {
|
||||
const r = JSON.parse(xhr.responseText);
|
||||
toast(t().uploaded(r.count));
|
||||
|
||||
Reference in New Issue
Block a user