mirror of
https://github.com/9001/copyparty.git
synced 2025-08-17 09:02:15 -06:00
recent-uploads: move rendering to js
* loads 50% faster, reducing server-load by 30% * inhibits search engines from indexing it * eyecandy (filter applies automatically on edit)
This commit is contained in:
parent
3bb7b677f8
commit
87598dcd7f
|
@ -5031,11 +5031,11 @@ class HttpCli(object):
|
||||||
ret.sort(key=lambda x: x["at"], reverse=True) # type: ignore
|
ret.sort(key=lambda x: x["at"], reverse=True) # type: ignore
|
||||||
ret = ret[:2000]
|
ret = ret[:2000]
|
||||||
|
|
||||||
|
ret.sort(key=lambda x: x["at"], reverse=True) # type: ignore
|
||||||
|
|
||||||
if len(ret) > 2000:
|
if len(ret) > 2000:
|
||||||
ret = ret[:2000]
|
ret = ret[:2000]
|
||||||
|
|
||||||
ret.sort(key=lambda x: x["at"], reverse=True) # type: ignore
|
|
||||||
|
|
||||||
for rv in ret:
|
for rv in ret:
|
||||||
rv["vp"] = quotep(rv["vp"])
|
rv["vp"] = quotep(rv["vp"])
|
||||||
nfk = rv.pop("nfk")
|
nfk = rv.pop("nfk")
|
||||||
|
@ -5124,10 +5124,6 @@ class HttpCli(object):
|
||||||
if not dots and "/." in vp:
|
if not dots and "/." in vp:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
n -= 1
|
|
||||||
if not n:
|
|
||||||
break
|
|
||||||
|
|
||||||
rv = {
|
rv = {
|
||||||
"vp": vp,
|
"vp": vp,
|
||||||
"sz": sz,
|
"sz": sz,
|
||||||
|
@ -5145,13 +5141,17 @@ class HttpCli(object):
|
||||||
ret.sort(key=lambda x: x["at"], reverse=True) # type: ignore
|
ret.sort(key=lambda x: x["at"], reverse=True) # type: ignore
|
||||||
ret = ret[:1000]
|
ret = ret[:1000]
|
||||||
|
|
||||||
if len(ret) > 1000:
|
n -= 1
|
||||||
ret = ret[:1000]
|
if not n:
|
||||||
|
break
|
||||||
|
|
||||||
ret.sort(key=lambda x: x["at"], reverse=True) # type: ignore
|
ret.sort(key=lambda x: x["at"], reverse=True) # type: ignore
|
||||||
|
|
||||||
|
if len(ret) > 1000:
|
||||||
|
ret = ret[:1000]
|
||||||
|
|
||||||
for rv in ret:
|
for rv in ret:
|
||||||
rv["evp"] = quotep(rv["vp"])
|
rv["vp"] = quotep(rv["vp"])
|
||||||
nfk = rv.pop("nfk")
|
nfk = rv.pop("nfk")
|
||||||
if not nfk:
|
if not nfk:
|
||||||
continue
|
continue
|
||||||
|
@ -5184,15 +5184,16 @@ class HttpCli(object):
|
||||||
for v in ret:
|
for v in ret:
|
||||||
v["vp"] = self.args.SR + v["vp"]
|
v["vp"] = self.args.SR + v["vp"]
|
||||||
|
|
||||||
self.log("%s #%d %.2fsec" % (lm, len(ret), time.time() - t0))
|
now = time.time()
|
||||||
|
self.log("%s #%d %.2fsec" % (lm, len(ret), now - t0))
|
||||||
|
|
||||||
|
ret2 = {"now": int(now), "filter": sfilt, "ups": ret}
|
||||||
|
jtxt = json.dumps(ret2, separators=(",\n", ": "))
|
||||||
if "j" in self.ouparam:
|
if "j" in self.ouparam:
|
||||||
jtxt = json.dumps(ret, separators=(",\n", ": "))
|
|
||||||
self.reply(jtxt.encode("utf-8", "replace"), mime="application/json")
|
self.reply(jtxt.encode("utf-8", "replace"), mime="application/json")
|
||||||
return True
|
return True
|
||||||
|
|
||||||
rows = [[x["vp"], x["evp"], x["sz"], x["ip"], x["at"]] for x in ret]
|
html = self.j2s("rups", this=self, v=jtxt)
|
||||||
html = self.j2s("rups", this=self, rows=rows, filt=sfilt, now=int(time.time()))
|
|
||||||
self.reply(html.encode("utf-8"), status=200)
|
self.reply(html.encode("utf-8"), status=200)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
|
@ -10,16 +10,10 @@ html {
|
||||||
padding: 0 1em 3em 1em;
|
padding: 0 1em 3em 1em;
|
||||||
line-height: 2.3em;
|
line-height: 2.3em;
|
||||||
}
|
}
|
||||||
form {
|
|
||||||
display: inline;
|
|
||||||
padding-left: 1em;
|
|
||||||
}
|
|
||||||
input[type=submit],
|
|
||||||
a {
|
a {
|
||||||
color: #047;
|
color: #047;
|
||||||
background: #fff;
|
background: #fff;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
border: none;
|
|
||||||
border-bottom: 1px solid #8ab;
|
border-bottom: 1px solid #8ab;
|
||||||
border-radius: .2em;
|
border-radius: .2em;
|
||||||
padding: .2em .6em;
|
padding: .2em .6em;
|
||||||
|
@ -89,7 +83,6 @@ html.bz {
|
||||||
background: #11121d;
|
background: #11121d;
|
||||||
color: #bbd;
|
color: #bbd;
|
||||||
}
|
}
|
||||||
html.z input[type=submit],
|
|
||||||
html.z a {
|
html.z a {
|
||||||
color: #fff;
|
color: #fff;
|
||||||
background: #057;
|
background: #057;
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
<title>{{ s_doctitle }}</title>
|
<title>{{ s_doctitle }}</title>
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=0.8">
|
<meta name="viewport" content="width=device-width, initial-scale=0.8">
|
||||||
|
<meta name="robots" content="noindex, nofollow">
|
||||||
<meta name="theme-color" content="#{{ tcolor }}">
|
<meta name="theme-color" content="#{{ tcolor }}">
|
||||||
<link rel="stylesheet" media="screen" href="{{ r }}/.cpr/rups.css?_={{ ts }}">
|
<link rel="stylesheet" media="screen" href="{{ r }}/.cpr/rups.css?_={{ ts }}">
|
||||||
<link rel="stylesheet" media="screen" href="{{ r }}/.cpr/ui.css?_={{ ts }}">
|
<link rel="stylesheet" media="screen" href="{{ r }}/.cpr/ui.css?_={{ ts }}">
|
||||||
|
@ -14,14 +15,10 @@
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<div id="wrap">
|
<div id="wrap">
|
||||||
<a id="a" href="{{ r }}/?ru" class="af">refresh</a>
|
<a href="#" id="re">refresh</a>
|
||||||
<a id="a" href="{{ r }}/?h" class="af">control-panel</a>
|
<a href="{{ r }}/?h">control-panel</a>
|
||||||
<form method="get" enctype="application/x-www-form-urlencoded" accept-charset="utf-8" action="{{ r }}">
|
Filter: <input type="text" id="filter" size="20" placeholder="documents/passwords" />
|
||||||
<input type="hidden" name="ru" value="a" />
|
<span id="hits"></span>
|
||||||
Filter: <input type="text" name="filter" size="20" placeholder="documents/passwords" value="{{ filt }}" />
|
|
||||||
<input type="submit" />
|
|
||||||
</form>
|
|
||||||
<span id="hits"></span>
|
|
||||||
<table id="tab"><thead><tr>
|
<table id="tab"><thead><tr>
|
||||||
<th>size</th>
|
<th>size</th>
|
||||||
<th>who</th>
|
<th>who</th>
|
||||||
|
@ -29,27 +26,12 @@
|
||||||
<th>age</th>
|
<th>age</th>
|
||||||
<th>dir</th>
|
<th>dir</th>
|
||||||
<th>file</th>
|
<th>file</th>
|
||||||
</tr></thead><tbody>
|
</tr></thead><tbody id="tb"></tbody></table>
|
||||||
{% for vp, evp, sz, ip, at in rows %}
|
|
||||||
<tr>
|
|
||||||
<td>{{ sz }}</td>
|
|
||||||
<td>{{ ip }}</td>
|
|
||||||
<td>{{ at }}</td>
|
|
||||||
<td>{{ (now-at) }}</td>
|
|
||||||
<td></td>
|
|
||||||
<td><a href="{{ r }}{{ evp }}">{{ vp|e }}</a></td>
|
|
||||||
</tr>
|
|
||||||
{% endfor %}
|
|
||||||
</tbody></table>
|
|
||||||
{% if not rows %}
|
|
||||||
(the database is not aware of any uploads)
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
</div>
|
||||||
<a href="#" id="repl">π</a>
|
<a href="#" id="repl">π</a>
|
||||||
<script>
|
<script>
|
||||||
|
|
||||||
var SR="{{ r }}",
|
var SR="{{ r }}",
|
||||||
NOW={{ now }},
|
|
||||||
lang="{{ lang }}",
|
lang="{{ lang }}",
|
||||||
dfavico="{{ favico }}";
|
dfavico="{{ favico }}";
|
||||||
|
|
||||||
|
@ -58,6 +40,7 @@ document.documentElement.className = (STG && STG.cpp_thm) || "{{ this.args.theme
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
<script src="{{ r }}/.cpr/util.js?_={{ ts }}"></script>
|
<script src="{{ r }}/.cpr/util.js?_={{ ts }}"></script>
|
||||||
|
<script>var V={{ v }};</script>
|
||||||
<script src="{{ r }}/.cpr/rups.js?_={{ ts }}"></script>
|
<script src="{{ r }}/.cpr/rups.js?_={{ ts }}"></script>
|
||||||
{%- if js %}
|
{%- if js %}
|
||||||
<script src="{{ js }}_={{ ts }}"></script>
|
<script src="{{ js }}_={{ ts }}"></script>
|
||||||
|
|
|
@ -1,34 +1,66 @@
|
||||||
(function() {
|
function render() {
|
||||||
var tab = ebi('tab').tBodies[0],
|
var ups = V.ups, now = V.now, html = [];
|
||||||
tr = Array.prototype.slice.call(tab.rows, 0),
|
ebi('filter').value = V.filter;
|
||||||
rows = [];
|
ebi('hits').innerHTML = 'showing ' + ups.length + ' files';
|
||||||
|
|
||||||
for (var a = 0; a < tr.length; a++) {
|
for (var a = 0; a < ups.length; a++) {
|
||||||
var td = tr[a].cells,
|
var f = ups[a],
|
||||||
an = td[5].children[0];
|
vsp = vsplit(f.vp.split('?')[0]),
|
||||||
|
dn = esc(uricom_dec(vsp[0])),
|
||||||
|
fn = esc(uricom_dec(vsp[1])),
|
||||||
|
at = f.at,
|
||||||
|
td = now - f.at,
|
||||||
|
ts = !at ? '(?)' : unix2iso(at),
|
||||||
|
sa = !at ? '(?)' : td > 60 ? shumantime(td) : (td + 's'),
|
||||||
|
sz = ('' + f.sz).replace(/\B(?=(\d{3})+(?!\d))/g, " ");
|
||||||
|
|
||||||
rows.push([
|
html.push('<tr><td>' + sz +
|
||||||
td[0].textContent,
|
'</td><td>' + f.ip +
|
||||||
td[2].textContent,
|
'</td><td>' + ts +
|
||||||
td[3].textContent,
|
'</td><td>' + sa +
|
||||||
an.textContent,
|
'</td><td><a href="' + vsp[0] + '">' + dn +
|
||||||
an.getAttribute('href'),
|
'</a></td><td><a href="' + f.vp + '">' + fn +
|
||||||
]);
|
'</a></td></tr>');
|
||||||
}
|
}
|
||||||
|
if (!ups.length) {
|
||||||
for (var a = 0; a < rows.length; a++) {
|
var t = V.filter ? ' matching the filter' : '';
|
||||||
var t = rows[a],
|
html = ['<tr><td colspan="6">there are no uploads' + t + '</td></tr>'];
|
||||||
sz = t[0],
|
|
||||||
at = parseInt(t[1]),
|
|
||||||
nam = vsplit(t[3]),
|
|
||||||
dh = vsplit(t[4])[0];
|
|
||||||
|
|
||||||
tr[a].cells[0].innerHTML = sz.replace(/\B(?=(\d{3})+(?!\d))/g, " ");
|
|
||||||
tr[a].cells[2].innerHTML = at ? unix2iso(at) : '(?)';
|
|
||||||
tr[a].cells[3].innerHTML = at ? shumantime(t[2]) : '(?)';
|
|
||||||
tr[a].cells[4].innerHTML = '<a href="' + dh + '">' + nam[0] + '</a>';
|
|
||||||
tr[a].cells[5].children[0].innerHTML = nam[1].split('?')[0];
|
|
||||||
}
|
}
|
||||||
|
ebi('tb').innerHTML = html.join('');
|
||||||
|
}
|
||||||
|
render();
|
||||||
|
|
||||||
ebi('hits').innerHTML = '-- showing ' + rows.length + ' files';
|
var ti;
|
||||||
})();
|
function ask(e) {
|
||||||
|
ev(e);
|
||||||
|
clearTimeout(ti);
|
||||||
|
ebi('hits').innerHTML = 'Loading...';
|
||||||
|
|
||||||
|
var xhr = new XHR(),
|
||||||
|
filter = unsmart(ebi('filter').value);
|
||||||
|
|
||||||
|
hist_replace(get_evpath().split('?')[0] + '?ru&filter=' + uricom_enc(filter));
|
||||||
|
|
||||||
|
xhr.onload = xhr.onerror = function () {
|
||||||
|
try {
|
||||||
|
V = JSON.parse(this.responseText)
|
||||||
|
}
|
||||||
|
catch (ex) {
|
||||||
|
ebi('tb').innerHTML = '<tr><td colspan="6">failed to decode server response as json: <pre>' + esc(this.responseText) + '</pre></td></tr>';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
render();
|
||||||
|
};
|
||||||
|
xhr.open('GET', SR + '/?ru&j&filter=' + uricom_enc(filter), true);
|
||||||
|
xhr.send();
|
||||||
|
}
|
||||||
|
ebi('re').onclick = ask;
|
||||||
|
ebi('filter').oninput = function () {
|
||||||
|
clearTimeout(ti);
|
||||||
|
ti = setTimeout(ask, 500);
|
||||||
|
ebi('hits').innerHTML = '...';
|
||||||
|
};
|
||||||
|
ebi('filter').onkeydown = function (e) {
|
||||||
|
if (('' + e.key).endsWith('Enter'))
|
||||||
|
ask();
|
||||||
|
};
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
<title>{{ s_doctitle }}</title>
|
<title>{{ s_doctitle }}</title>
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=0.8">
|
<meta name="viewport" content="width=device-width, initial-scale=0.8">
|
||||||
|
<meta name="robots" content="noindex, nofollow">
|
||||||
<meta name="theme-color" content="#{{ tcolor }}">
|
<meta name="theme-color" content="#{{ tcolor }}">
|
||||||
<link rel="stylesheet" media="screen" href="{{ r }}/.cpr/shares.css?_={{ ts }}">
|
<link rel="stylesheet" media="screen" href="{{ r }}/.cpr/shares.css?_={{ ts }}">
|
||||||
<link rel="stylesheet" media="screen" href="{{ r }}/.cpr/ui.css?_={{ ts }}">
|
<link rel="stylesheet" media="screen" href="{{ r }}/.cpr/ui.css?_={{ ts }}">
|
||||||
|
@ -14,8 +15,8 @@
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<div id="wrap">
|
<div id="wrap">
|
||||||
<a id="a" href="{{ r }}/?shares" class="af">refresh</a>
|
<a href="{{ r }}/?shares">refresh</a>
|
||||||
<a id="a" href="{{ r }}/?h" class="af">control-panel</a>
|
<a href="{{ r }}/?h">control-panel</a>
|
||||||
|
|
||||||
<span>axs = perms (read,write,move,delet)</span>
|
<span>axs = perms (read,write,move,delet)</span>
|
||||||
<span>nf = numFiles (0=dir)</span>
|
<span>nf = numFiles (0=dir)</span>
|
||||||
|
|
|
@ -275,8 +275,6 @@ class VHttpSrv(object):
|
||||||
self.gurl = Garda("")
|
self.gurl = Garda("")
|
||||||
|
|
||||||
self.u2idx = None
|
self.u2idx = None
|
||||||
self.ptn_cc = re.compile(r"[\x00-\x1f]")
|
|
||||||
self.uparam_cc_ok = set("doc move tree".split())
|
|
||||||
|
|
||||||
def cachebuster(self):
|
def cachebuster(self):
|
||||||
return "a"
|
return "a"
|
||||||
|
|
Loading…
Reference in a new issue