mirror of https://github.com/dirtbags/moth.git
dev server now includes mothballer
This commit is contained in:
parent
2f06fd556c
commit
88995f3c48
|
@ -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"]
|
||||
|
|
|
@ -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("<p>seed = {}</p>".format(seed))
|
||||
p.write("<ul>")
|
||||
for i in sorted(glob.glob(os.path.join(request.app["puzzles_dir"], "*", ""))):
|
||||
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>")
|
||||
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)
|
||||
|
|
|
@ -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__':
|
|
@ -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;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue