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
This commit is contained in:
ed 2025-03-26 20:07:35 +00:00
parent 43bbd566d7
commit fc88341820
4 changed files with 34 additions and 13 deletions

View file

@ -1431,6 +1431,7 @@ def add_db_metadata(ap):
def add_txt(ap): def add_txt(ap):
ap2 = ap.add_argument_group('textfile options') 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("-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("-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)") 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)")

View file

@ -83,6 +83,7 @@ def vf_vmap() -> dict[str, str]:
"md_sbf", "md_sbf",
"lg_sba", "lg_sba",
"md_sba", "md_sba",
"md_hist",
"nrand", "nrand",
"u2ow", "u2ow",
"og_desc", "og_desc",
@ -291,6 +292,7 @@ flagcats = {
"og_ua": "if defined: only send OG html if useragent matches this regex", "og_ua": "if defined: only send OG html if useragent matches this regex",
}, },
"textfiles": { "textfiles": {
"md_hist": "where to put markdown backups; s=subfolder, v=volHist, n=nope",
"exp": "enable textfile expansion; see --help-exp", "exp": "enable textfile expansion; see --help-exp",
"exp_md": "placeholders to expand in markdown files; see --help", "exp_md": "placeholders to expand in markdown files; see --help",
"exp_lg": "placeholders to expand in prologue/epilogue; see --help", "exp_lg": "placeholders to expand in prologue/epilogue; see --help",

View file

@ -57,6 +57,7 @@ from .util import (
UnrecvEOF, UnrecvEOF,
WrongPostKey, WrongPostKey,
absreal, absreal,
afsenc,
alltrace, alltrace,
atomic_move, atomic_move,
b64dec, b64dec,
@ -3501,6 +3502,7 @@ class HttpCli(object):
fp = os.path.join(fp, fn) fp = os.path.join(fp, fn)
rem = "{}/{}".format(rp, fn).strip("/") rem = "{}/{}".format(rp, fn).strip("/")
dbv, vrem = vfs.get_dbv(rem)
if not rem.endswith(".md") and not self.can_delete: if not rem.endswith(".md") and not self.can_delete:
raise Pebkac(400, "only markdown pls") raise Pebkac(400, "only markdown pls")
@ -3555,13 +3557,27 @@ class HttpCli(object):
mdir, mfile = os.path.split(fp) mdir, mfile = os.path.split(fp)
fname, fext = mfile.rsplit(".", 1) if "." in mfile else (mfile, "md") fname, fext = mfile.rsplit(".", 1) if "." in mfile else (mfile, "md")
mfile2 = "{}.{:.3f}.{}".format(fname, srv_lastmod, fext) 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") dp = os.path.join(mdir, ".hist")
bos.mkdir(dp) try:
hidedir(dp) bos.mkdir(dp)
except: hidedir(dp)
pass except:
wrename(self.log, fp, os.path.join(mdir, ".hist", mfile2), vfs.flags) pass
if dp:
wrename(self.log, fp, os.path.join(dp, mfile2), vfs.flags)
assert self.parser.gen # !rm assert self.parser.gen # !rm
p_field, _, p_data = next(self.parser.gen) p_field, _, p_data = next(self.parser.gen)
@ -3634,13 +3650,12 @@ class HttpCli(object):
wunlink(self.log, fp, vfs.flags) wunlink(self.log, fp, vfs.flags)
raise Pebkac(403, t) raise Pebkac(403, t)
vfs, rem = vfs.get_dbv(rem)
self.conn.hsrv.broker.say( self.conn.hsrv.broker.say(
"up2k.hash_file", "up2k.hash_file",
vfs.realpath, dbv.realpath,
vfs.vpath, dbv.vpath,
vfs.flags, dbv.flags,
vsplit(rem)[0], vsplit(vrem)[0],
fn, fn,
self.ip, self.ip,
new_lastmod, new_lastmod,
@ -6020,9 +6035,11 @@ class HttpCli(object):
# check for old versions of files, # check for old versions of files,
# [num-backups, most-recent, hist-path] # [num-backups, most-recent, hist-path]
hist: dict[str, tuple[int, float, str]] = {} hist: dict[str, tuple[int, float, str]] = {}
histdir = os.path.join(fsroot, ".hist")
ptn = RE_MDV
try: try:
if vf["md_hist"] != "s":
raise Exception()
histdir = os.path.join(fsroot, ".hist")
ptn = RE_MDV
for hfn in bos.listdir(histdir): for hfn in bos.listdir(histdir):
m = ptn.match(hfn) m = ptn.match(hfn)
if not m: if not m:

View file

@ -174,6 +174,7 @@ class Cfg(Namespace):
lang="eng", lang="eng",
log_badpwd=1, log_badpwd=1,
logout=573, logout=573,
md_hist="s",
mte={"a": True}, mte={"a": True},
mth={}, mth={},
mtp=[], mtp=[],