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:
ed 2024-12-20 23:52:03 +00:00
parent 3bb7b677f8
commit 87598dcd7f
6 changed files with 85 additions and 77 deletions

View file

@ -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

View file

@ -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;

View file

@ -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>
&nbsp; Filter: <input type="text" id="filter" size="20" placeholder="documents/passwords" />
&nbsp; <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>

View file

@ -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();
};

View file

@ -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>

View file

@ -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"