From fc88341820ebd2440b455db5b8dd3fb12aff96c9 Mon Sep 17 00:00:00 2001 From: ed Date: Wed, 26 Mar 2025 20:07:35 +0000 Subject: [PATCH] add option to store markdown backups elsewhere `--md-hist` / volflag `md_hist` specifies where to put old versions of markdown files when edited using the web-ui; * `s` = create `.hist` subfolder next to the markdown file (the default, both previously and now) * `v` = use the volume's hist-path, either according to `--hist` or the `hist` volflag. NOTE: old versions will not be retrievable through the web-ui * `n` = nope / disabled; overwrite without backup --- copyparty/__main__.py | 1 + copyparty/cfg.py | 2 ++ copyparty/httpcli.py | 43 ++++++++++++++++++++++++++++++------------- tests/util.py | 1 + 4 files changed, 34 insertions(+), 13 deletions(-) diff --git a/copyparty/__main__.py b/copyparty/__main__.py index c2b42e3e..687d09b0 100644 --- a/copyparty/__main__.py +++ b/copyparty/__main__.py @@ -1431,6 +1431,7 @@ def add_db_metadata(ap): def add_txt(ap): ap2 = ap.add_argument_group('textfile options') + ap2.add_argument("--md-hist", metavar="TXT", type=u, default="s", help="where to store old version of markdown files; [\033[32ms\033[0m]=subfolder, [\033[32mv\033[0m]=volume-histpath, [\033[32mn\033[0m]=nope/disabled (volflag=md_hist)") ap2.add_argument("-mcr", metavar="SEC", type=int, default=60, help="the textfile editor will check for serverside changes every \033[33mSEC\033[0m seconds") ap2.add_argument("-emp", action="store_true", help="enable markdown plugins -- neat but dangerous, big XSS risk") ap2.add_argument("--exp", action="store_true", help="enable textfile expansion -- replace {{self.ip}} and such; see \033[33m--help-exp\033[0m (volflag=exp)") diff --git a/copyparty/cfg.py b/copyparty/cfg.py index 89f0831f..8a44123e 100644 --- a/copyparty/cfg.py +++ b/copyparty/cfg.py @@ -83,6 +83,7 @@ def vf_vmap() -> dict[str, str]: "md_sbf", "lg_sba", "md_sba", + "md_hist", "nrand", "u2ow", "og_desc", @@ -291,6 +292,7 @@ flagcats = { "og_ua": "if defined: only send OG html if useragent matches this regex", }, "textfiles": { + "md_hist": "where to put markdown backups; s=subfolder, v=volHist, n=nope", "exp": "enable textfile expansion; see --help-exp", "exp_md": "placeholders to expand in markdown files; see --help", "exp_lg": "placeholders to expand in prologue/epilogue; see --help", diff --git a/copyparty/httpcli.py b/copyparty/httpcli.py index d2f9a30d..d9b1fd21 100644 --- a/copyparty/httpcli.py +++ b/copyparty/httpcli.py @@ -57,6 +57,7 @@ from .util import ( UnrecvEOF, WrongPostKey, absreal, + afsenc, alltrace, atomic_move, b64dec, @@ -3501,6 +3502,7 @@ class HttpCli(object): fp = os.path.join(fp, fn) rem = "{}/{}".format(rp, fn).strip("/") + dbv, vrem = vfs.get_dbv(rem) if not rem.endswith(".md") and not self.can_delete: raise Pebkac(400, "only markdown pls") @@ -3555,13 +3557,27 @@ class HttpCli(object): mdir, mfile = os.path.split(fp) fname, fext = mfile.rsplit(".", 1) if "." in mfile else (mfile, "md") mfile2 = "{}.{:.3f}.{}".format(fname, srv_lastmod, fext) - try: + + dp = "" + hist_cfg = dbv.flags["md_hist"] + if hist_cfg == "v": + vrd = vsplit(vrem)[0] + zb = hashlib.sha512(afsenc(vrd)).digest() + zs = ub64enc(zb).decode("ascii")[:24].lower() + dp = "%s/md/%s/%s/%s" % (dbv.histpath, zs[:2], zs[2:4], zs) + self.log("moving old version to %s/%s" % (dp, mfile2)) + if bos.makedirs(dp): + with open(os.path.join(dp, "dir.txt"), "wb") as f: + f.write(afsenc(vrd)) + elif hist_cfg == "s": dp = os.path.join(mdir, ".hist") - bos.mkdir(dp) - hidedir(dp) - except: - pass - wrename(self.log, fp, os.path.join(mdir, ".hist", mfile2), vfs.flags) + try: + bos.mkdir(dp) + hidedir(dp) + except: + pass + if dp: + wrename(self.log, fp, os.path.join(dp, mfile2), vfs.flags) assert self.parser.gen # !rm p_field, _, p_data = next(self.parser.gen) @@ -3634,13 +3650,12 @@ class HttpCli(object): wunlink(self.log, fp, vfs.flags) raise Pebkac(403, t) - vfs, rem = vfs.get_dbv(rem) self.conn.hsrv.broker.say( "up2k.hash_file", - vfs.realpath, - vfs.vpath, - vfs.flags, - vsplit(rem)[0], + dbv.realpath, + dbv.vpath, + dbv.flags, + vsplit(vrem)[0], fn, self.ip, new_lastmod, @@ -6020,9 +6035,11 @@ class HttpCli(object): # check for old versions of files, # [num-backups, most-recent, hist-path] hist: dict[str, tuple[int, float, str]] = {} - histdir = os.path.join(fsroot, ".hist") - ptn = RE_MDV try: + if vf["md_hist"] != "s": + raise Exception() + histdir = os.path.join(fsroot, ".hist") + ptn = RE_MDV for hfn in bos.listdir(histdir): m = ptn.match(hfn) if not m: diff --git a/tests/util.py b/tests/util.py index 075c5d2e..72beb006 100644 --- a/tests/util.py +++ b/tests/util.py @@ -174,6 +174,7 @@ class Cfg(Namespace): lang="eng", log_badpwd=1, logout=573, + md_hist="s", mte={"a": True}, mth={}, mtp=[],