mirror of
https://github.com/9001/copyparty.git
synced 2025-08-17 09:02:15 -06:00
list active downloads in controlpanel
This commit is contained in:
parent
0ce7cf5e10
commit
8aba5aed4f
|
@ -1687,6 +1687,7 @@ scrape_configs:
|
||||||
currently the following metrics are available,
|
currently the following metrics are available,
|
||||||
* `cpp_uptime_seconds` time since last copyparty restart
|
* `cpp_uptime_seconds` time since last copyparty restart
|
||||||
* `cpp_boot_unixtime_seconds` same but as an absolute timestamp
|
* `cpp_boot_unixtime_seconds` same but as an absolute timestamp
|
||||||
|
* `cpp_active_dl` number of active downloads
|
||||||
* `cpp_http_conns` number of open http(s) connections
|
* `cpp_http_conns` number of open http(s) connections
|
||||||
* `cpp_http_reqs` number of http(s) requests handled
|
* `cpp_http_reqs` number of http(s) requests handled
|
||||||
* `cpp_sus_reqs` number of 403/422/malicious requests
|
* `cpp_sus_reqs` number of 403/422/malicious requests
|
||||||
|
|
|
@ -80,6 +80,7 @@ web/deps/prismd.css
|
||||||
web/deps/scp.woff2
|
web/deps/scp.woff2
|
||||||
web/deps/sha512.ac.js
|
web/deps/sha512.ac.js
|
||||||
web/deps/sha512.hw.js
|
web/deps/sha512.hw.js
|
||||||
|
web/iiam.gif
|
||||||
web/md.css
|
web/md.css
|
||||||
web/md.html
|
web/md.html
|
||||||
web/md.js
|
web/md.js
|
||||||
|
|
|
@ -1321,6 +1321,7 @@ def add_admin(ap):
|
||||||
ap2.add_argument("--no-reload", action="store_true", help="disable ?reload=cfg (reload users/volumes/volflags from config file)")
|
ap2.add_argument("--no-reload", action="store_true", help="disable ?reload=cfg (reload users/volumes/volflags from config file)")
|
||||||
ap2.add_argument("--no-rescan", action="store_true", help="disable ?scan (volume reindexing)")
|
ap2.add_argument("--no-rescan", action="store_true", help="disable ?scan (volume reindexing)")
|
||||||
ap2.add_argument("--no-stack", action="store_true", help="disable ?stack (list all stacks)")
|
ap2.add_argument("--no-stack", action="store_true", help="disable ?stack (list all stacks)")
|
||||||
|
ap2.add_argument("--dl-list", metavar="LVL", type=int, default=2, help="who can see active downloads in the controlpanel? [\033[32m0\033[0m]=nobody, [\033[32m1\033[0m]=admins, [\033[32m2\033[0m]=everyone")
|
||||||
|
|
||||||
|
|
||||||
def add_thumbnail(ap):
|
def add_thumbnail(ap):
|
||||||
|
|
|
@ -43,6 +43,9 @@ class BrokerMp(object):
|
||||||
self.procs = []
|
self.procs = []
|
||||||
self.mutex = threading.Lock()
|
self.mutex = threading.Lock()
|
||||||
|
|
||||||
|
self.retpend: dict[int, Any] = {}
|
||||||
|
self.retpend_mutex = threading.Lock()
|
||||||
|
|
||||||
self.num_workers = self.args.j or CORES
|
self.num_workers = self.args.j or CORES
|
||||||
self.log("broker", "booting {} subprocesses".format(self.num_workers))
|
self.log("broker", "booting {} subprocesses".format(self.num_workers))
|
||||||
for n in range(1, self.num_workers + 1):
|
for n in range(1, self.num_workers + 1):
|
||||||
|
@ -54,6 +57,8 @@ class BrokerMp(object):
|
||||||
self.procs.append(proc)
|
self.procs.append(proc)
|
||||||
proc.start()
|
proc.start()
|
||||||
|
|
||||||
|
Daemon(self.periodic, "mp-periodic")
|
||||||
|
|
||||||
def shutdown(self) -> None:
|
def shutdown(self) -> None:
|
||||||
self.log("broker", "shutting down")
|
self.log("broker", "shutting down")
|
||||||
for n, proc in enumerate(self.procs):
|
for n, proc in enumerate(self.procs):
|
||||||
|
@ -90,8 +95,10 @@ class BrokerMp(object):
|
||||||
self.log(*args)
|
self.log(*args)
|
||||||
|
|
||||||
elif dest == "retq":
|
elif dest == "retq":
|
||||||
# response from previous ipc call
|
with self.retpend_mutex:
|
||||||
raise Exception("invalid broker_mp usage")
|
retq = self.retpend.pop(retq_id)
|
||||||
|
|
||||||
|
retq.put(args[0])
|
||||||
|
|
||||||
else:
|
else:
|
||||||
# new ipc invoking managed service in hub
|
# new ipc invoking managed service in hub
|
||||||
|
@ -109,7 +116,6 @@ class BrokerMp(object):
|
||||||
proc.q_pend.put((retq_id, "retq", rv))
|
proc.q_pend.put((retq_id, "retq", rv))
|
||||||
|
|
||||||
def ask(self, dest: str, *args: Any) -> Union[ExceptionalQueue, NotExQueue]:
|
def ask(self, dest: str, *args: Any) -> Union[ExceptionalQueue, NotExQueue]:
|
||||||
|
|
||||||
# new non-ipc invoking managed service in hub
|
# new non-ipc invoking managed service in hub
|
||||||
obj = self.hub
|
obj = self.hub
|
||||||
for node in dest.split("."):
|
for node in dest.split("."):
|
||||||
|
@ -121,17 +127,30 @@ class BrokerMp(object):
|
||||||
retq.put(rv)
|
retq.put(rv)
|
||||||
return retq
|
return retq
|
||||||
|
|
||||||
|
def wask(self, dest: str, *args: Any) -> list[Union[ExceptionalQueue, NotExQueue]]:
|
||||||
|
# call from hub to workers
|
||||||
|
ret = []
|
||||||
|
for p in self.procs:
|
||||||
|
retq = ExceptionalQueue(1)
|
||||||
|
retq_id = id(retq)
|
||||||
|
with self.retpend_mutex:
|
||||||
|
self.retpend[retq_id] = retq
|
||||||
|
|
||||||
|
p.q_pend.put((retq_id, dest, list(args)))
|
||||||
|
ret.append(retq)
|
||||||
|
return ret
|
||||||
|
|
||||||
def say(self, dest: str, *args: Any) -> None:
|
def say(self, dest: str, *args: Any) -> None:
|
||||||
"""
|
"""
|
||||||
send message to non-hub component in other process,
|
send message to non-hub component in other process,
|
||||||
returns a Queue object which eventually contains the response if want_retval
|
returns a Queue object which eventually contains the response if want_retval
|
||||||
(not-impl here since nothing uses it yet)
|
(not-impl here since nothing uses it yet)
|
||||||
"""
|
"""
|
||||||
if dest == "listen":
|
if dest == "httpsrv.listen":
|
||||||
for p in self.procs:
|
for p in self.procs:
|
||||||
p.q_pend.put((0, dest, [args[0], len(self.procs)]))
|
p.q_pend.put((0, dest, [args[0], len(self.procs)]))
|
||||||
|
|
||||||
elif dest == "set_netdevs":
|
elif dest == "httpsrv.set_netdevs":
|
||||||
for p in self.procs:
|
for p in self.procs:
|
||||||
p.q_pend.put((0, dest, list(args)))
|
p.q_pend.put((0, dest, list(args)))
|
||||||
|
|
||||||
|
@ -140,3 +159,19 @@ class BrokerMp(object):
|
||||||
|
|
||||||
else:
|
else:
|
||||||
raise Exception("what is " + str(dest))
|
raise Exception("what is " + str(dest))
|
||||||
|
|
||||||
|
def periodic(self) -> None:
|
||||||
|
while True:
|
||||||
|
time.sleep(1)
|
||||||
|
|
||||||
|
tdli = {}
|
||||||
|
tdls = {}
|
||||||
|
qs = self.wask("httpsrv.read_dls")
|
||||||
|
for q in qs:
|
||||||
|
qr = q.get()
|
||||||
|
dli, dls = qr
|
||||||
|
tdli.update(dli)
|
||||||
|
tdls.update(dls)
|
||||||
|
tdl = (tdli, tdls)
|
||||||
|
for p in self.procs:
|
||||||
|
p.q_pend.put((0, "httpsrv.write_dls", tdl))
|
||||||
|
|
|
@ -82,37 +82,38 @@ class MpWorker(BrokerCli):
|
||||||
while True:
|
while True:
|
||||||
retq_id, dest, args = self.q_pend.get()
|
retq_id, dest, args = self.q_pend.get()
|
||||||
|
|
||||||
# self.logw("work: [{}]".format(d[0]))
|
if dest == "retq":
|
||||||
|
# response from previous ipc call
|
||||||
|
with self.retpend_mutex:
|
||||||
|
retq = self.retpend.pop(retq_id)
|
||||||
|
|
||||||
|
retq.put(args)
|
||||||
|
continue
|
||||||
|
|
||||||
if dest == "shutdown":
|
if dest == "shutdown":
|
||||||
self.httpsrv.shutdown()
|
self.httpsrv.shutdown()
|
||||||
self.logw("ok bye")
|
self.logw("ok bye")
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
return
|
return
|
||||||
|
|
||||||
elif dest == "reload":
|
if dest == "reload":
|
||||||
self.logw("mpw.asrv reloading")
|
self.logw("mpw.asrv reloading")
|
||||||
self.asrv.reload()
|
self.asrv.reload()
|
||||||
self.logw("mpw.asrv reloaded")
|
self.logw("mpw.asrv reloaded")
|
||||||
|
continue
|
||||||
|
|
||||||
elif dest == "reload_sessions":
|
if dest == "reload_sessions":
|
||||||
with self.asrv.mutex:
|
with self.asrv.mutex:
|
||||||
self.asrv.load_sessions()
|
self.asrv.load_sessions()
|
||||||
|
continue
|
||||||
|
|
||||||
elif dest == "listen":
|
obj = self
|
||||||
self.httpsrv.listen(args[0], args[1])
|
for node in dest.split("."):
|
||||||
|
obj = getattr(obj, node)
|
||||||
|
|
||||||
elif dest == "set_netdevs":
|
rv = obj(*args) # type: ignore
|
||||||
self.httpsrv.set_netdevs(args[0])
|
if retq_id:
|
||||||
|
self.say("retq", rv, retq_id=retq_id)
|
||||||
elif dest == "retq":
|
|
||||||
# response from previous ipc call
|
|
||||||
with self.retpend_mutex:
|
|
||||||
retq = self.retpend.pop(retq_id)
|
|
||||||
|
|
||||||
retq.put(args)
|
|
||||||
|
|
||||||
else:
|
|
||||||
raise Exception("what is " + str(dest))
|
|
||||||
|
|
||||||
def ask(self, dest: str, *args: Any) -> Union[ExceptionalQueue, NotExQueue]:
|
def ask(self, dest: str, *args: Any) -> Union[ExceptionalQueue, NotExQueue]:
|
||||||
retq = ExceptionalQueue(1)
|
retq = ExceptionalQueue(1)
|
||||||
|
@ -123,5 +124,5 @@ class MpWorker(BrokerCli):
|
||||||
self.q_yield.put((retq_id, dest, list(args)))
|
self.q_yield.put((retq_id, dest, list(args)))
|
||||||
return retq
|
return retq
|
||||||
|
|
||||||
def say(self, dest: str, *args: Any) -> None:
|
def say(self, dest: str, *args: Any, retq_id=0) -> None:
|
||||||
self.q_yield.put((0, dest, list(args)))
|
self.q_yield.put((retq_id, dest, list(args)))
|
||||||
|
|
|
@ -53,11 +53,11 @@ class BrokerThr(BrokerCli):
|
||||||
return NotExQueue(obj(*args)) # type: ignore
|
return NotExQueue(obj(*args)) # type: ignore
|
||||||
|
|
||||||
def say(self, dest: str, *args: Any) -> None:
|
def say(self, dest: str, *args: Any) -> None:
|
||||||
if dest == "listen":
|
if dest == "httpsrv.listen":
|
||||||
self.httpsrv.listen(args[0], 1)
|
self.httpsrv.listen(args[0], 1)
|
||||||
return
|
return
|
||||||
|
|
||||||
if dest == "set_netdevs":
|
if dest == "httpsrv.set_netdevs":
|
||||||
self.httpsrv.set_netdevs(args[0])
|
self.httpsrv.set_netdevs(args[0])
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
|
@ -186,6 +186,7 @@ class HttpCli(object):
|
||||||
self.rem = " "
|
self.rem = " "
|
||||||
self.vpath = " "
|
self.vpath = " "
|
||||||
self.vpaths = " "
|
self.vpaths = " "
|
||||||
|
self.dl_id = ""
|
||||||
self.gctx = " " # additional context for garda
|
self.gctx = " " # additional context for garda
|
||||||
self.trailing_slash = True
|
self.trailing_slash = True
|
||||||
self.uname = " "
|
self.uname = " "
|
||||||
|
@ -726,6 +727,11 @@ class HttpCli(object):
|
||||||
except Pebkac:
|
except Pebkac:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
finally:
|
||||||
|
if self.dl_id:
|
||||||
|
self.conn.hsrv.dli.pop(self.dl_id, None)
|
||||||
|
self.conn.hsrv.dls.pop(self.dl_id, None)
|
||||||
|
|
||||||
def dip(self) -> str:
|
def dip(self) -> str:
|
||||||
if self.args.plain_ip:
|
if self.args.plain_ip:
|
||||||
return self.ip.replace(":", ".")
|
return self.ip.replace(":", ".")
|
||||||
|
@ -1218,6 +1224,9 @@ class HttpCli(object):
|
||||||
if "shares" in self.uparam:
|
if "shares" in self.uparam:
|
||||||
return self.tx_shares()
|
return self.tx_shares()
|
||||||
|
|
||||||
|
if "dls" in self.uparam:
|
||||||
|
return self.tx_dls()
|
||||||
|
|
||||||
if "h" in self.uparam:
|
if "h" in self.uparam:
|
||||||
return self.tx_mounts()
|
return self.tx_mounts()
|
||||||
|
|
||||||
|
@ -3690,6 +3699,8 @@ class HttpCli(object):
|
||||||
self.args.s_wr_sz,
|
self.args.s_wr_sz,
|
||||||
self.args.s_wr_slp,
|
self.args.s_wr_slp,
|
||||||
not self.args.no_poll,
|
not self.args.no_poll,
|
||||||
|
{},
|
||||||
|
"",
|
||||||
)
|
)
|
||||||
res.close()
|
res.close()
|
||||||
|
|
||||||
|
@ -3736,6 +3747,7 @@ class HttpCli(object):
|
||||||
editions: dict[str, tuple[str, int]] = {}
|
editions: dict[str, tuple[str, int]] = {}
|
||||||
for ext in ("", ".gz"):
|
for ext in ("", ".gz"):
|
||||||
if ptop is not None:
|
if ptop is not None:
|
||||||
|
assert job and ap_data # type: ignore # !rm
|
||||||
sz = job["size"]
|
sz = job["size"]
|
||||||
file_ts = job["lmod"]
|
file_ts = job["lmod"]
|
||||||
editions["plain"] = (ap_data, sz)
|
editions["plain"] = (ap_data, sz)
|
||||||
|
@ -3904,7 +3916,21 @@ class HttpCli(object):
|
||||||
self.send_headers(length=upper - lower, status=status, mime=mime)
|
self.send_headers(length=upper - lower, status=status, mime=mime)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
dls = self.conn.hsrv.dls
|
||||||
|
if upper - lower > 0x400000: # 4m
|
||||||
|
now = time.time()
|
||||||
|
self.dl_id = "%s:%s" % (self.ip, self.addr[1])
|
||||||
|
dls[self.dl_id] = (now, 0)
|
||||||
|
self.conn.hsrv.dli[self.dl_id] = (
|
||||||
|
now,
|
||||||
|
upper - lower,
|
||||||
|
self.vn,
|
||||||
|
self.vpath,
|
||||||
|
self.uname,
|
||||||
|
)
|
||||||
|
|
||||||
if ptop is not None:
|
if ptop is not None:
|
||||||
|
assert job and ap_data # type: ignore # !rm
|
||||||
return self.tx_pipe(
|
return self.tx_pipe(
|
||||||
ptop, req_path, ap_data, job, lower, upper, status, mime, logmsg
|
ptop, req_path, ap_data, job, lower, upper, status, mime, logmsg
|
||||||
)
|
)
|
||||||
|
@ -3923,6 +3949,8 @@ class HttpCli(object):
|
||||||
self.args.s_wr_sz,
|
self.args.s_wr_sz,
|
||||||
self.args.s_wr_slp,
|
self.args.s_wr_slp,
|
||||||
not self.args.no_poll,
|
not self.args.no_poll,
|
||||||
|
dls,
|
||||||
|
self.dl_id,
|
||||||
)
|
)
|
||||||
|
|
||||||
if remains > 0:
|
if remains > 0:
|
||||||
|
@ -4073,6 +4101,8 @@ class HttpCli(object):
|
||||||
wr_sz,
|
wr_sz,
|
||||||
wr_slp,
|
wr_slp,
|
||||||
not self.args.no_poll,
|
not self.args.no_poll,
|
||||||
|
self.conn.hsrv.dls,
|
||||||
|
self.dl_id,
|
||||||
)
|
)
|
||||||
|
|
||||||
spd = self._spd((upper - lower) - remains)
|
spd = self._spd((upper - lower) - remains)
|
||||||
|
@ -4158,6 +4188,18 @@ class HttpCli(object):
|
||||||
self.log("transcoding to [{}]".format(cfmt))
|
self.log("transcoding to [{}]".format(cfmt))
|
||||||
fgen = gfilter(fgen, self.thumbcli, self.uname, vpath, cfmt)
|
fgen = gfilter(fgen, self.thumbcli, self.uname, vpath, cfmt)
|
||||||
|
|
||||||
|
now = time.time()
|
||||||
|
self.dl_id = "%s:%s" % (self.ip, self.addr[1])
|
||||||
|
self.conn.hsrv.dli[self.dl_id] = (
|
||||||
|
now,
|
||||||
|
0,
|
||||||
|
self.vn,
|
||||||
|
"%s :%s" % (self.vpath, ext),
|
||||||
|
self.uname,
|
||||||
|
)
|
||||||
|
dls = self.conn.hsrv.dls
|
||||||
|
dls[self.dl_id] = (time.time(), 0)
|
||||||
|
|
||||||
bgen = packer(
|
bgen = packer(
|
||||||
self.log,
|
self.log,
|
||||||
self.asrv,
|
self.asrv,
|
||||||
|
@ -4166,6 +4208,7 @@ class HttpCli(object):
|
||||||
pre_crc="crc" in uarg,
|
pre_crc="crc" in uarg,
|
||||||
cmp=uarg if cancmp or uarg == "pax" else "",
|
cmp=uarg if cancmp or uarg == "pax" else "",
|
||||||
)
|
)
|
||||||
|
n = 0
|
||||||
bsent = 0
|
bsent = 0
|
||||||
for buf in bgen.gen():
|
for buf in bgen.gen():
|
||||||
if not buf:
|
if not buf:
|
||||||
|
@ -4179,6 +4222,11 @@ class HttpCli(object):
|
||||||
bgen.stop()
|
bgen.stop()
|
||||||
break
|
break
|
||||||
|
|
||||||
|
n += 1
|
||||||
|
if n >= 4:
|
||||||
|
n = 0
|
||||||
|
dls[self.dl_id] = (time.time(), bsent)
|
||||||
|
|
||||||
spd = self._spd(bsent)
|
spd = self._spd(bsent)
|
||||||
self.log("{}, {}".format(logmsg, spd))
|
self.log("{}, {}".format(logmsg, spd))
|
||||||
return True
|
return True
|
||||||
|
@ -4436,6 +4484,32 @@ class HttpCli(object):
|
||||||
|
|
||||||
assert vstate.items and vs # type: ignore # !rm
|
assert vstate.items and vs # type: ignore # !rm
|
||||||
|
|
||||||
|
dls = dl_list = []
|
||||||
|
if self.conn.hsrv.tdls:
|
||||||
|
zi = self.args.dl_list
|
||||||
|
if zi == 2 or (zi == 1 and self.avol):
|
||||||
|
dl_list = self.get_dls()
|
||||||
|
for t0, t1, sent, sz, vp, dl_id, uname in dl_list:
|
||||||
|
rem = sz - sent
|
||||||
|
td = max(0.1, now - t0)
|
||||||
|
rd, fn = vsplit(vp)
|
||||||
|
if not rd:
|
||||||
|
rd = "/"
|
||||||
|
erd = quotep(rd)
|
||||||
|
rds = rd.replace("/", " / ")
|
||||||
|
spd = humansize(sent / td, True) + "/s"
|
||||||
|
hsent = humansize(sent, True)
|
||||||
|
idle = s2hms(now - t1, True)
|
||||||
|
usr = "%s @%s" % (dl_id, uname) if dl_id else uname
|
||||||
|
if sz and sent and td:
|
||||||
|
eta = s2hms((sz - sent) / (sent / td), True)
|
||||||
|
perc = int(100 * sent / sz)
|
||||||
|
else:
|
||||||
|
eta = perc = "--"
|
||||||
|
|
||||||
|
fn = html_escape(fn) if fn else self.conn.hsrv.iiam
|
||||||
|
dls.append((perc, hsent, spd, eta, idle, usr, erd, rds, fn))
|
||||||
|
|
||||||
fmt = self.uparam.get("ls", "")
|
fmt = self.uparam.get("ls", "")
|
||||||
if not fmt and (self.ua.startswith("curl/") or self.ua.startswith("fetch")):
|
if not fmt and (self.ua.startswith("curl/") or self.ua.startswith("fetch")):
|
||||||
fmt = "v"
|
fmt = "v"
|
||||||
|
@ -4457,6 +4531,12 @@ class HttpCli(object):
|
||||||
txt += "\n%s" % (", ".join((str(x) for x in zt)),)
|
txt += "\n%s" % (", ".join((str(x) for x in zt)),)
|
||||||
txt += "\n"
|
txt += "\n"
|
||||||
|
|
||||||
|
if dls:
|
||||||
|
txt += "\n\nactive downloads:"
|
||||||
|
for zt in dls:
|
||||||
|
txt += "\n%s" % (", ".join((str(x) for x in zt)),)
|
||||||
|
txt += "\n"
|
||||||
|
|
||||||
if rvol:
|
if rvol:
|
||||||
txt += "\nyou can browse:"
|
txt += "\nyou can browse:"
|
||||||
for v in rvol:
|
for v in rvol:
|
||||||
|
@ -4480,6 +4560,7 @@ class HttpCli(object):
|
||||||
avol=avol,
|
avol=avol,
|
||||||
in_shr=self.args.shr and self.vpath.startswith(self.args.shr1),
|
in_shr=self.args.shr and self.vpath.startswith(self.args.shr1),
|
||||||
vstate=vstate,
|
vstate=vstate,
|
||||||
|
dls=dls,
|
||||||
ups=ups,
|
ups=ups,
|
||||||
scanning=vs["scanning"],
|
scanning=vs["scanning"],
|
||||||
hashq=vs["hashq"],
|
hashq=vs["hashq"],
|
||||||
|
@ -4700,6 +4781,40 @@ class HttpCli(object):
|
||||||
ret["a"] = dirs
|
ret["a"] = dirs
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
|
def get_dls(self) -> list[list[Any]]:
|
||||||
|
ret = []
|
||||||
|
dls = self.conn.hsrv.tdls
|
||||||
|
for dl_id, (t0, sz, vn, vp, uname) in self.conn.hsrv.tdli.items():
|
||||||
|
t1, sent = dls[dl_id]
|
||||||
|
if sent > 0x100000: # 1m; buffers 2~4
|
||||||
|
sent -= 0x100000
|
||||||
|
if self.uname not in vn.axs.uread:
|
||||||
|
vp = ""
|
||||||
|
elif self.uname not in vn.axs.udot and (vp.startswith(".") or "/." in vp):
|
||||||
|
vp = ""
|
||||||
|
if self.uname not in vn.axs.uadmin:
|
||||||
|
dl_id = uname = ""
|
||||||
|
|
||||||
|
ret.append([t0, t1, sent, sz, vp, dl_id, uname])
|
||||||
|
return ret
|
||||||
|
|
||||||
|
def tx_dls(self) -> bool:
|
||||||
|
ret = [
|
||||||
|
{
|
||||||
|
"t0": x[0],
|
||||||
|
"t1": x[1],
|
||||||
|
"sent": x[2],
|
||||||
|
"size": x[3],
|
||||||
|
"path": x[4],
|
||||||
|
"conn": x[5],
|
||||||
|
"uname": x[6],
|
||||||
|
}
|
||||||
|
for x in self.get_dls()
|
||||||
|
]
|
||||||
|
zs = json.dumps(ret, separators=(",\n", ": "))
|
||||||
|
self.reply(zs.encode("utf-8", "replace"), mime="application/json")
|
||||||
|
return True
|
||||||
|
|
||||||
def tx_ups(self) -> bool:
|
def tx_ups(self) -> bool:
|
||||||
idx = self.conn.get_u2idx()
|
idx = self.conn.get_u2idx()
|
||||||
if not idx or not hasattr(idx, "p_end"):
|
if not idx or not hasattr(idx, "p_end"):
|
||||||
|
|
|
@ -81,6 +81,7 @@ from .util import (
|
||||||
)
|
)
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
|
from .authsrv import VFS
|
||||||
from .broker_util import BrokerCli
|
from .broker_util import BrokerCli
|
||||||
from .ssdp import SSDPr
|
from .ssdp import SSDPr
|
||||||
|
|
||||||
|
@ -130,6 +131,12 @@ class HttpSrv(object):
|
||||||
self.bans: dict[str, int] = {}
|
self.bans: dict[str, int] = {}
|
||||||
self.aclose: dict[str, int] = {}
|
self.aclose: dict[str, int] = {}
|
||||||
|
|
||||||
|
dli: dict[str, tuple[float, int, "VFS", str, str]] = {} # info
|
||||||
|
dls: dict[str, tuple[float, int]] = {} # state
|
||||||
|
self.dli = self.tdli = dli
|
||||||
|
self.dls = self.tdls = dls
|
||||||
|
self.iiam = '<img src="%s.cpr/iiam.gif" />' % (self.args.SRS,)
|
||||||
|
|
||||||
self.bound: set[tuple[str, int]] = set()
|
self.bound: set[tuple[str, int]] = set()
|
||||||
self.name = "hsrv" + nsuf
|
self.name = "hsrv" + nsuf
|
||||||
self.mutex = threading.Lock()
|
self.mutex = threading.Lock()
|
||||||
|
@ -205,6 +212,9 @@ class HttpSrv(object):
|
||||||
self.start_threads(4)
|
self.start_threads(4)
|
||||||
|
|
||||||
if nid:
|
if nid:
|
||||||
|
self.tdli = {}
|
||||||
|
self.tdls = {}
|
||||||
|
|
||||||
if self.args.stackmon:
|
if self.args.stackmon:
|
||||||
start_stackmon(self.args.stackmon, nid)
|
start_stackmon(self.args.stackmon, nid)
|
||||||
|
|
||||||
|
@ -579,3 +589,32 @@ class HttpSrv(object):
|
||||||
ident += "a"
|
ident += "a"
|
||||||
|
|
||||||
self.u2idx_free[ident] = u2idx
|
self.u2idx_free[ident] = u2idx
|
||||||
|
|
||||||
|
def read_dls(
|
||||||
|
self,
|
||||||
|
) -> tuple[
|
||||||
|
dict[str, tuple[float, int, str, str, str]], dict[str, tuple[float, int]]
|
||||||
|
]:
|
||||||
|
"""
|
||||||
|
mp-broker asking for local dl-info + dl-state;
|
||||||
|
reduce overhead by sending just the vfs vpath
|
||||||
|
"""
|
||||||
|
dli = {k: (a, b, c.vpath, d, e) for k, (a, b, c, d, e) in self.dli.items()}
|
||||||
|
return (dli, self.dls)
|
||||||
|
|
||||||
|
def write_dls(
|
||||||
|
self,
|
||||||
|
sdli: dict[str, tuple[float, int, str, str, str]],
|
||||||
|
dls: dict[str, tuple[float, int]],
|
||||||
|
) -> None:
|
||||||
|
"""
|
||||||
|
mp-broker pushing total dl-info + dl-state;
|
||||||
|
swap out the vfs vpath with the vfs node
|
||||||
|
"""
|
||||||
|
dli: dict[str, tuple[float, int, "VFS", str, str]] = {}
|
||||||
|
for k, (a, b, c, d, e) in sdli.items():
|
||||||
|
vn = self.asrv.vfs.all_vols[c]
|
||||||
|
dli[k] = (a, b, vn, d, e)
|
||||||
|
|
||||||
|
self.tdli = dli
|
||||||
|
self.tdls = dls
|
||||||
|
|
|
@ -72,6 +72,9 @@ class Metrics(object):
|
||||||
v = "{:.3f}".format(self.hsrv.t0)
|
v = "{:.3f}".format(self.hsrv.t0)
|
||||||
addug("cpp_boot_unixtime", "seconds", v, t)
|
addug("cpp_boot_unixtime", "seconds", v, t)
|
||||||
|
|
||||||
|
t = "number of active downloads"
|
||||||
|
addg("cpp_active_dl", str(len(self.hsrv.tdls)), t)
|
||||||
|
|
||||||
t = "number of open http(s) client connections"
|
t = "number of open http(s) client connections"
|
||||||
addg("cpp_http_conns", str(self.hsrv.ncli), t)
|
addg("cpp_http_conns", str(self.hsrv.ncli), t)
|
||||||
|
|
||||||
|
|
|
@ -1004,15 +1004,18 @@ class SvcHub(object):
|
||||||
except:
|
except:
|
||||||
self.log("root", "ssdp startup failed;\n" + min_ex(), 3)
|
self.log("root", "ssdp startup failed;\n" + min_ex(), 3)
|
||||||
|
|
||||||
def reload(self, rescan_all_vols: bool, up2k: bool) -> None:
|
def reload(self, rescan_all_vols: bool, up2k: bool) -> str:
|
||||||
|
t = "config has been reloaded"
|
||||||
with self.reload_mutex:
|
with self.reload_mutex:
|
||||||
self.log("root", "reloading config")
|
self.log("root", "reloading config")
|
||||||
self.asrv.reload(9 if up2k else 4)
|
self.asrv.reload(9 if up2k else 4)
|
||||||
if up2k:
|
if up2k:
|
||||||
self.up2k.reload(rescan_all_vols)
|
self.up2k.reload(rescan_all_vols)
|
||||||
|
t += "; volumes are now reinitializing"
|
||||||
else:
|
else:
|
||||||
self.log("root", "reload done")
|
self.log("root", "reload done")
|
||||||
self.broker.reload()
|
self.broker.reload()
|
||||||
|
return t
|
||||||
|
|
||||||
def _reload_sessions(self) -> None:
|
def _reload_sessions(self) -> None:
|
||||||
with self.asrv.mutex:
|
with self.asrv.mutex:
|
||||||
|
|
|
@ -371,7 +371,7 @@ class TcpSrv(object):
|
||||||
if self.args.q:
|
if self.args.q:
|
||||||
print(msg)
|
print(msg)
|
||||||
|
|
||||||
self.hub.broker.say("listen", srv)
|
self.hub.broker.say("httpsrv.listen", srv)
|
||||||
|
|
||||||
self.srv = srvs
|
self.srv = srvs
|
||||||
self.bound = bound
|
self.bound = bound
|
||||||
|
@ -379,7 +379,7 @@ class TcpSrv(object):
|
||||||
self._distribute_netdevs()
|
self._distribute_netdevs()
|
||||||
|
|
||||||
def _distribute_netdevs(self):
|
def _distribute_netdevs(self):
|
||||||
self.hub.broker.say("set_netdevs", self.netdevs)
|
self.hub.broker.say("httpsrv.set_netdevs", self.netdevs)
|
||||||
self.hub.start_zeroconf()
|
self.hub.start_zeroconf()
|
||||||
gencert(self.log, self.args, self.netdevs)
|
gencert(self.log, self.args, self.netdevs)
|
||||||
self.hub.restart_ftpd()
|
self.hub.restart_ftpd()
|
||||||
|
|
|
@ -2791,7 +2791,10 @@ def sendfile_py(
|
||||||
bufsz: int,
|
bufsz: int,
|
||||||
slp: float,
|
slp: float,
|
||||||
use_poll: bool,
|
use_poll: bool,
|
||||||
|
dls: dict[str, tuple[float, int]],
|
||||||
|
dl_id: str,
|
||||||
) -> int:
|
) -> int:
|
||||||
|
sent = 0
|
||||||
remains = upper - lower
|
remains = upper - lower
|
||||||
f.seek(lower)
|
f.seek(lower)
|
||||||
while remains > 0:
|
while remains > 0:
|
||||||
|
@ -2808,6 +2811,10 @@ def sendfile_py(
|
||||||
except:
|
except:
|
||||||
return remains
|
return remains
|
||||||
|
|
||||||
|
if dl_id:
|
||||||
|
sent += len(buf)
|
||||||
|
dls[dl_id] = (time.time(), sent)
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
@ -2820,6 +2827,8 @@ def sendfile_kern(
|
||||||
bufsz: int,
|
bufsz: int,
|
||||||
slp: float,
|
slp: float,
|
||||||
use_poll: bool,
|
use_poll: bool,
|
||||||
|
dls: dict[str, tuple[float, int]],
|
||||||
|
dl_id: str,
|
||||||
) -> int:
|
) -> int:
|
||||||
out_fd = s.fileno()
|
out_fd = s.fileno()
|
||||||
in_fd = f.fileno()
|
in_fd = f.fileno()
|
||||||
|
@ -2832,7 +2841,7 @@ def sendfile_kern(
|
||||||
while ofs < upper:
|
while ofs < upper:
|
||||||
stuck = stuck or time.time()
|
stuck = stuck or time.time()
|
||||||
try:
|
try:
|
||||||
req = min(2 ** 30, upper - ofs)
|
req = min(0x2000000, upper - ofs) # 32 MiB
|
||||||
if use_poll:
|
if use_poll:
|
||||||
poll.poll(10000)
|
poll.poll(10000)
|
||||||
else:
|
else:
|
||||||
|
@ -2856,6 +2865,9 @@ def sendfile_kern(
|
||||||
return upper - ofs
|
return upper - ofs
|
||||||
|
|
||||||
ofs += n
|
ofs += n
|
||||||
|
if dl_id:
|
||||||
|
dls[dl_id] = (time.time(), ofs - lower)
|
||||||
|
|
||||||
# print("sendfile: ok, sent {} now, {} total, {} remains".format(n, ofs - lower, upper - ofs))
|
# print("sendfile: ok, sent {} now, {} total, {} remains".format(n, ofs - lower, upper - ofs))
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
|
|
BIN
copyparty/web/iiam.gif
Normal file
BIN
copyparty/web/iiam.gif
Normal file
Binary file not shown.
After Width: | Height: | Size: 230 B |
|
@ -90,6 +90,10 @@ table {
|
||||||
text-align: left;
|
text-align: left;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
.vols td:empty,
|
||||||
|
.vols th:empty {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
.num {
|
.num {
|
||||||
border-right: 1px solid #bbb;
|
border-right: 1px solid #bbb;
|
||||||
}
|
}
|
||||||
|
@ -222,3 +226,6 @@ html.bz {
|
||||||
color: #bbd;
|
color: #bbd;
|
||||||
background: #11121d;
|
background: #11121d;
|
||||||
}
|
}
|
||||||
|
html.bz .vols img {
|
||||||
|
filter: sepia(0.8) hue-rotate(180deg);
|
||||||
|
}
|
||||||
|
|
|
@ -44,6 +44,18 @@
|
||||||
</table>
|
</table>
|
||||||
{%- endif %}
|
{%- endif %}
|
||||||
|
|
||||||
|
{%- if dls %}
|
||||||
|
<h1 id="ae">active downloads:</h1>
|
||||||
|
<table class="vols">
|
||||||
|
<thead><tr><th>%</th><th>sent</th><th>speed</th><th>eta</th><th>idle</th><th></th><th>dir</th><th>file</th></tr></thead>
|
||||||
|
<tbody>
|
||||||
|
{% for u in dls %}
|
||||||
|
<tr><td>{{ u[0] }}</td><td>{{ u[1] }}</td><td>{{ u[2] }}</td><td>{{ u[3] }}</td><td>{{ u[4] }}</td><td>{{ u[5] }}</td><td><a href="{{ u[6] }}">{{ u[7]|e }}</a></td><td>{{ u[8] }}</td></tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
{%- endif %}
|
||||||
|
|
||||||
{%- if avol %}
|
{%- if avol %}
|
||||||
<h1>admin panel:</h1>
|
<h1>admin panel:</h1>
|
||||||
<table><tr><td> <!-- hehehe -->
|
<table><tr><td> <!-- hehehe -->
|
||||||
|
|
|
@ -37,6 +37,7 @@ var Ls = {
|
||||||
"ab1": "skru av no304",
|
"ab1": "skru av no304",
|
||||||
"ac1": "skru på no304",
|
"ac1": "skru på no304",
|
||||||
"ad1": "no304 stopper all bruk av cache. Hvis ikke k304 var nok, prøv denne. Vil mangedoble dataforbruk!",
|
"ad1": "no304 stopper all bruk av cache. Hvis ikke k304 var nok, prøv denne. Vil mangedoble dataforbruk!",
|
||||||
|
"ae1": "utgående:",
|
||||||
},
|
},
|
||||||
"eng": {
|
"eng": {
|
||||||
"d2": "shows the state of all active threads",
|
"d2": "shows the state of all active threads",
|
||||||
|
@ -86,6 +87,7 @@ var Ls = {
|
||||||
"ab1": "关闭 k304",
|
"ab1": "关闭 k304",
|
||||||
"ac1": "开启 k304",
|
"ac1": "开启 k304",
|
||||||
"ad1": "启用 no304 将禁用所有缓存;如果 k304 不够,可以尝试此选项。这将消耗大量的网络流量!", //m
|
"ad1": "启用 no304 将禁用所有缓存;如果 k304 不够,可以尝试此选项。这将消耗大量的网络流量!", //m
|
||||||
|
"ae1": "正在下载:", //m
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -140,6 +140,7 @@ authenticate using header `Cookie: cppwd=foo` or url param `&pw=foo`
|
||||||
| GET | `?tar&j` | pregenerate jpg thumbnails |
|
| GET | `?tar&j` | pregenerate jpg thumbnails |
|
||||||
| GET | `?tar&p` | pregenerate audio waveforms |
|
| GET | `?tar&p` | pregenerate audio waveforms |
|
||||||
| GET | `?shares` | list your shared files/folders |
|
| GET | `?shares` | list your shared files/folders |
|
||||||
|
| GET | `?dls` | show active downloads (do this as admin) |
|
||||||
| GET | `?ups` | show recent uploads from your IP |
|
| GET | `?ups` | show recent uploads from your IP |
|
||||||
| GET | `?ups&filter=f` | ...where URL contains `f` |
|
| GET | `?ups&filter=f` | ...where URL contains `f` |
|
||||||
| GET | `?mime=foo` | specify return mimetype `foo` |
|
| GET | `?mime=foo` | specify return mimetype `foo` |
|
||||||
|
|
|
@ -94,6 +94,7 @@ copyparty/web/deps/prismd.css,
|
||||||
copyparty/web/deps/scp.woff2,
|
copyparty/web/deps/scp.woff2,
|
||||||
copyparty/web/deps/sha512.ac.js,
|
copyparty/web/deps/sha512.ac.js,
|
||||||
copyparty/web/deps/sha512.hw.js,
|
copyparty/web/deps/sha512.hw.js,
|
||||||
|
copyparty/web/iiam.gif,
|
||||||
copyparty/web/md.css,
|
copyparty/web/md.css,
|
||||||
copyparty/web/md.html,
|
copyparty/web/md.html,
|
||||||
copyparty/web/md.js,
|
copyparty/web/md.js,
|
||||||
|
|
|
@ -79,6 +79,7 @@ var tl_cpanel = {
|
||||||
"ab1": "disable no304",
|
"ab1": "disable no304",
|
||||||
"ac1": "enable no304",
|
"ac1": "enable no304",
|
||||||
"ad1": "enabling no304 will disable all caching; try this if k304 wasn't enough. This will waste a huge amount of network traffic!",
|
"ad1": "enabling no304 will disable all caching; try this if k304 wasn't enough. This will waste a huge amount of network traffic!",
|
||||||
|
"ae1": "active downloads:",
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -57,6 +57,7 @@ class TestMetrics(unittest.TestCase):
|
||||||
ptns = r"""
|
ptns = r"""
|
||||||
cpp_uptime_seconds [0-9]\.[0-9]{3}$
|
cpp_uptime_seconds [0-9]\.[0-9]{3}$
|
||||||
cpp_boot_unixtime_seconds [0-9]{7,10}\.[0-9]{3}$
|
cpp_boot_unixtime_seconds [0-9]{7,10}\.[0-9]{3}$
|
||||||
|
cpp_active_dl 0$
|
||||||
cpp_http_reqs_created [0-9]{7,10}$
|
cpp_http_reqs_created [0-9]{7,10}$
|
||||||
cpp_http_reqs_total -1$
|
cpp_http_reqs_total -1$
|
||||||
cpp_http_conns 9$
|
cpp_http_conns 9$
|
||||||
|
|
|
@ -134,7 +134,7 @@ class Cfg(Namespace):
|
||||||
ex = "hash_mt safe_dedup srch_time u2abort u2j u2sz"
|
ex = "hash_mt safe_dedup srch_time u2abort u2j u2sz"
|
||||||
ka.update(**{k: 1 for k in ex.split()})
|
ka.update(**{k: 1 for k in ex.split()})
|
||||||
|
|
||||||
ex = "au_vol mtab_age reg_cap s_thead s_tbody th_convt"
|
ex = "au_vol dl_list mtab_age reg_cap s_thead s_tbody th_convt"
|
||||||
ka.update(**{k: 9 for k in ex.split()})
|
ka.update(**{k: 9 for k in ex.split()})
|
||||||
|
|
||||||
ex = "db_act k304 loris no304 re_maxage rproxy rsp_jtr rsp_slp s_wr_slp snap_wri theme themes turbo"
|
ex = "db_act k304 loris no304 re_maxage rproxy rsp_jtr rsp_slp s_wr_slp snap_wri theme themes turbo"
|
||||||
|
@ -254,6 +254,8 @@ class VHttpSrv(object):
|
||||||
self.broker = NullBroker(args, asrv)
|
self.broker = NullBroker(args, asrv)
|
||||||
self.prism = None
|
self.prism = None
|
||||||
self.bans = {}
|
self.bans = {}
|
||||||
|
self.tdls = self.dls = {}
|
||||||
|
self.tdli = self.dli = {}
|
||||||
self.nreq = 0
|
self.nreq = 0
|
||||||
self.nsus = 0
|
self.nsus = 0
|
||||||
|
|
||||||
|
@ -292,6 +294,8 @@ class VHttpConn(object):
|
||||||
self.args = args
|
self.args = args
|
||||||
self.asrv = asrv
|
self.asrv = asrv
|
||||||
self.bans = {}
|
self.bans = {}
|
||||||
|
self.tdls = self.dls = {}
|
||||||
|
self.tdli = self.dli = {}
|
||||||
self.freshen_pwd = 0.0
|
self.freshen_pwd = 0.0
|
||||||
|
|
||||||
Ctor = VHttpSrvUp2k if use_up2k else VHttpSrv
|
Ctor = VHttpSrvUp2k if use_up2k else VHttpSrv
|
||||||
|
|
Loading…
Reference in a new issue