mirror of https://github.com/dirtbags/moth.git
Remove external library dependencies
This commit is contained in:
parent
0ff3679b2c
commit
041b582935
|
@ -3,7 +3,8 @@
|
||||||
import asyncio
|
import asyncio
|
||||||
import cgitb
|
import cgitb
|
||||||
import html
|
import html
|
||||||
from aiohttp import web
|
import cgi
|
||||||
|
import http.server
|
||||||
import io
|
import io
|
||||||
import json
|
import json
|
||||||
import mimetypes
|
import mimetypes
|
||||||
|
@ -17,130 +18,134 @@ import socketserver
|
||||||
import sys
|
import sys
|
||||||
import traceback
|
import traceback
|
||||||
import mothballer
|
import mothballer
|
||||||
|
import parse
|
||||||
|
from http import HTTPStatus
|
||||||
|
|
||||||
|
|
||||||
sys.dont_write_bytecode = True # Don't write .pyc files
|
sys.dont_write_bytecode = True # Don't write .pyc files
|
||||||
|
|
||||||
|
|
||||||
def get_seed(request):
|
class MothServer(http.server.ThreadingHTTPServer):
|
||||||
seedstr = request.match_info.get("seed")
|
def __init__(self, server_address, RequestHandlerClass):
|
||||||
if seedstr == "random":
|
super().__init__(server_address, RequestHandlerClass)
|
||||||
return random.getrandbits(32)
|
self.args = {}
|
||||||
else:
|
|
||||||
return int(seedstr)
|
|
||||||
|
|
||||||
|
|
||||||
def get_puzzle(request, data=None):
|
class MothRequestHandler(http.server.SimpleHTTPRequestHandler):
|
||||||
seed = get_seed(request)
|
endpoints = []
|
||||||
if not data:
|
|
||||||
data = request.match_info
|
|
||||||
category = data.get("cat")
|
|
||||||
points = int(data.get("points"))
|
|
||||||
filename = data.get("filename")
|
|
||||||
cat = moth.Category(request.app["puzzles_dir"].joinpath(category), seed)
|
|
||||||
puzzle = cat.puzzle(points)
|
|
||||||
return puzzle
|
|
||||||
|
|
||||||
async def handle_answer(request):
|
def __init__(self, request, client_address, server):
|
||||||
data = await request.post()
|
super().__init__(request, client_address, server, directory=server.args["theme_dir"])
|
||||||
puzzle = get_puzzle(request, data)
|
|
||||||
ret = {
|
|
||||||
"status": "success",
|
|
||||||
"data": {
|
|
||||||
"short": "",
|
|
||||||
"description": "Provided answer was not in list of answers"
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
if data.get("answer") in puzzle.answers:
|
|
||||||
ret["data"]["description"] = "Answer is correct"
|
|
||||||
return web.Response(
|
|
||||||
content_type="application/json",
|
|
||||||
body=json.dumps(ret),
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
async def handle_puzzlelist(request):
|
def get_seed(self):
|
||||||
seed = get_seed(request)
|
return seed
|
||||||
puzzles = {
|
|
||||||
"__devel__": [[0, ""]],
|
|
||||||
}
|
|
||||||
for p in request.app["puzzles_dir"].glob("*"):
|
|
||||||
if not p.is_dir() or p.match(".*"):
|
|
||||||
continue
|
|
||||||
catName = p.parts[-1]
|
|
||||||
cat = moth.Category(p, seed)
|
|
||||||
puzzles[catName] = [[i, str(i)] for i in cat.pointvals()]
|
|
||||||
puzzles[catName].append([0, ""])
|
|
||||||
if len(puzzles) <= 1:
|
|
||||||
logging.warning("No directories found matching {}/*".format(request.app["puzzles_dir"]))
|
|
||||||
return web.Response(
|
|
||||||
content_type="application/json",
|
|
||||||
body=json.dumps(puzzles),
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
async def handle_puzzle(request):
|
def get_puzzle(self):
|
||||||
seed = get_seed(request)
|
category = self.req.get("cat")
|
||||||
category = request.match_info.get("cat")
|
points = int(self.req.get("points"))
|
||||||
points = int(request.match_info.get("points"))
|
cat = moth.Category(self.server.args["puzzles_dir"].joinpath(category), self.seed)
|
||||||
cat = moth.Category(request.app["puzzles_dir"].joinpath(category), seed)
|
puzzle = cat.puzzle(points)
|
||||||
puzzle = cat.puzzle(points)
|
return puzzle
|
||||||
|
|
||||||
obj = puzzle.package()
|
|
||||||
obj["answers"] = puzzle.answers
|
|
||||||
obj["hint"] = puzzle.hint
|
|
||||||
obj["summary"] = puzzle.summary
|
|
||||||
obj["logs"] = puzzle.logs
|
|
||||||
|
|
||||||
return web.Response(
|
|
||||||
content_type="application/json",
|
|
||||||
body=json.dumps(obj),
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
async def handle_puzzlefile(request):
|
def handle_answer(self):
|
||||||
seed = get_seed(request)
|
for f in ("cat", "points", "answer"):
|
||||||
category = request.match_info.get("cat")
|
self.req[f] = self.fields.getfirst(f)
|
||||||
points = int(request.match_info.get("points"))
|
puzzle = self.get_puzzle()
|
||||||
filename = request.match_info.get("filename")
|
ret = {
|
||||||
cat = moth.Category(request.app["puzzles_dir"].joinpath(category), seed)
|
"status": "success",
|
||||||
puzzle = cat.puzzle(points)
|
"data": {
|
||||||
|
"short": "",
|
||||||
|
"description": "Provided answer was not in list of answers"
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
try:
|
if self.req.get("answer") in puzzle.answers:
|
||||||
file = puzzle.files[filename]
|
ret["data"]["description"] = "Answer is correct"
|
||||||
except KeyError:
|
self.send_response(200)
|
||||||
return web.Response(status=404)
|
self.send_header("Content-Type", "application/json")
|
||||||
|
self.end_headers()
|
||||||
|
self.wfile.write(json.dumps(ret).encode("utf-8"))
|
||||||
|
endpoints.append(('/{seed}/answer', handle_answer))
|
||||||
|
|
||||||
content_type, _ = mimetypes.guess_type(file.name)
|
|
||||||
return web.Response(
|
|
||||||
body=file.stream.read(), # Is there no way to pipe this, must we slurp the whole thing into memory?
|
|
||||||
content_type=content_type,
|
|
||||||
)
|
|
||||||
|
|
||||||
async def handle_mothballer(request):
|
def handle_puzzlelist(self):
|
||||||
seed = get_seed(request)
|
puzzles = {
|
||||||
category = request.match_info.get("cat")
|
"__devel__": [[0, ""]],
|
||||||
|
}
|
||||||
|
for p in self.server.args["puzzles_dir"].glob("*"):
|
||||||
|
if not p.is_dir() or p.match(".*"):
|
||||||
|
continue
|
||||||
|
catName = p.parts[-1]
|
||||||
|
cat = moth.Category(p, self.seed)
|
||||||
|
puzzles[catName] = [[i, str(i)] for i in cat.pointvals()]
|
||||||
|
puzzles[catName].append([0, ""])
|
||||||
|
if len(puzzles) <= 1:
|
||||||
|
logging.warning("No directories found matching {}/*".format(self.server.args["puzzles_dir"]))
|
||||||
|
self.send_response(200)
|
||||||
|
self.send_header("Content-Type", "application/json")
|
||||||
|
self.end_headers()
|
||||||
|
self.wfile.write(json.dumps(puzzles).encode("utf-8"))
|
||||||
|
endpoints.append(('/{seed}/puzzles.json', handle_puzzlelist))
|
||||||
|
|
||||||
try:
|
|
||||||
catdir = request.app["puzzles_dir"].joinpath(category)
|
def handle_puzzle(self):
|
||||||
mb = mothballer.package(category, catdir, seed)
|
puzzle = self.get_puzzle()
|
||||||
except:
|
|
||||||
body = cgitb.html(sys.exc_info())
|
obj = puzzle.package()
|
||||||
resp = web.Response(text=body, content_type="text/html")
|
obj["answers"] = puzzle.answers
|
||||||
|
obj["hint"] = puzzle.hint
|
||||||
|
obj["summary"] = puzzle.summary
|
||||||
|
obj["logs"] = puzzle.logs
|
||||||
|
|
||||||
|
self.send_response(200)
|
||||||
|
self.send_header("Content-Type", "application/json")
|
||||||
|
self.end_headers()
|
||||||
|
self.wfile.write(json.dumps(obj).encode("utf-8"))
|
||||||
|
endpoints.append(('/{seed}/content/{cat}/{points}/puzzle.json', handle_puzzle))
|
||||||
|
|
||||||
|
|
||||||
|
def handle_puzzlefile(self):
|
||||||
|
puzzle = self.get_puzzle()
|
||||||
|
|
||||||
|
try:
|
||||||
|
file = puzzle.files[self.req["filename"]]
|
||||||
|
except KeyError:
|
||||||
|
return web.Response(status=404)
|
||||||
|
|
||||||
|
self.send_response(200)
|
||||||
|
self.send_header("Content-Type", mimetypes.guess_type(file.name))
|
||||||
|
self.end_headers()
|
||||||
|
shutil.copyfileobj(file.stream, self.wfile)
|
||||||
|
endpoints.append(("/{seed}/content/{cat}/{points}/{filename}", handle_puzzlefile))
|
||||||
|
|
||||||
|
|
||||||
|
def handle_mothballer(self):
|
||||||
|
category = self.req.get("cat")
|
||||||
|
|
||||||
|
try:
|
||||||
|
catdir = self.server.args["puzzles_dir"].joinpath(category)
|
||||||
|
mb = mothballer.package(category, catdir, self.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={}.mb".format(category)},
|
||||||
|
content_type="application/octet_stream",
|
||||||
|
)
|
||||||
return resp
|
return resp
|
||||||
|
endpoints.append(("/{seed}/mothballer/{cat}", handle_mothballer))
|
||||||
mb_buf = mb.read()
|
|
||||||
resp = web.Response(
|
|
||||||
body=mb_buf,
|
|
||||||
headers={"Content-Disposition": "attachment; filename={}.mb".format(category)},
|
|
||||||
content_type="application/octet_stream",
|
|
||||||
)
|
|
||||||
return resp
|
|
||||||
|
|
||||||
|
|
||||||
async def handle_index(request):
|
def handle_index(self):
|
||||||
seed = random.getrandbits(32)
|
seed = self.get_seed()
|
||||||
body = """<!DOCTYPE html>
|
body = """<!DOCTYPE html>
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<title>Dev Server</title>
|
<title>Dev Server</title>
|
||||||
|
@ -177,19 +182,51 @@ sessionStorage.setItem("id", "devel-server")
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
""".format(seed=seed)
|
""".format(seed=seed)
|
||||||
return web.Response(
|
|
||||||
content_type="text/html",
|
self.send_response(200)
|
||||||
body=body,
|
self.send_header("Content-Type", "text/html; charset=utf-8")
|
||||||
)
|
self.end_headers()
|
||||||
|
self.wfile.write(body.encode('utf-8'))
|
||||||
|
endpoints.append((r"/", handle_index))
|
||||||
|
|
||||||
|
|
||||||
async def handle_static(request):
|
def handle_theme_file(self):
|
||||||
themes = request.app["theme_dir"]
|
self.path = "/" + self.req.get("path", "")
|
||||||
fn = request.match_info.get("filename")
|
super().do_GET()
|
||||||
if not fn:
|
endpoints.append(("/{seed}/", handle_theme_file))
|
||||||
fn = "index.html"
|
endpoints.append(("/{seed}/{path}", handle_theme_file))
|
||||||
path = themes.joinpath(fn)
|
|
||||||
return web.FileResponse(path)
|
|
||||||
|
def do_GET(self):
|
||||||
|
self.fields = cgi.FieldStorage(
|
||||||
|
fp=self.rfile,
|
||||||
|
headers=self.headers,
|
||||||
|
environ={
|
||||||
|
"REQUEST_METHOD": self.command,
|
||||||
|
"CONTENT_TYPE": self.headers["Content-Type"],
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
for pattern, function in self.endpoints:
|
||||||
|
result = parse.parse(pattern, self.path)
|
||||||
|
if result:
|
||||||
|
self.req = result.named
|
||||||
|
seed = self.req.get("seed", "random")
|
||||||
|
if seed == "random":
|
||||||
|
self.seed = random.getrandbits(32)
|
||||||
|
else:
|
||||||
|
self.seed = int(seed)
|
||||||
|
return function(self)
|
||||||
|
super().do_GET()
|
||||||
|
|
||||||
|
def do_POST(self):
|
||||||
|
self.do_GET()
|
||||||
|
|
||||||
|
def do_HEAD(self):
|
||||||
|
self.send_error(
|
||||||
|
HTTPStatus.NOT_IMPLEMENTED,
|
||||||
|
"Unsupported method (%r)" % self.command,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
@ -220,15 +257,10 @@ if __name__ == '__main__':
|
||||||
|
|
||||||
mydir = os.path.dirname(os.path.dirname(os.path.realpath(sys.argv[0])))
|
mydir = os.path.dirname(os.path.dirname(os.path.realpath(sys.argv[0])))
|
||||||
|
|
||||||
app = web.Application()
|
server = MothServer((addr, port), MothRequestHandler)
|
||||||
app["base_url"] = args.base
|
server.args["base_url"] = args.base
|
||||||
app["puzzles_dir"] = pathlib.Path(args.puzzles)
|
server.args["puzzles_dir"] = pathlib.Path(args.puzzles)
|
||||||
app["theme_dir"] = pathlib.Path(args.theme)
|
server.args["theme_dir"] = args.theme
|
||||||
app.router.add_route("GET", "/", handle_index)
|
|
||||||
app.router.add_route("*", "/{seed}/answer", handle_answer)
|
logging.info("Listening on %s:%d", addr, port)
|
||||||
app.router.add_route("*", "/{seed}/puzzles.json", handle_puzzlelist)
|
server.serve_forever()
|
||||||
app.router.add_route("GET", "/{seed}/content/{cat}/{points}/puzzle.json", handle_puzzle)
|
|
||||||
app.router.add_route("GET", "/{seed}/content/{cat}/{points}/{filename}", handle_puzzlefile)
|
|
||||||
app.router.add_route("GET", "/{seed}/mothballer/{cat}", handle_mothballer)
|
|
||||||
app.router.add_route("GET", "/{seed}/{filename:.*}", handle_static)
|
|
||||||
web.run_app(app, host=addr, port=port)
|
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,7 +1,7 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/namsral/flag"
|
"flag"
|
||||||
"log"
|
"log"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"mime"
|
"mime"
|
||||||
|
|
Loading…
Reference in New Issue