verify markdown saves with a full roundtrip

This commit is contained in:
ed 2020-05-03 20:57:52 +02:00
parent da1094db84
commit 6e43ee7cc7
4 changed files with 69 additions and 8 deletions

View file

@ -8,7 +8,6 @@ import time
import json import json
from datetime import datetime from datetime import datetime
import calendar import calendar
import mimetypes
from .__init__ import E, PY2, WINDOWS from .__init__ import E, PY2, WINDOWS
from .util import * # noqa # pylint: disable=unused-wildcard-import from .util import * # noqa # pylint: disable=unused-wildcard-import
@ -526,7 +525,7 @@ class HttpCli(object):
except Pebkac as ex: except Pebkac as ex:
errmsg = str(ex) errmsg = str(ex)
td = time.time() - t0 td = max(0.1, time.time() - t0)
sz_total = sum(x[0] for x in files) sz_total = sum(x[0] for x in files)
spd = (sz_total / td) / (1024 * 1024) spd = (sz_total / td) / (1024 * 1024)
@ -665,6 +664,7 @@ class HttpCli(object):
return file_lastmod, int(file_ts) > int(cli_ts) return file_lastmod, int(file_ts) > int(cli_ts)
except: except:
self.log("bad lastmod format: {}".format(cli_lastmod)) self.log("bad lastmod format: {}".format(cli_lastmod))
self.log(" expected format: {}".format(file_lastmod))
return file_lastmod, file_lastmod != cli_lastmod return file_lastmod, file_lastmod != cli_lastmod
return file_lastmod, True return file_lastmod, True
@ -789,7 +789,7 @@ class HttpCli(object):
self.send_headers( self.send_headers(
length=upper - lower, length=upper - lower,
status=status, status=status,
mime=mimetypes.guess_type(req_path)[0] or "application/octet-stream", mime=guess_mime(req_path)[0] or "application/octet-stream",
) )
logmsg += str(status) + logtail logmsg += str(status) + logtail

View file

@ -8,6 +8,7 @@ import struct
import hashlib import hashlib
import platform import platform
import threading import threading
import mimetypes
import subprocess as sp # nosec import subprocess as sp # nosec
from .__init__ import PY2, WINDOWS from .__init__ import PY2, WINDOWS
@ -474,6 +475,13 @@ def unescape_cookie(orig):
return ret return ret
def guess_mime(url):
if url.endswith(".md"):
return ["text/plain; charset=UTF-8"]
return mimetypes.guess_type(url)
def runcmd(*argv): def runcmd(*argv):
p = sp.Popen(argv, stdout=sp.PIPE, stderr=sp.PIPE) p = sp.Popen(argv, stdout=sp.PIPE, stderr=sp.PIPE)
stdout, stderr = p.communicate() stdout, stderr = p.communicate()

View file

@ -47,6 +47,9 @@ var dom_md = document.getElementById('mt');
gfm: true gfm: true
} }
}, },
shortcuts: {
"save": "Ctrl-S"
},
insertTexts: ["[](", ")"], insertTexts: ["[](", ")"],
tabSize: 4, tabSize: 4,
toolbar: tbar, toolbar: tbar,
@ -85,10 +88,12 @@ function save(mde) {
return; return;
} }
var txt = mde.value();
var fd = new FormData(); var fd = new FormData();
fd.append("act", "tput"); fd.append("act", "tput");
fd.append("lastmod", (force ? -1 : last_modified)); fd.append("lastmod", (force ? -1 : last_modified));
fd.append("body", mde.value()); fd.append("body", txt);
var url = (document.location + '').split('?')[0] + '?raw'; var url = (document.location + '').split('?')[0] + '?raw';
var xhr = new XMLHttpRequest(); var xhr = new XMLHttpRequest();
@ -97,6 +102,7 @@ function save(mde) {
xhr.onreadystatechange = save_cb; xhr.onreadystatechange = save_cb;
xhr.btn = save_btn; xhr.btn = save_btn;
xhr.mde = mde; xhr.mde = mde;
xhr.txt = txt;
xhr.send(fd); xhr.send(fd);
} }
@ -138,8 +144,55 @@ function save_cb() {
return; return;
} }
last_modified = r.lastmod;
this.btn.classList.remove('force-save'); this.btn.classList.remove('force-save');
alert('save OK -- wrote ' + r.size + ' bytes.\n\nsha512: ' + r.sha512); //alert('save OK -- wrote ' + r.size + ' bytes.\n\nsha512: ' + r.sha512);
md_changed(this.mde, true);
// download the saved doc from the server and compare
var url = (document.location + '').split('?')[0] + '?raw';
var xhr = new XMLHttpRequest();
xhr.open('GET', url, true);
xhr.responseType = 'text';
xhr.onreadystatechange = save_chk;
xhr.btn = this.save_btn;
xhr.mde = this.mde;
xhr.txt = this.txt;
xhr.lastmod = r.lastmod;
xhr.send();
}
function save_chk() {
if (this.readyState != XMLHttpRequest.DONE)
return;
if (this.status !== 200) {
alert('Error! The file was NOT saved.\n\n' + this.status + ": " + (this.responseText + '').replace(/^<pre>/, ""));
return;
}
var doc1 = this.txt.replace(/\r/g, "");
var doc2 = this.responseText.replace(/\r/g, "");
if (doc1 != doc2) {
alert(
'Error! The document on the server does not appear to have saved correctly (your editor contents and the server copy is not identical). Place the document on your clipboard for now and check the server logs for hints\n\n' +
'Length: yours=' + doc1.length + ', server=' + doc2.length
);
alert('yours, ' + doc1.length + ' byte:\n[' + doc1 + ']');
alert('server, ' + doc2.length + ' byte:\n[' + doc2 + ']');
return;
}
last_modified = this.lastmod;
md_changed(this.mde, true);
var ok = document.createElement('div');
ok.setAttribute('style', 'font-size:6em;font-family:serif;font-weight:bold;color:#cf6;background:#444;border-radius:.3em;padding:.6em 0;position:fixed;top:30%;left:calc(50% - 2em);width:4em;text-align:center;z-index:9001;transition:opacity 0.2s ease-in-out;opacity:1');
ok.innerHTML = 'OK✔';
var parent = document.getElementById('m');
document.documentElement.appendChild(ok);
setTimeout(function () {
ok.style.opacity = 0;
}, 500);
setTimeout(function () {
ok.parentNode.removeChild(ok);
}, 750);
} }

View file

@ -22,7 +22,7 @@
</form> </form>
</div> </div>
<div id="op_new_md" class="opview opbox act"> <div id="op_new_md" class="opview opbox">
<form method="post" enctype="multipart/form-data" accept-charset="utf-8" action="/{{ vdir }}"> <form method="post" enctype="multipart/form-data" accept-charset="utf-8" action="/{{ vdir }}">
<input type="hidden" name="act" value="new_md" /> <input type="hidden" name="act" value="new_md" />
<input type="text" name="name" size="30"> <input type="text" name="name" size="30">