mirror of
https://github.com/9001/copyparty.git
synced 2025-08-17 00:52:16 -06:00
fix some issues with shares mentioned in #84;
* crash when root volume is unmapped * rephrase login-page for shares * add chrome support (lol) * fix confusing helptext * improve ux * placeholders in share creator * button to disable expiration in share creator * human-readable timestamps in share listing
This commit is contained in:
parent
fca70b3508
commit
7ff46966da
|
@ -757,7 +757,7 @@ this feature was made with [identity providers](#identity-providers) in mind --
|
|||
when creating a share, the creator can choose any of the following options:
|
||||
|
||||
* password-protection
|
||||
* expire after a certain time
|
||||
* expire after a certain time; `0` or blank means infinite
|
||||
* allow visitors to upload (if the user who creates the share has write-access)
|
||||
|
||||
semi-intentional limitations:
|
||||
|
@ -768,7 +768,10 @@ semi-intentional limitations:
|
|||
* when linking something to discord (for example) it'll get accessed by their scraper and that would count as a hit
|
||||
* browsers wouldn't be able to resume a broken download unless the requester's IP gets allowlisted for X minutes (ref. tricky)
|
||||
|
||||
the links are created inside a specific toplevel folder which must be specified with server-config `--shr`, for example `--shr /share/` (this also enables the feature)
|
||||
specify `--shr /foobar` to enable this feature; a toplevel virtual folder named `foobar` is then created, and that's where all the shares will be served from
|
||||
|
||||
* you can name it whatever, `foobar` is just an example
|
||||
* if you're using config files, put `shr: /foobar` inside the `[global]` section instead
|
||||
|
||||
users can delete their own shares in the controlpanel, and a list of privileged users (`--shr-adm`) are allowed to see and/or delet any share on the server
|
||||
|
||||
|
|
|
@ -975,8 +975,8 @@ def add_fs(ap):
|
|||
def add_share(ap):
|
||||
db_path = os.path.join(E.cfg, "shares.db")
|
||||
ap2 = ap.add_argument_group('share-url options')
|
||||
ap2.add_argument("--shr", metavar="URL", default="", help="base url for shared files, for example [\033[32m/share\033[0m] (must be a toplevel subfolder)")
|
||||
ap2.add_argument("--shr-db", metavar="PATH", default=db_path, help="database to store shares in")
|
||||
ap2.add_argument("--shr", metavar="DIR", default="", help="toplevel virtual folder for shared files/folders, for example [\033[32m/share\033[0m]")
|
||||
ap2.add_argument("--shr-db", metavar="FILE", default=db_path, help="database to store shares in")
|
||||
ap2.add_argument("--shr-adm", metavar="U,U", default="", help="comma-separated list of users allowed to view/delete any share")
|
||||
ap2.add_argument("--shr-v", action="store_true", help="debug")
|
||||
|
||||
|
|
|
@ -1508,7 +1508,6 @@ class AuthSrv(object):
|
|||
import sqlite3
|
||||
|
||||
shv = VFS(self.log_func, "", shr, AXS(), {"d2d": True})
|
||||
par = vfs.all_vols[""]
|
||||
|
||||
db_path = self.args.shr_db
|
||||
db = sqlite3.connect(db_path)
|
||||
|
@ -1539,7 +1538,7 @@ class AuthSrv(object):
|
|||
# don't know the abspath yet + wanna ensure the user
|
||||
# still has the privs they granted, so nullmap it
|
||||
shv.nodes[s_k] = VFS(
|
||||
self.log_func, "", "%s/%s" % (shr, s_k), s_axs, par.flags.copy()
|
||||
self.log_func, "", "%s/%s" % (shr, s_k), s_axs, shv.flags.copy()
|
||||
)
|
||||
|
||||
vfs.nodes[shr] = vfs.all_vols[shr] = shv
|
||||
|
|
|
@ -3958,6 +3958,7 @@ class HttpCli(object):
|
|||
rvol=rvol,
|
||||
wvol=wvol,
|
||||
avol=avol,
|
||||
in_shr=self.args.shr and self.vpath.startswith(self.args.shr[1:]),
|
||||
vstate=vstate,
|
||||
scanning=vs["scanning"],
|
||||
hashq=vs["hashq"],
|
||||
|
@ -4006,10 +4007,10 @@ class HttpCli(object):
|
|||
def tx_404(self, is_403: bool = False) -> bool:
|
||||
rc = 404
|
||||
if self.args.vague_403:
|
||||
t = '<h1 id="n">404 not found ┐( ´ -`)┌</h1><p id="o">or maybe you don\'t have access -- try logging in or <a href="{}/?h">go home</a></p>'
|
||||
pt = "404 not found ┐( ´ -`)┌ (or maybe you don't have access -- try logging in)"
|
||||
t = '<h1 id="n">404 not found ┐( ´ -`)┌</h1><p id="o">or maybe you don\'t have access -- try a password or <a href="{}/?h">go home</a></p>'
|
||||
pt = "404 not found ┐( ´ -`)┌ (or maybe you don't have access -- try a password)"
|
||||
elif is_403:
|
||||
t = '<h1 id="p">403 forbiddena ~┻━┻</h1><p id="q">you\'ll have to log in or <a href="{}/?h">go home</a></p>'
|
||||
t = '<h1 id="p">403 forbiddena ~┻━┻</h1><p id="q">use a password or <a href="{}/?h">go home</a></p>'
|
||||
pt = "403 forbiddena ~┻━┻ (you'll have to log in)"
|
||||
rc = 403
|
||||
else:
|
||||
|
@ -4026,7 +4027,8 @@ class HttpCli(object):
|
|||
|
||||
t = t.format(self.args.SR)
|
||||
qv = quotep(self.vpaths) + self.ourlq()
|
||||
html = self.j2s("splash", this=self, qvpath=qv, msg=t)
|
||||
in_shr = self.args.shr and self.vpath.startswith(self.args.shr[1:])
|
||||
html = self.j2s("splash", this=self, qvpath=qv, in_shr=in_shr, msg=t)
|
||||
self.reply(html.encode("utf-8"), status=rc)
|
||||
return True
|
||||
|
||||
|
@ -4382,7 +4384,8 @@ class HttpCli(object):
|
|||
pw = req.get("pw") or ""
|
||||
now = int(time.time())
|
||||
sexp = req["exp"]
|
||||
exp = now + int(sexp) * 60 if sexp else 0
|
||||
exp = int(sexp) if sexp else 0
|
||||
exp = now + exp * 60 if exp else 0
|
||||
pr = "".join(zc for zc, zb in zip("rwmd", (s_rd, s_wr, s_mv, s_del)) if zb)
|
||||
|
||||
q = "insert into sh values (?,?,?,?,?,?,?,?)"
|
||||
|
|
|
@ -376,7 +376,13 @@ class SvcHub(object):
|
|||
|
||||
import sqlite3
|
||||
|
||||
al.shr = "/%s/" % (al.shr.strip("/"))
|
||||
al.shr = al.shr.strip("/")
|
||||
if "/" in al.shr:
|
||||
t = "config error: --shr must be the name of a virtual toplevel directory to put shares inside"
|
||||
self.log("root", t, 1)
|
||||
raise Exception(t)
|
||||
|
||||
al.shr = "/%s/" % (al.shr,)
|
||||
|
||||
create = True
|
||||
db_path = self.args.shr_db
|
||||
|
|
|
@ -3820,13 +3820,14 @@ var fileman = (function () {
|
|||
'<button id="sh_rand">🎲 random</button>',
|
||||
'<button id="sh_apply">✅ create share</button>',
|
||||
'</td></tr>',
|
||||
'<tr><td>name</td><td><input type="text" id="sh_k" ' + NOAC + ' tt="name your link" /></td></tr>',
|
||||
'<tr><td>name</td><td><input type="text" id="sh_k" ' + NOAC + ' placeholder="optional link name; will be random if blank" /></td></tr>',
|
||||
'<tr><td>source</td><td><input type="text" id="sh_vp" ' + NOAC + ' readonly tt="the file or folder to share" /></td></tr>',
|
||||
'<tr><td>passwd</td><td><input type="text" id="sh_pw" ' + NOAC + ' tt="optional password" /></td></tr>',
|
||||
'<tr><td>passwd</td><td><input type="text" id="sh_pw" ' + NOAC + ' placeholder="optional password" /></td></tr>',
|
||||
'<tr><td>expiry</td><td class="exs">',
|
||||
'<input type="text" id="sh_exm" ' + NOAC + ' /> min / ',
|
||||
'<input type="text" id="sh_exh" ' + NOAC + ' /> hours / ',
|
||||
'<input type="text" id="sh_exd" ' + NOAC + ' /> days',
|
||||
'<input type="text" id="sh_exd" ' + NOAC + ' /> days / ',
|
||||
'<button id="sh_noex">never</button>',
|
||||
'</td></tr>',
|
||||
'<tr><td>perms</td><td class="sh_axs">',
|
||||
];
|
||||
|
@ -3840,11 +3841,12 @@ var fileman = (function () {
|
|||
var sh_rand = ebi('sh_rand'),
|
||||
sh_abrt = ebi('sh_abrt'),
|
||||
sh_apply = ebi('sh_apply'),
|
||||
sh_noex = ebi('sh_noex'),
|
||||
exm = ebi('sh_exm'),
|
||||
exh = ebi('sh_exh'),
|
||||
exd = ebi('sh_exd'),
|
||||
sh_k = ebi('sh_k'),
|
||||
sh_vp = ebi('sh_vp');
|
||||
sh_vp = ebi('sh_vp'),
|
||||
sh_pw = ebi('sh_pw');
|
||||
|
||||
function setexp(a, b) {
|
||||
|
@ -3870,6 +3872,9 @@ var fileman = (function () {
|
|||
exm.onfocus = exh.onfocus = exd.onfocus = function () {
|
||||
this.value = '';
|
||||
};
|
||||
sh_noex.onclick = function () {
|
||||
setexp(0, 1);
|
||||
};
|
||||
exm.onblur = exh.onblur = exd.onblur = setdef;
|
||||
|
||||
exm.onkeydown = exh.onkeydown = exd.onkeydown =
|
||||
|
@ -3910,6 +3915,7 @@ var fileman = (function () {
|
|||
};
|
||||
|
||||
function shr_cb() {
|
||||
toast.hide();
|
||||
if (this.status !== 201) {
|
||||
shui.style.display = 'block';
|
||||
var msg = unpre(this.responseText);
|
||||
|
@ -3919,7 +3925,7 @@ var fileman = (function () {
|
|||
var surl = this.responseText;
|
||||
modal.confirm(L.fs_ok + esc(surl), function() {
|
||||
cliptxt(surl, function () {
|
||||
toast.ok(1, 'copied to clipboard');
|
||||
toast.ok(2, 'copied to clipboard');
|
||||
});
|
||||
});
|
||||
}
|
||||
|
@ -3934,6 +3940,8 @@ var fileman = (function () {
|
|||
plist.push(pbtns[a].textContent);
|
||||
|
||||
shui.style.display = 'none';
|
||||
toast.inf(30, "creating share...");
|
||||
|
||||
var body = {
|
||||
"k": sh_k.value,
|
||||
"vp": sh_vp.value,
|
||||
|
|
|
@ -58,6 +58,9 @@ td, th {
|
|||
text-align: left;
|
||||
white-space: nowrap;
|
||||
}
|
||||
td+td+td+td+td+td+td+td {
|
||||
font-family: var(--font-mono), monospace, monospace;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -15,13 +15,13 @@
|
|||
<body>
|
||||
<div id="wrap">
|
||||
<a id="a" href="{{ r }}/?shares" class="af">refresh</a>
|
||||
<a id="a" href="{{ r }}/?h" class="af">controlpanel</a>
|
||||
<a id="a" href="{{ r }}/?h" class="af">control-panel</a>
|
||||
|
||||
<span>axs = perms (read,write,move,delet)</span>
|
||||
<span>st 1=file 2=dir</span>
|
||||
<span>min/hrs = time left</span>
|
||||
|
||||
<table><tr>
|
||||
<table id="tab"><thead><tr>
|
||||
<th>delete</th>
|
||||
<th>sharekey</th>
|
||||
<th>pw</th>
|
||||
|
@ -33,7 +33,7 @@
|
|||
<th>expires</th>
|
||||
<th>min</th>
|
||||
<th>hrs</th>
|
||||
</tr>
|
||||
</tr></thead><tbody>
|
||||
{% for k, pw, vp, pr, st, un, t0, t1 in rows %}
|
||||
<tr>
|
||||
<td><a href="#" k="{{ k }}">delete</a></td>
|
||||
|
@ -45,11 +45,11 @@
|
|||
<td>{{ un|e }}</td>
|
||||
<td>{{ t0 }}</td>
|
||||
<td>{{ t1 }}</td>
|
||||
<td>{{ (t1 - now) // 60 if t1 else "never" }}</td>
|
||||
<td>{{ (t1 - now) // 3600 if t1 else "never" }}</td>
|
||||
<td>{{ ((t1 - now) / 60) | round(1) if t1 else "inf" }}</td>
|
||||
<td>{{ ((t1 - now) / 3600) | round(1) if t1 else "inf" }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
</tbody></table>
|
||||
{% if not rows %}
|
||||
(you don't have any active shares btw)
|
||||
{% endif %}
|
||||
|
|
|
@ -17,3 +17,21 @@ function cb() {
|
|||
|
||||
document.location = '?shares';
|
||||
}
|
||||
|
||||
(function() {
|
||||
var tab = ebi('tab').tBodies[0],
|
||||
tr = Array.prototype.slice.call(tab.rows, 0);
|
||||
|
||||
var buf = [];
|
||||
for (var a = 0; a < tr.length; a++)
|
||||
for (var b = 7; b < 9; b++)
|
||||
buf.push(parseInt(tr[a].cells[b].innerHTML));
|
||||
|
||||
var ibuf = 0;
|
||||
for (var a = 0; a < tr.length; a++)
|
||||
for (var b = 7; b < 9; b++) {
|
||||
var v = buf[ibuf++];
|
||||
tr[a].cells[b].innerHTML =
|
||||
v ? unix2iso(v).replace(' ', ', ') : 'never';
|
||||
}
|
||||
})();
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
|
||||
<body>
|
||||
<div id="wrap">
|
||||
{%- if not in_shr %}
|
||||
<a id="a" href="{{ r }}/?h" class="af">refresh</a>
|
||||
<a id="v" href="{{ r }}/?hc" class="af">connect</a>
|
||||
|
||||
|
@ -23,6 +24,7 @@
|
|||
<a id="c" href="{{ r }}/?pw=x" class="logout">logout</a>
|
||||
<p><span id="m">welcome back,</span> <strong>{{ this.uname|e }}</strong></p>
|
||||
{%- endif %}
|
||||
{%- endif %}
|
||||
|
||||
{%- if msg %}
|
||||
<div id="msg">
|
||||
|
@ -76,6 +78,37 @@
|
|||
</ul>
|
||||
{%- endif %}
|
||||
|
||||
{%- if in_shr %}
|
||||
<h1 id="z">unlock this share:</h1>
|
||||
<div>
|
||||
<form id="lf" method="post" enctype="multipart/form-data" action="{{ r }}/{{ qvpath }}">
|
||||
<input type="hidden" id="la" name="act" value="login" />
|
||||
<input type="password" id="lp" name="cppwd" placeholder=" password" />
|
||||
<input type="hidden" name="uhash" id="uhash" value="x" />
|
||||
<input type="submit" id="ls" value="Unlock" />
|
||||
{% if ahttps %}
|
||||
<a id="w" href="{{ ahttps }}">switch to https</a>
|
||||
{% endif %}
|
||||
</form>
|
||||
</div>
|
||||
{%- else %}
|
||||
<h1 id="l">login for more:</h1>
|
||||
<div>
|
||||
<form id="lf" method="post" enctype="multipart/form-data" action="{{ r }}/{{ qvpath }}">
|
||||
<input type="hidden" id="la" name="act" value="login" />
|
||||
<input type="password" id="lp" name="cppwd" placeholder=" password" />
|
||||
<input type="hidden" name="uhash" id="uhash" value="x" />
|
||||
<input type="submit" id="ls" value="Login" />
|
||||
{% if chpw %}
|
||||
<a id="x" href="#">change password</a>
|
||||
{% endif %}
|
||||
{% if ahttps %}
|
||||
<a id="w" href="{{ ahttps }}">switch to https</a>
|
||||
{% endif %}
|
||||
</form>
|
||||
</div>
|
||||
{%- endif %}
|
||||
|
||||
<h1 id="cc">other stuff:</h1>
|
||||
<ul>
|
||||
{%- if this.uname != '*' and this.args.shr %}
|
||||
|
@ -94,21 +127,6 @@
|
|||
<li><a id="k" href="{{ r }}/?reset" class="r" onclick="localStorage.clear();return true">reset client settings</a></li>
|
||||
</ul>
|
||||
|
||||
<h1 id="l">login for more:</h1>
|
||||
<div>
|
||||
<form id="lf" method="post" enctype="multipart/form-data" action="{{ r }}/{{ qvpath }}">
|
||||
<input type="hidden" id="la" name="act" value="login" />
|
||||
<input type="password" id="lp" name="cppwd" placeholder=" password" />
|
||||
<input type="hidden" name="uhash" id="uhash" value="x" />
|
||||
<input type="submit" id="ls" value="Login" />
|
||||
{% if chpw %}
|
||||
<a id="x" href="#">change password</a>
|
||||
{% endif %}
|
||||
{% if ahttps %}
|
||||
<a id="w" href="{{ ahttps }}">switch to https</a>
|
||||
{% endif %}
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<a href="#" id="repl">π</a>
|
||||
{%- if not this.args.nb %}
|
||||
|
|
|
@ -17,9 +17,9 @@ var Ls = {
|
|||
"l1": "logg inn:",
|
||||
"m1": "velkommen tilbake,",
|
||||
"n1": "404: filen finnes ikke ┐( ´ -`)┌",
|
||||
"o1": 'eller kanskje du ikke har tilgang? prøv å logge inn eller <a href="' + SR + '/?h">gå hjem</a>',
|
||||
"o1": 'eller kanskje du ikke har tilgang? prøv et passord eller <a href="' + SR + '/?h">gå hjem</a>',
|
||||
"p1": "403: tilgang nektet ~┻━┻",
|
||||
"q1": 'du må logge inn eller <a href="' + SR + '/?h">gå hjem</a>',
|
||||
"q1": 'prøv et passord eller <a href="' + SR + '/?h">gå hjem</a>',
|
||||
"r1": "gå hjem",
|
||||
".s1": "kartlegg",
|
||||
"t1": "handling",
|
||||
|
@ -29,6 +29,7 @@ var Ls = {
|
|||
"w1": "bytt til https",
|
||||
"x1": "bytt passord",
|
||||
"y1": "dine delinger",
|
||||
"z1": "lås opp område",
|
||||
"ta1": "du må skrive et nytt passord først",
|
||||
"ta2": "gjenta for å bekrefte nytt passord:",
|
||||
"ta3": "fant en skrivefeil; vennligst prøv igjen",
|
||||
|
|
Loading…
Reference in a new issue