dev server now includes mothballer

This commit is contained in:
Neale Pickett 2018-05-11 23:00:55 +00:00
parent 2f06fd556c
commit 88995f3c48
4 changed files with 60 additions and 15 deletions

View File

@ -2,6 +2,6 @@ FROM alpine
RUN apk --no-cache add python3 py3-pillow RUN apk --no-cache add python3 py3-pillow
COPY tools/package-puzzles.py tools/moth.py tools/mistune.py tools/answer_words.txt /moth/ COPY . /moth/
ENTRYPOINT ["python3", "/moth/package-puzzles.py"] ENTRYPOINT ["python3", "/moth/tools/mothballer.py"]

View File

@ -1,6 +1,7 @@
#!/usr/bin/python3 #!/usr/bin/python3
import asyncio import asyncio
import cgitb
import glob import glob
import html import html
from aiohttp import web from aiohttp import web
@ -15,11 +16,12 @@ import shutil
import socketserver import socketserver
import sys import sys
import traceback import traceback
import mothballer
sys.dont_write_bytecode = True # Don't write .pyc files sys.dont_write_bytecode = True # Don't write .pyc files
def mkseed(): def mkseed():
return bytes(random.choice(b'abcdef0123456789') for i in range(40)) return bytes(random.choice(b'abcdef0123456789') for i in range(40)).decode('ascii')
class Page: class Page:
def __init__(self, title, depth=0): def __init__(self, title, depth=0):
@ -72,11 +74,17 @@ async def handle_front(request):
return p.response(request) return p.response(request)
async def handle_puzzlelist(request): async def handle_puzzlelist(request):
seed = request.query.get("seed", mkseed())
p = Page("Puzzle Categories", 1) p = Page("Puzzle Categories", 1)
p.write("<p>seed = {}</p>".format(seed))
p.write("<ul>") p.write("<ul>")
for i in sorted(glob.glob(os.path.join(request.app["puzzles_dir"], "*", ""))): for i in sorted(glob.glob(os.path.join(request.app["puzzles_dir"], "*", ""))):
bn = os.path.basename(i.strip('/\\')) bn = os.path.basename(i.strip('/\\'))
p.write('<li><a href="{}/">puzzles/{}/</a></li>'.format(bn, bn)) p.write("<li>")
p.write("<a href=\"../mothballer/{cat}?seed={seed}\" class=\"download\" title=\"download mothball\">[mb]</a>".format(cat=bn, seed=seed))
p.write(" ")
p.write("<a href=\"{cat}/?seed={seed}\">{cat}</a>".format(cat=bn, seed=seed))
p.write("</li>")
p.write("</ul>") p.write("</ul>")
return p.response(request) return p.response(request)
@ -137,7 +145,7 @@ async def handle_puzzle(request):
return p.response(request) return p.response(request)
async def handle_puzzlefile(request): async def handle_puzzlefile(request):
seed = request.query.get("seed", mkseed()) seed = request.query.get("seed", mkseed()).encode('ascii')
category = request.match_info.get("category") category = request.match_info.get("category")
points = int(request.match_info.get("points")) points = int(request.match_info.get("points"))
filename = request.match_info.get("filename") filename = request.match_info.get("filename")
@ -158,6 +166,25 @@ async def handle_puzzlefile(request):
resp.body = file.stream.read() resp.body = file.stream.read()
return resp return resp
async def handle_mothballer(request):
seed = request.query.get("seed", mkseed())
category = request.match_info.get("category")
try:
catdir = os.path.join(request.app["puzzles_dir"], category)
mb = mothballer.package(category, catdir, seed)
except:
body = cgitb.html(sys.exc_info())
resp = web.Response(text=body, content_type="text/html")
return resp
mb_buf = mb.read()
resp = web.Response(
body=mb_buf,
headers={"Content-Disposition": "attachment; filename={}.zip".format(category)},
content_type="application/octet_stream",
)
return resp
if __name__ == '__main__': if __name__ == '__main__':
import argparse import argparse
@ -192,5 +219,6 @@ if __name__ == '__main__':
app.router.add_route("GET", "/puzzles/{category}/", handle_category) app.router.add_route("GET", "/puzzles/{category}/", handle_category)
app.router.add_route("GET", "/puzzles/{category}/{points}/", handle_puzzle) app.router.add_route("GET", "/puzzles/{category}/{points}/", handle_puzzle)
app.router.add_route("GET", "/puzzles/{category}/{points}/{filename}", handle_puzzlefile) app.router.add_route("GET", "/puzzles/{category}/{points}/{filename}", handle_puzzlefile)
app.router.add_route("GET", "/mothballer/{category}", handle_mothballer)
app.router.add_static("/files/", mydir, show_index=True) app.router.add_static("/files/", mydir, show_index=True)
web.run_app(app, host=addr, port=port) web.run_app(app, host=addr, port=port)

View File

@ -89,15 +89,11 @@ def generate_html(ziphandle, puzzle, puzzledir, category, points, authors, files
ziphandle.writestr(os.path.join(puzzledir, 'index.html'), html_content.getvalue()) ziphandle.writestr(os.path.join(puzzledir, 'index.html'), html_content.getvalue())
def build_category(categorydir, outdir): def build_category(categorydir, outdir):
zipfileraw = tempfile.NamedTemporaryFile(delete=False)
zf = zipfile.ZipFile(zipfileraw, 'x')
category_seed = binascii.b2a_hex(os.urandom(20)) category_seed = binascii.b2a_hex(os.urandom(20))
puzzles_dict = {} puzzles_dict = {}
secrets = {} secrets = {}
categoryname = os.path.basename(categorydir.strip(os.sep)) categoryname = os.path.basename(categorydir.strip(os.sep))
seedfn = os.path.join("category_seed.txt")
zipfilename = os.path.join(outdir, "%s.zip" % categoryname) zipfilename = os.path.join(outdir, "%s.zip" % categoryname)
logging.info("Building {} from {}".format(zipfilename, categorydir)) logging.info("Building {} from {}".format(zipfilename, categorydir))
@ -111,16 +107,27 @@ def build_category(categorydir, outdir):
existing.close() existing.close()
logging.debug("Using PRNG seed {}".format(category_seed)) logging.debug("Using PRNG seed {}".format(category_seed))
zf.writestr(seedfn, category_seed) zipfileraw = tempfile.NamedTemporaryFile(delete=False)
mothball = package(categoryname, categorydir, zfraw)
shutil.copyfileobj(mothball, zipfileraw)
zipfileraw.close()
shutil.move(zipfileraw.name, zipfilename)
cat = moth.Category(categorydir, category_seed)
# Returns a file-like object containing the contents of the new zip file
def package(categoryname, categorydir, seed):
zfraw = io.BytesIO()
zf = zipfile.ZipFile(zfraw, 'x')
zf.writestr("category_seed.txt", seed)
cat = moth.Category(categorydir, seed)
mapping = {} mapping = {}
answers = {} answers = {}
summary = {} summary = {}
for puzzle in cat: for puzzle in cat:
logging.info("Processing point value {}".format(puzzle.points)) logging.info("Processing point value {}".format(puzzle.points))
hashmap = hashlib.sha1(category_seed) hashmap = hashlib.sha1(seed.encode('utf-8'))
hashmap.update(str(puzzle.points).encode('utf-8')) hashmap.update(str(puzzle.points).encode('utf-8'))
puzzlehash = hashmap.hexdigest() puzzlehash = hashmap.hexdigest()
@ -152,8 +159,8 @@ def build_category(categorydir, outdir):
# clean up # clean up
zf.close() zf.close()
zfraw.seek(0)
shutil.move(zipfileraw.name, zipfilename) return zfraw
if __name__ == '__main__': if __name__ == '__main__':

View File

@ -78,6 +78,16 @@ pre, tt {
height: 70%; height: 70%;
} }
.download {
background: #080;
color: white;
display: inline-block;
}
.download:link {
color: inherit;
text-decoration: inherit;
}
h1 { h1 {
text-align: center; text-align: center;
font-size: 120%; font-size: 120%;
@ -124,6 +134,6 @@ a:visited {
} }
::-webkit-scrollbar-thumb { ::-webkit-scrollbar-thumb {
background: rgba(255, 255, 255, 0.2); background: rgba(255, 255, 255, 0.2);
border-radius: 1em; border-radius: 1em;
} }