mirror of
https://github.com/9001/copyparty.git
synced 2025-08-16 08:32:13 -06:00
[DB-V6]: store usernames; closes #530
This commit is contained in:
parent
1228b5510b
commit
4df033ecc3
|
@ -1120,6 +1120,7 @@ def add_upload(ap):
|
|||
ap2.add_argument("--put-ck", metavar="ALG", type=u, default="sha512", help="default checksum-hasher for PUT/WebDAV uploads: no / md5 / sha1 / sha256 / sha512 / b2 / blake2 / b2s / blake2s (volflag=put_ck)")
|
||||
ap2.add_argument("--bup-ck", metavar="ALG", type=u, default="sha512", help="default checksum-hasher for bup/basic-uploader: no / md5 / sha1 / sha256 / sha512 / b2 / blake2 / b2s / blake2s (volflag=bup_ck)")
|
||||
ap2.add_argument("--unpost", metavar="SEC", type=int, default=3600*12, help="grace period where uploads can be deleted by the uploader, even without delete permissions; 0=disabled, default=12h")
|
||||
ap2.add_argument("--unp-who", metavar="NUM", type=int, default=1, help="clients can undo recent uploads by using the unpost tab (requires \033[33m-e2d\033[0m). [\033[32m0\033[0m] = never allowed (disable feature), [\033[32m1\033[0m] = allow if client has the same IP as the upload AND is using the same account, [\033[32m2\033[0m] = just check the IP, [\033[32m3\033[0m] = just check account-name (volflag=unp_who)")
|
||||
ap2.add_argument("--u2abort", metavar="NUM", type=int, default=1, help="clients can abort incomplete uploads by using the unpost tab (requires \033[33m-e2d\033[0m). [\033[32m0\033[0m] = never allowed (disable feature), [\033[32m1\033[0m] = allow if client has the same IP as the upload AND is using the same account, [\033[32m2\033[0m] = just check the IP, [\033[32m3\033[0m] = just check account-name (volflag=u2abort)")
|
||||
ap2.add_argument("--blank-wt", metavar="SEC", type=int, default=300, help="file write grace period (any client can write to a blank file last-modified more recently than \033[33mSEC\033[0m seconds ago)")
|
||||
ap2.add_argument("--reg-cap", metavar="N", type=int, default=38400, help="max number of uploads to keep in memory when running without \033[33m-e2d\033[0m; roughly 1 MiB RAM per 600")
|
||||
|
|
|
@ -118,6 +118,7 @@ def vf_vmap() -> dict[str, str]:
|
|||
"u2ts",
|
||||
"uid",
|
||||
"gid",
|
||||
"unp_who",
|
||||
"ups_who",
|
||||
"zip_who",
|
||||
"zipmaxn",
|
||||
|
@ -343,6 +344,7 @@ flagcats = {
|
|||
"dky": 'allow seeing files (not folders) inside a specific folder\nwith "g" perm, and does not require a valid dirkey to do so',
|
||||
"rss": "allow '?rss' URL suffix (experimental)",
|
||||
"rmagic": "expensive analysis for mimetype accuracy",
|
||||
"unp_who=2": "unpost only if same... 1=ip+name, 2=ip, 3=name",
|
||||
"ups_who=2": "restrict viewing the list of recent uploads",
|
||||
"zip_who=2": "restrict access to download-as-zip/tar",
|
||||
"zipmaxn=9k": "reject download-as-zip if more than 9000 files",
|
||||
|
|
|
@ -5501,6 +5501,10 @@ class HttpCli(object):
|
|||
and ("*" in x.axs.uwrite or self.uname in x.axs.uwrite or x == shr_dbv)
|
||||
]
|
||||
|
||||
q = ""
|
||||
qp = (0,)
|
||||
q_c = -1
|
||||
|
||||
for vol in allvols:
|
||||
cur = idx.get_cur(vol)
|
||||
if not cur:
|
||||
|
@ -5508,9 +5512,23 @@ class HttpCli(object):
|
|||
|
||||
nfk, fk_alg = fk_vols.get(vol) or (0, 0)
|
||||
|
||||
zi = vol.flags["unp_who"]
|
||||
if q_c != zi:
|
||||
q_c = zi
|
||||
q = "select sz, rd, fn, at from up where "
|
||||
if zi == 1:
|
||||
q += "ip=? and un=?"
|
||||
qp = (self.ip, self.uname, lim)
|
||||
elif zi == 2:
|
||||
q += "ip=?"
|
||||
qp = (self.ip, lim)
|
||||
if zi == 3:
|
||||
q += "un=?"
|
||||
qp = (self.uname, lim)
|
||||
q += " and at>? order by at desc"
|
||||
|
||||
n = 2000
|
||||
q = "select sz, rd, fn, at from up where ip=? and at>? order by at desc"
|
||||
for sz, rd, fn, at in cur.execute(q, (self.ip, lim)):
|
||||
for sz, rd, fn, at in cur.execute(q, qp):
|
||||
vp = "/" + "/".join(x for x in [vol.vpath, rd, fn] if x)
|
||||
if nfi == 0 or (nfi == 1 and vfi in vp.lower()):
|
||||
pass
|
||||
|
@ -5635,8 +5653,8 @@ class HttpCli(object):
|
|||
continue
|
||||
|
||||
n = 1000
|
||||
q = "select sz, rd, fn, ip, at from up where at>0 order by at desc"
|
||||
for sz, rd, fn, ip, at in cur.execute(q):
|
||||
q = "select sz, rd, fn, ip, at, un from up where at>0 order by at desc"
|
||||
for sz, rd, fn, ip, at, un in cur.execute(q):
|
||||
vp = "/" + "/".join(x for x in [vol.vpath, rd, fn] if x)
|
||||
if nfi == 0 or (nfi == 1 and vfi in vp.lower()):
|
||||
pass
|
||||
|
@ -5657,6 +5675,7 @@ class HttpCli(object):
|
|||
"sz": sz,
|
||||
"ip": ip,
|
||||
"at": at,
|
||||
"un": un,
|
||||
"nfk": nfk,
|
||||
"adm": adm,
|
||||
}
|
||||
|
@ -5701,12 +5720,16 @@ class HttpCli(object):
|
|||
adm = rv.pop("adm")
|
||||
if not adm:
|
||||
rv["ip"] = "(You)" if rv["ip"] == self.ip else "(?)"
|
||||
if rv["un"] not in ("*", self.uname):
|
||||
rv["un"] = "(?)"
|
||||
else:
|
||||
for rv in ret:
|
||||
adm = rv.pop("adm")
|
||||
if not adm:
|
||||
rv["ip"] = "(You)" if rv["ip"] == self.ip else "(?)"
|
||||
rv["at"] = 0
|
||||
if rv["un"] not in ("*", self.uname):
|
||||
rv["un"] = "(?)"
|
||||
|
||||
if self.is_vproxied:
|
||||
for v in ret:
|
||||
|
@ -6628,13 +6651,15 @@ class HttpCli(object):
|
|||
tags = {k: v for k, v in r}
|
||||
|
||||
if is_admin:
|
||||
q = "select ip, at from up where rd=? and fn=?"
|
||||
q = "select ip, at, un from up where rd=? and fn=?"
|
||||
try:
|
||||
zs1, zs2 = icur.execute(q, erd_efn).fetchone()
|
||||
zs1, zs2, zs3 = icur.execute(q, erd_efn).fetchone()
|
||||
if zs1:
|
||||
tags["up_ip"] = zs1
|
||||
if zs2:
|
||||
tags[".up_at"] = zs2
|
||||
if zs3:
|
||||
tags["up_by"] = zs3
|
||||
except:
|
||||
pass
|
||||
elif add_up_at:
|
||||
|
@ -6655,7 +6680,7 @@ class HttpCli(object):
|
|||
|
||||
lmte = list(mte)
|
||||
if self.can_admin:
|
||||
lmte.extend(("up_ip", ".up_at"))
|
||||
lmte.extend(("up_by", "up_ip", ".up_at"))
|
||||
|
||||
if "nodirsz" not in vf:
|
||||
tagset.add(".files")
|
||||
|
|
|
@ -391,7 +391,7 @@ class U2idx(object):
|
|||
fk_alg = 2 if "fka" in flags else 1
|
||||
c = cur.execute(uq, tuple(vuv))
|
||||
for hit in c:
|
||||
w, ts, sz, rd, fn, ip, at = hit[:7]
|
||||
w, ts, sz, rd, fn = hit[:5]
|
||||
|
||||
if rd.startswith("//") or fn.startswith("//"):
|
||||
rd, fn = s3dec(rd, fn)
|
||||
|
|
|
@ -77,7 +77,7 @@ except:
|
|||
if HAVE_SQLITE3:
|
||||
import sqlite3
|
||||
|
||||
DB_VER = 5
|
||||
DB_VER = 6
|
||||
|
||||
if True: # pylint: disable=using-constant-test
|
||||
from typing import Any, Optional, Pattern, Union
|
||||
|
@ -1655,7 +1655,7 @@ class Up2k(object):
|
|||
abspath = cdirs + fn
|
||||
nohash = reh.search(abspath) if reh else False
|
||||
|
||||
sql = "select w, mt, sz, ip, at from up where rd = ? and fn = ?"
|
||||
sql = "select w, mt, sz, ip, at, un from up where rd = ? and fn = ?"
|
||||
try:
|
||||
c = db.c.execute(sql, (rd, fn))
|
||||
except:
|
||||
|
@ -1664,7 +1664,7 @@ class Up2k(object):
|
|||
in_db = list(c.fetchall())
|
||||
if in_db:
|
||||
self.pp.n -= 1
|
||||
dw, dts, dsz, ip, at = in_db[0]
|
||||
dw, dts, dsz, ip, at, un = in_db[0]
|
||||
if len(in_db) > 1:
|
||||
t = "WARN: multiple entries: %r => %r |%d|\n%r"
|
||||
rep_db = "\n".join([repr(x) for x in in_db])
|
||||
|
@ -1677,6 +1677,9 @@ class Up2k(object):
|
|||
if dts == lmod and dsz == sz and (nohash or dw[0] != "#" or not sz):
|
||||
continue
|
||||
|
||||
if un is None:
|
||||
un = ""
|
||||
|
||||
t = "reindex %r => %r mtime(%s/%s) size(%s/%s)"
|
||||
self.log(t % (top, rp, dts, lmod, dsz, sz))
|
||||
self.db_rm(db.c, rd, fn, 0)
|
||||
|
@ -1687,6 +1690,7 @@ class Up2k(object):
|
|||
dw = ""
|
||||
ip = ""
|
||||
at = 0
|
||||
un = ""
|
||||
|
||||
self.pp.msg = "a%d %s" % (self.pp.n, abspath)
|
||||
|
||||
|
@ -1712,9 +1716,10 @@ class Up2k(object):
|
|||
if dw and dw != wark:
|
||||
ip = ""
|
||||
at = 0
|
||||
un = ""
|
||||
|
||||
# skip upload hooks by not providing vflags
|
||||
self.db_add(db.c, {}, rd, fn, lmod, sz, "", "", wark, wark, "", "", ip, at)
|
||||
self.db_add(db.c, {}, rd, fn, lmod, sz, "", "", wark, wark, "", un, ip, at)
|
||||
db.n += 1
|
||||
db.nf += 1
|
||||
tfa += 1
|
||||
|
@ -2151,8 +2156,8 @@ class Up2k(object):
|
|||
|
||||
with self.mutex:
|
||||
try:
|
||||
q = "select rd, fn, ip, at from up where substr(w,1,16)=? and +w=?"
|
||||
rd, fn, ip, at = cur.execute(q, (w16, w)).fetchone()
|
||||
q = "select rd, fn, ip, at, un from up where substr(w,1,16)=? and +w=?"
|
||||
rd, fn, ip, at, un = cur.execute(q, (w16, w)).fetchone()
|
||||
except:
|
||||
# file modified/deleted since spooling
|
||||
continue
|
||||
|
@ -2171,12 +2176,15 @@ class Up2k(object):
|
|||
abspath = djoin(ptop, rd, fn)
|
||||
self.pp.msg = "c%d %s" % (nq, abspath)
|
||||
if not mpool:
|
||||
n_tags = self._tagscan_file(cur, entags, w, abspath, ip, at)
|
||||
n_tags = self._tagscan_file(cur, entags, w, abspath, ip, at, un)
|
||||
else:
|
||||
oth_tags = {}
|
||||
if ip:
|
||||
oth_tags = {"up_ip": ip, "up_at": at}
|
||||
else:
|
||||
oth_tags = {}
|
||||
oth_tags["up_ip"] = ip
|
||||
if at:
|
||||
oth_tags["up_at"] = at
|
||||
if un:
|
||||
oth_tags["up_by"] = un
|
||||
|
||||
mpool.put(Mpqe({}, entags, w, abspath, oth_tags))
|
||||
with self.mutex:
|
||||
|
@ -2332,8 +2340,8 @@ class Up2k(object):
|
|||
if w in in_progress:
|
||||
continue
|
||||
|
||||
q = "select rd, fn, ip, at from up where substr(w,1,16)=? limit 1"
|
||||
rd, fn, ip, at = cur.execute(q, (w,)).fetchone()
|
||||
q = "select rd, fn, ip, at, un from up where substr(w,1,16)=? limit 1"
|
||||
rd, fn, ip, at, un = cur.execute(q, (w,)).fetchone()
|
||||
rd, fn = s3dec(rd, fn)
|
||||
abspath = djoin(ptop, rd, fn)
|
||||
|
||||
|
@ -2357,7 +2365,10 @@ class Up2k(object):
|
|||
|
||||
if ip:
|
||||
oth_tags["up_ip"] = ip
|
||||
if at:
|
||||
oth_tags["up_at"] = at
|
||||
if un:
|
||||
oth_tags["up_by"] = un
|
||||
|
||||
jobs.append(Mpqe(parsers, set(), w, abspath, oth_tags))
|
||||
in_progress[w] = True
|
||||
|
@ -2546,6 +2557,7 @@ class Up2k(object):
|
|||
abspath: str,
|
||||
ip: str,
|
||||
at: float,
|
||||
un: Optional[str],
|
||||
) -> int:
|
||||
"""will mutex(main)"""
|
||||
assert self.mtag # !rm
|
||||
|
@ -2566,7 +2578,10 @@ class Up2k(object):
|
|||
|
||||
if ip:
|
||||
tags["up_ip"] = ip
|
||||
if at:
|
||||
tags["up_at"] = at
|
||||
if un:
|
||||
tags["up_by"] = un
|
||||
|
||||
with self.mutex:
|
||||
return self._tag_file(write_cur, entags, wark, abspath, tags)
|
||||
|
@ -2670,16 +2685,19 @@ class Up2k(object):
|
|||
if not existed and ver is None:
|
||||
return self._try_create_db(db_path, cur)
|
||||
|
||||
if ver == 4:
|
||||
for upver in (4, 5):
|
||||
if ver != upver:
|
||||
continue
|
||||
try:
|
||||
t = "creating backup before upgrade: "
|
||||
cur = self._backup_db(db_path, cur, ver, t)
|
||||
self._upgrade_v4(cur)
|
||||
ver = 5
|
||||
getattr(self, "_upgrade_v%d" % (upver,))(cur)
|
||||
ver += 1 # type: ignore
|
||||
except:
|
||||
self.log("WARN: failed to upgrade from v4", 3)
|
||||
self.log("WARN: failed to upgrade from v%d" % (ver,), 3)
|
||||
|
||||
if ver == DB_VER:
|
||||
# these no longer serve their intended purpose but they're great as additional sanchks
|
||||
self._add_dhash_tab(cur)
|
||||
self._add_xiu_tab(cur)
|
||||
self._add_cv_tab(cur)
|
||||
|
@ -2781,7 +2799,7 @@ class Up2k(object):
|
|||
idx = r"create index up_w on up(w)"
|
||||
|
||||
for cmd in [
|
||||
r"create table up (w text, mt int, sz int, rd text, fn text, ip text, at int)",
|
||||
r"create table up (w text, mt int, sz int, rd text, fn text, ip text, at int, un text)",
|
||||
r"create index up_vp on up(rd, fn)",
|
||||
r"create index up_fn on up(fn)",
|
||||
r"create index up_ip on up(ip)",
|
||||
|
@ -2814,6 +2832,15 @@ class Up2k(object):
|
|||
|
||||
cur.connection.commit()
|
||||
|
||||
def _upgrade_v5(self, cur: "sqlite3.Cursor") -> None:
|
||||
for cmd in [
|
||||
r"alter table up add column un text",
|
||||
r"update kv set v=6 where k='sver'",
|
||||
]:
|
||||
cur.execute(cmd)
|
||||
|
||||
cur.connection.commit()
|
||||
|
||||
def _add_dhash_tab(self, cur: "sqlite3.Cursor") -> None:
|
||||
# v5 -> v5a
|
||||
try:
|
||||
|
@ -3011,7 +3038,7 @@ class Up2k(object):
|
|||
argv = [dwark[:16], dwark]
|
||||
|
||||
c2 = cur.execute(q, tuple(argv))
|
||||
for _, dtime, dsize, dp_dir, dp_fn, ip, at in c2:
|
||||
for _, dtime, dsize, dp_dir, dp_fn, ip, at, _ in c2:
|
||||
if dp_dir.startswith("//") or dp_fn.startswith("//"):
|
||||
dp_dir, dp_fn = s3dec(dp_dir, dp_fn)
|
||||
|
||||
|
@ -3433,7 +3460,7 @@ class Up2k(object):
|
|||
try:
|
||||
vrel = vjoin(job["prel"], fname)
|
||||
xlink = bool(vf.get("xlink"))
|
||||
cur, wark, _, _, _, _ = self._find_from_vpath(ptop, vrel)
|
||||
cur, wark, _, _, _, _, _ = self._find_from_vpath(ptop, vrel)
|
||||
self._forget_file(ptop, vrel, cur, wark, True, st.st_size, xlink)
|
||||
except Exception as ex:
|
||||
self.log("skipping replace-relink: %r" % (ex,))
|
||||
|
@ -3890,14 +3917,14 @@ class Up2k(object):
|
|||
# plugins may expect this to look like an actual IP
|
||||
db_ip = "1.1.1.1" if "no_db_ip" in vflags else ip
|
||||
|
||||
sql = "insert into up values (?,?,?,?,?,?,?)"
|
||||
v = (dwark, int(ts), sz, rd, fn, db_ip, int(at or 0))
|
||||
sql = "insert into up values (?,?,?,?,?,?,?,?)"
|
||||
v = (dwark, int(ts), sz, rd, fn, db_ip, int(at or 0), usr)
|
||||
try:
|
||||
db.execute(sql, v)
|
||||
except:
|
||||
assert self.mem_cur # !rm
|
||||
rd, fn = s3enc(self.mem_cur, rd, fn)
|
||||
v = (dwark, int(ts), sz, rd, fn, db_ip, int(at or 0))
|
||||
v = (dwark, int(ts), sz, rd, fn, db_ip, int(at or 0), usr)
|
||||
db.execute(sql, v)
|
||||
|
||||
self.volsize[db] += sz
|
||||
|
@ -4038,7 +4065,7 @@ class Up2k(object):
|
|||
vn, rem = vn0.get_dbv(rem0)
|
||||
ptop = vn.realpath
|
||||
with self.mutex, self.reg_mutex:
|
||||
abrt_cfg = self.flags.get(ptop, {}).get("u2abort", 1)
|
||||
abrt_cfg = vn.flags.get("u2abort", 1)
|
||||
addr = (ip or "\n") if abrt_cfg in (1, 2) else ""
|
||||
user = ((uname or "\n"), "*") if abrt_cfg in (1, 3) else None
|
||||
reg = self.registry.get(ptop, {}) if abrt_cfg else {}
|
||||
|
@ -4059,17 +4086,22 @@ class Up2k(object):
|
|||
if partial:
|
||||
dip = ip
|
||||
dat = time.time()
|
||||
dun = uname
|
||||
un_cfg = 1
|
||||
else:
|
||||
if not self.args.unpost:
|
||||
un_cfg = vn.flags["unp_who"]
|
||||
if not self.args.unpost or not un_cfg:
|
||||
t = "the unpost feature is disabled in server config"
|
||||
raise Pebkac(400, t)
|
||||
|
||||
_, _, _, _, dip, dat = self._find_from_vpath(ptop, rem)
|
||||
_, _, _, _, dip, dat, dun = self._find_from_vpath(ptop, rem)
|
||||
|
||||
t = "you cannot delete this: "
|
||||
if not dip:
|
||||
t += "file not found"
|
||||
elif dip != ip:
|
||||
elif dip != ip and un_cfg in (1, 2):
|
||||
t += "not uploaded by (You)"
|
||||
elif dun != uname and un_cfg in (1, 3):
|
||||
t += "not uploaded by (You)"
|
||||
elif dat < time.time() - self.args.unpost:
|
||||
t += "uploaded too long ago"
|
||||
|
@ -4158,7 +4190,7 @@ class Up2k(object):
|
|||
try:
|
||||
ptop = dbv.realpath
|
||||
xlink = bool(dbv.flags.get("xlink"))
|
||||
cur, wark, _, _, _, _ = self._find_from_vpath(ptop, volpath)
|
||||
cur, wark, _, _, _, _, _ = self._find_from_vpath(ptop, volpath)
|
||||
self._forget_file(
|
||||
ptop, volpath, cur, wark, True, st.st_size, xlink
|
||||
)
|
||||
|
@ -4319,7 +4351,7 @@ class Up2k(object):
|
|||
|
||||
bos.makedirs(os.path.dirname(dabs), vf=dvn.flags)
|
||||
|
||||
c1, w, ftime_, fsize_, ip, at = self._find_from_vpath(
|
||||
c1, w, ftime_, fsize_, ip, at, un = self._find_from_vpath(
|
||||
svn_dbv.realpath, srem_dbv
|
||||
)
|
||||
c2 = self.cur.get(dvn.realpath)
|
||||
|
@ -4344,7 +4376,7 @@ class Up2k(object):
|
|||
w,
|
||||
w,
|
||||
"",
|
||||
"",
|
||||
un or "",
|
||||
ip or "",
|
||||
at or 0,
|
||||
)
|
||||
|
@ -4605,7 +4637,7 @@ class Up2k(object):
|
|||
|
||||
return "k"
|
||||
|
||||
c1, w, ftime_, fsize_, ip, at = self._find_from_vpath(svn.realpath, srem)
|
||||
c1, w, ftime_, fsize_, ip, at, un = self._find_from_vpath(svn.realpath, srem)
|
||||
c2 = self.cur.get(dvn.realpath)
|
||||
|
||||
has_dupes = False
|
||||
|
@ -4639,7 +4671,7 @@ class Up2k(object):
|
|||
w,
|
||||
w,
|
||||
"",
|
||||
"",
|
||||
un or "",
|
||||
ip or "",
|
||||
at or 0,
|
||||
)
|
||||
|
@ -4739,13 +4771,14 @@ class Up2k(object):
|
|||
Optional[int],
|
||||
str,
|
||||
Optional[int],
|
||||
str,
|
||||
]:
|
||||
cur = self.cur.get(ptop)
|
||||
if not cur:
|
||||
return None, None, None, None, "", None
|
||||
return None, None, None, None, "", None, ""
|
||||
|
||||
rd, fn = vsplit(vrem)
|
||||
q = "select w, mt, sz, ip, at from up where rd=? and fn=? limit 1"
|
||||
q = "select w, mt, sz, ip, at, un from up where rd=? and fn=? limit 1"
|
||||
try:
|
||||
c = cur.execute(q, (rd, fn))
|
||||
except:
|
||||
|
@ -4754,9 +4787,9 @@ class Up2k(object):
|
|||
|
||||
hit = c.fetchone()
|
||||
if hit:
|
||||
wark, ftime, fsize, ip, at = hit
|
||||
return cur, wark, ftime, fsize, ip, at
|
||||
return cur, None, None, None, "", None
|
||||
wark, ftime, fsize, ip, at, un = hit
|
||||
return cur, wark, ftime, fsize, ip, at, un
|
||||
return cur, None, None, None, "", None, ""
|
||||
|
||||
def _forget_file(
|
||||
self,
|
||||
|
|
|
@ -3602,7 +3602,7 @@ def runihook(
|
|||
verbose: bool,
|
||||
cmd: str,
|
||||
vol: "VFS",
|
||||
ups: list[tuple[str, int, int, str, str, str, int]],
|
||||
ups: list[tuple[str, int, int, str, str, str, int, str]],
|
||||
) -> bool:
|
||||
_, chk, fork, jtxt, wait, sp_ka, acmd = _parsehook(log, cmd)
|
||||
bcmd = [sfsenc(x) for x in acmd]
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
function render() {
|
||||
var html = ['<table id="tab"><thead><tr><th>size</th><th>who</th><th>when</th><th>age</th><th>dir</th><th>file</th></tr></thead><tbody>'];
|
||||
var html = ['<table id="tab"><thead><tr><th>size</th><th>who</th><th>ip</th><th>when</th><th>age</th><th>dir</th><th>file</th></tr></thead><tbody>'];
|
||||
var ups = V.ups, now = V.now;
|
||||
ebi('filter').value = V.filter;
|
||||
ebi('hits').innerHTML = 'showing ' + ups.length + ' files';
|
||||
|
@ -16,6 +16,7 @@ function render() {
|
|||
sz = ('' + f.sz).replace(/\B(?=(\d{3})+(?!\d))/g, " ");
|
||||
|
||||
html.push('<tr><td>' + sz +
|
||||
'</td><td>' + (f.un || '') +
|
||||
'</td><td>' + f.ip +
|
||||
'</td><td>' + ts +
|
||||
'</td><td>' + sa +
|
||||
|
|
Loading…
Reference in a new issue