From 7ff46966dafb4901c001c19a6feb6b40190e15ec Mon Sep 17 00:00:00 2001
From: ed
Date: Mon, 19 Aug 2024 21:38:47 +0000
Subject: [PATCH] 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
---
README.md | 7 ++++--
copyparty/__main__.py | 4 ++--
copyparty/authsrv.py | 3 +--
copyparty/httpcli.py | 13 +++++++----
copyparty/svchub.py | 8 ++++++-
copyparty/web/browser.js | 18 +++++++++++----
copyparty/web/shares.css | 3 +++
copyparty/web/shares.html | 12 +++++-----
copyparty/web/shares.js | 18 +++++++++++++++
copyparty/web/splash.html | 48 +++++++++++++++++++++++++++------------
copyparty/web/splash.js | 5 ++--
11 files changed, 99 insertions(+), 40 deletions(-)
diff --git a/README.md b/README.md
index 68b03614..31f5172d 100644
--- a/README.md
+++ b/README.md
@@ -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
diff --git a/copyparty/__main__.py b/copyparty/__main__.py
index 08e34367..c1533a30 100644
--- a/copyparty/__main__.py
+++ b/copyparty/__main__.py
@@ -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")
diff --git a/copyparty/authsrv.py b/copyparty/authsrv.py
index d09f7089..592e7972 100644
--- a/copyparty/authsrv.py
+++ b/copyparty/authsrv.py
@@ -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
diff --git a/copyparty/httpcli.py b/copyparty/httpcli.py
index bd2c37fc..5432dcd3 100644
--- a/copyparty/httpcli.py
+++ b/copyparty/httpcli.py
@@ -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 = '404 not found ┐( ´ -`)┌
or maybe you don\'t have access -- try logging in or go home
' - pt = "404 not found ┐( ´ -`)┌ (or maybe you don't have access -- try logging in)" + t = 'or maybe you don\'t have access -- try a password or go home
' + pt = "404 not found ┐( ´ -`)┌ (or maybe you don't have access -- try a password)" elif is_403: - t = 'you\'ll have to log in or go home
' + t = 'use a password or go home
' 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 (?,?,?,?,?,?,?,?)" diff --git a/copyparty/svchub.py b/copyparty/svchub.py index b67359e3..3d18eacf 100644 --- a/copyparty/svchub.py +++ b/copyparty/svchub.py @@ -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 diff --git a/copyparty/web/browser.js b/copyparty/web/browser.js index ac01fa72..81c8a806 100644 --- a/copyparty/web/browser.js +++ b/copyparty/web/browser.js @@ -3820,13 +3820,14 @@ var fileman = (function () { '', '', '', - 'delete | sharekey | pw | @@ -33,7 +33,7 @@expires | min | hrs | -||
---|---|---|---|---|---|---|---|
delete | @@ -45,11 +45,11 @@{{ un|e }} | {{ t0 }} | {{ t1 }} | -{{ (t1 - now) // 60 if t1 else "never" }} | -{{ (t1 - now) // 3600 if t1 else "never" }} | +{{ ((t1 - now) / 60) | round(1) if t1 else "inf" }} | +{{ ((t1 - now) / 3600) | round(1) if t1 else "inf" }} |
welcome back, {{ this.uname|e }}
{%- endif %} + {%- endif %} {%- if msg %}