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
|
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
|
#!/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)
|
||||||
|
|
|
@ -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__':
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue