From 8d276a1b8b59fe00c946cf20cbe735ef4aefb48e Mon Sep 17 00:00:00 2001 From: Neale Pickett Date: Fri, 11 May 2018 23:00:55 +0000 Subject: [PATCH] dev server now includes mothballer --- Dockerfile.moth-compile | 4 +-- tools/devel-server.py | 34 +++++++++++++++++++-- tools/{package-puzzles.py => mothballer.py} | 25 +++++++++------ www/res/style.css | 12 +++++++- 4 files changed, 60 insertions(+), 15 deletions(-) rename tools/{package-puzzles.py => mothballer.py} (91%) diff --git a/Dockerfile.moth-compile b/Dockerfile.moth-compile index 5680e4f..cd1331f 100644 --- a/Dockerfile.moth-compile +++ b/Dockerfile.moth-compile @@ -2,6 +2,6 @@ FROM alpine 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"] diff --git a/tools/devel-server.py b/tools/devel-server.py index 354f899..830eb9f 100755 --- a/tools/devel-server.py +++ b/tools/devel-server.py @@ -1,6 +1,7 @@ #!/usr/bin/python3 import asyncio +import cgitb import glob import html from aiohttp import web @@ -15,11 +16,12 @@ import shutil import socketserver import sys import traceback +import mothballer sys.dont_write_bytecode = True # Don't write .pyc files 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: def __init__(self, title, depth=0): @@ -72,11 +74,17 @@ async def handle_front(request): return p.response(request) async def handle_puzzlelist(request): + seed = request.query.get("seed", mkseed()) p = Page("Puzzle Categories", 1) + p.write("

seed = {}

".format(seed)) p.write("") return p.response(request) @@ -137,7 +145,7 @@ async def handle_puzzle(request): return p.response(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") points = int(request.match_info.get("points")) filename = request.match_info.get("filename") @@ -158,6 +166,25 @@ async def handle_puzzlefile(request): resp.body = file.stream.read() 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__': 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}/{points}/", handle_puzzle) 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) web.run_app(app, host=addr, port=port) diff --git a/tools/package-puzzles.py b/tools/mothballer.py similarity index 91% rename from tools/package-puzzles.py rename to tools/mothballer.py index 4a8da80..76832a5 100755 --- a/tools/package-puzzles.py +++ b/tools/mothballer.py @@ -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()) def build_category(categorydir, outdir): - zipfileraw = tempfile.NamedTemporaryFile(delete=False) - zf = zipfile.ZipFile(zipfileraw, 'x') - category_seed = binascii.b2a_hex(os.urandom(20)) puzzles_dict = {} secrets = {} categoryname = os.path.basename(categorydir.strip(os.sep)) - seedfn = os.path.join("category_seed.txt") zipfilename = os.path.join(outdir, "%s.zip" % categoryname) logging.info("Building {} from {}".format(zipfilename, categorydir)) @@ -111,16 +107,27 @@ def build_category(categorydir, outdir): existing.close() 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 = {} answers = {} summary = {} for puzzle in cat: 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')) puzzlehash = hashmap.hexdigest() @@ -152,8 +159,8 @@ def build_category(categorydir, outdir): # clean up zf.close() - - shutil.move(zipfileraw.name, zipfilename) + zfraw.seek(0) + return zfraw if __name__ == '__main__': diff --git a/www/res/style.css b/www/res/style.css index c8ab4de..14f11e9 100644 --- a/www/res/style.css +++ b/www/res/style.css @@ -78,6 +78,16 @@ pre, tt { height: 70%; } +.download { + background: #080; + color: white; + display: inline-block; +} +.download:link { + color: inherit; + text-decoration: inherit; +} + h1 { text-align: center; font-size: 120%; @@ -124,6 +134,6 @@ a:visited { } ::-webkit-scrollbar-thumb { - background: rgba(255, 255, 255, 0.2); + background: rgba(255, 255, 255, 0.2); border-radius: 1em; }