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 = ret[:2000]
|
||||
|
||||
ret.sort(key=lambda x: x["at"], reverse=True) # type: ignore
|
||||
|
||||
if len(ret) > 2000:
|
||||
ret = ret[:2000]
|
||||
|
||||
ret.sort(key=lambda x: x["at"], reverse=True) # type: ignore
|
||||
|
||||
for rv in ret:
|
||||
rv["vp"] = quotep(rv["vp"])
|
||||
nfk = rv.pop("nfk")
|
||||
|
@ -5124,10 +5124,6 @@ class HttpCli(object):
|
|||
if not dots and "/." in vp:
|
||||
continue
|
||||
|
||||
n -= 1
|
||||
if not n:
|
||||
break
|
||||
|
||||
rv = {
|
||||
"vp": vp,
|
||||
"sz": sz,
|
||||
|
@ -5145,13 +5141,17 @@ class HttpCli(object):
|
|||
ret.sort(key=lambda x: x["at"], reverse=True) # type: ignore
|
||||
ret = ret[:1000]
|
||||
|
||||
if len(ret) > 1000:
|
||||
ret = ret[:1000]
|
||||
n -= 1
|
||||
if not n:
|
||||
break
|
||||
|
||||
ret.sort(key=lambda x: x["at"], reverse=True) # type: ignore
|
||||
|
||||
if len(ret) > 1000:
|
||||
ret = ret[:1000]
|
||||
|
||||
for rv in ret:
|
||||
rv["evp"] = quotep(rv["vp"])
|
||||
rv["vp"] = quotep(rv["vp"])
|
||||
nfk = rv.pop("nfk")
|
||||
if not nfk:
|
||||
continue
|
||||
|
@ -5184,15 +5184,16 @@ class HttpCli(object):
|
|||
for v in ret:
|
||||
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:
|
||||
jtxt = json.dumps(ret, separators=(",\n", ": "))
|
||||
self.reply(jtxt.encode("utf-8", "replace"), mime="application/json")
|
||||
return True
|
||||
|
||||
rows = [[x["vp"], x["evp"], x["sz"], x["ip"], x["at"]] for x in ret]
|
||||
html = self.j2s("rups", this=self, rows=rows, filt=sfilt, now=int(time.time()))
|
||||
html = self.j2s("rups", this=self, v=jtxt)
|
||||
self.reply(html.encode("utf-8"), status=200)
|
||||
return True
|
||||
|
||||
|
|
|
@ -10,16 +10,10 @@ html {
|
|||
padding: 0 1em 3em 1em;
|
||||
line-height: 2.3em;
|
||||
}
|
||||
form {
|
||||
display: inline;
|
||||
padding-left: 1em;
|
||||
}
|
||||
input[type=submit],
|
||||
a {
|
||||
color: #047;
|
||||
background: #fff;
|
||||
text-decoration: none;
|
||||
border: none;
|
||||
border-bottom: 1px solid #8ab;
|
||||
border-radius: .2em;
|
||||
padding: .2em .6em;
|
||||
|
@ -89,7 +83,6 @@ html.bz {
|
|||
background: #11121d;
|
||||
color: #bbd;
|
||||
}
|
||||
html.z input[type=submit],
|
||||
html.z a {
|
||||
color: #fff;
|
||||
background: #057;
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
<title>{{ s_doctitle }}</title>
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=0.8">
|
||||
<meta name="robots" content="noindex, nofollow">
|
||||
<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/ui.css?_={{ ts }}">
|
||||
|
@ -14,14 +15,10 @@
|
|||
|
||||
<body>
|
||||
<div id="wrap">
|
||||
<a id="a" href="{{ r }}/?ru" class="af">refresh</a>
|
||||
<a id="a" href="{{ r }}/?h" class="af">control-panel</a>
|
||||
<form method="get" enctype="application/x-www-form-urlencoded" accept-charset="utf-8" action="{{ r }}">
|
||||
<input type="hidden" name="ru" value="a" />
|
||||
Filter: <input type="text" name="filter" size="20" placeholder="documents/passwords" value="{{ filt }}" />
|
||||
<input type="submit" />
|
||||
</form>
|
||||
<span id="hits"></span>
|
||||
<a href="#" id="re">refresh</a>
|
||||
<a href="{{ r }}/?h">control-panel</a>
|
||||
Filter: <input type="text" id="filter" size="20" placeholder="documents/passwords" />
|
||||
<span id="hits"></span>
|
||||
<table id="tab"><thead><tr>
|
||||
<th>size</th>
|
||||
<th>who</th>
|
||||
|
@ -29,27 +26,12 @@
|
|||
<th>age</th>
|
||||
<th>dir</th>
|
||||
<th>file</th>
|
||||
</tr></thead><tbody>
|
||||
{% 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 %}
|
||||
</tr></thead><tbody id="tb"></tbody></table>
|
||||
</div>
|
||||
<a href="#" id="repl">π</a>
|
||||
<script>
|
||||
|
||||
var SR="{{ r }}",
|
||||
NOW={{ now }},
|
||||
lang="{{ lang }}",
|
||||
dfavico="{{ favico }}";
|
||||
|
||||
|
@ -58,6 +40,7 @@ document.documentElement.className = (STG && STG.cpp_thm) || "{{ this.args.theme
|
|||
|
||||
</script>
|
||||
<script src="{{ r }}/.cpr/util.js?_={{ ts }}"></script>
|
||||
<script>var V={{ v }};</script>
|
||||
<script src="{{ r }}/.cpr/rups.js?_={{ ts }}"></script>
|
||||
{%- if js %}
|
||||
<script src="{{ js }}_={{ ts }}"></script>
|
||||
|
|
|
@ -1,34 +1,66 @@
|
|||
(function() {
|
||||
var tab = ebi('tab').tBodies[0],
|
||||
tr = Array.prototype.slice.call(tab.rows, 0),
|
||||
rows = [];
|
||||
function render() {
|
||||
var ups = V.ups, now = V.now, html = [];
|
||||
ebi('filter').value = V.filter;
|
||||
ebi('hits').innerHTML = 'showing ' + ups.length + ' files';
|
||||
|
||||
for (var a = 0; a < tr.length; a++) {
|
||||
var td = tr[a].cells,
|
||||
an = td[5].children[0];
|
||||
for (var a = 0; a < ups.length; a++) {
|
||||
var f = ups[a],
|
||||
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([
|
||||
td[0].textContent,
|
||||
td[2].textContent,
|
||||
td[3].textContent,
|
||||
an.textContent,
|
||||
an.getAttribute('href'),
|
||||
]);
|
||||
html.push('<tr><td>' + sz +
|
||||
'</td><td>' + f.ip +
|
||||
'</td><td>' + ts +
|
||||
'</td><td>' + sa +
|
||||
'</td><td><a href="' + vsp[0] + '">' + dn +
|
||||
'</a></td><td><a href="' + f.vp + '">' + fn +
|
||||
'</a></td></tr>');
|
||||
}
|
||||
|
||||
for (var a = 0; a < rows.length; a++) {
|
||||
var t = rows[a],
|
||||
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];
|
||||
if (!ups.length) {
|
||||
var t = V.filter ? ' matching the filter' : '';
|
||||
html = ['<tr><td colspan="6">there are no uploads' + t + '</td></tr>'];
|
||||
}
|
||||
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>
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=0.8">
|
||||
<meta name="robots" content="noindex, nofollow">
|
||||
<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/ui.css?_={{ ts }}">
|
||||
|
@ -14,8 +15,8 @@
|
|||
|
||||
<body>
|
||||
<div id="wrap">
|
||||
<a id="a" href="{{ r }}/?shares" class="af">refresh</a>
|
||||
<a id="a" href="{{ r }}/?h" class="af">control-panel</a>
|
||||
<a href="{{ r }}/?shares">refresh</a>
|
||||
<a href="{{ r }}/?h">control-panel</a>
|
||||
|
||||
<span>axs = perms (read,write,move,delet)</span>
|
||||
<span>nf = numFiles (0=dir)</span>
|
||||
|
|
|
@ -275,8 +275,6 @@ class VHttpSrv(object):
|
|||
self.gurl = Garda("")
|
||||
|
||||
self.u2idx = None
|
||||
self.ptn_cc = re.compile(r"[\x00-\x1f]")
|
||||
self.uparam_cc_ok = set("doc move tree".split())
|
||||
|
||||
def cachebuster(self):
|
||||
return "a"
|
||||
|
|
Loading…
Reference in a new issue