mirror of https://github.com/dirtbags/moth.git
Now you can hit enter to sign in
This commit is contained in:
parent
02ed2162c2
commit
8e67abe0c0
|
@ -29,6 +29,16 @@ def get_seed(request):
|
|||
return int(seedstr)
|
||||
|
||||
|
||||
def get_puzzle(request):
|
||||
seed = get_seed(request)
|
||||
category = request.match_info.get("category")
|
||||
points = int(request.match_info.get("points"))
|
||||
filename = request.match_info.get("filename")
|
||||
cat = moth.Category(request.app["puzzles_dir"].joinpath(category), seed)
|
||||
puzzle = cat.puzzle(points)
|
||||
return puzzle
|
||||
|
||||
|
||||
async def handle_puzzlelist(request):
|
||||
seed = get_seed(request)
|
||||
puzzles = {
|
||||
|
@ -88,6 +98,16 @@ async def handle_puzzlefile(request):
|
|||
)
|
||||
|
||||
|
||||
async def handle_answer(request):
|
||||
seed = get_seed(request)
|
||||
category = request.match_info.get("category")
|
||||
points = int(request.match_info.get("points"))
|
||||
filename = request.match_info.get("filename")
|
||||
cat = moth.Category(request.app["puzzles_dir"].joinpath(category), seed)
|
||||
puzzle = cat.puzzle(points)
|
||||
|
||||
|
||||
|
||||
async def handle_mothballer(request):
|
||||
seed = get_seed(request)
|
||||
category = request.match_info.get("category")
|
||||
|
@ -113,19 +133,37 @@ async def handle_index(request):
|
|||
seed = random.getrandbits(32)
|
||||
body = """<!DOCTYPE html>
|
||||
<html>
|
||||
<head><title>Dev Server</title></head>
|
||||
<head>
|
||||
<title>Dev Server</title>
|
||||
<script>
|
||||
// Skip trying to log in
|
||||
sessionStorage.setItem("id", "Hello from the development server")
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Dev Server</h1>
|
||||
|
||||
<p>
|
||||
You need to provide the contest seed in the URL.
|
||||
If you don't have a contest seed in mind,
|
||||
why not try <a href="{seed}/">{seed}</a>?
|
||||
Pick a seed:
|
||||
</p>
|
||||
<ul>
|
||||
<li><a href="{seed}/">{seed}</a>: a special seed I made just for you!</li>
|
||||
<li><a href="random/">random</a>: will use a different seed every time you load a page (could be useful for debugging)</li>
|
||||
<li>You can also hack your own seed into the URL, if you want to.</li>
|
||||
</ul>
|
||||
|
||||
<p>
|
||||
If you are chaotic,
|
||||
you could even take your chances with a
|
||||
<a href="random/">random seed</a> for every HTTP request.
|
||||
This means generated files will get a different seed than the puzzle itself!
|
||||
Puzzles can be generated from Python code: these puzzles can use a random number generator if they want.
|
||||
The seed is used to create these random numbers.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
We like to make a new seed for every contest,
|
||||
and re-use that seed whenever we regenerate a category during an event
|
||||
(say to fix a bug).
|
||||
By using the same seed,
|
||||
we make sure that all the dynamically-generated puzzles have the same values
|
||||
in any new packages we build.
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -140,11 +178,7 @@ async def handle_static(request):
|
|||
themes = request.app["theme_dir"]
|
||||
fn = request.match_info.get("filename")
|
||||
if not fn:
|
||||
for fn in ("puzzle-list.html", "index.html"):
|
||||
path = themes.joinpath(fn)
|
||||
if path.exists():
|
||||
break
|
||||
else:
|
||||
fn = "index.html"
|
||||
path = themes.joinpath(fn)
|
||||
return web.FileResponse(path)
|
||||
|
||||
|
@ -182,7 +216,7 @@ if __name__ == '__main__':
|
|||
app["puzzles_dir"] = pathlib.Path(args.puzzles)
|
||||
app["theme_dir"] = pathlib.Path(args.theme)
|
||||
app.router.add_route("GET", "/", handle_index)
|
||||
app.router.add_route("GET", "/{seed}/puzzles.json", handle_puzzlelist)
|
||||
app.router.add_route("*", "/{seed}/puzzles.json", handle_puzzlelist)
|
||||
app.router.add_route("GET", "/{seed}/content/{category}/{points}/puzzle.json", handle_puzzle)
|
||||
app.router.add_route("GET", "/{seed}/content/{category}/{points}/{filename}", handle_puzzlefile)
|
||||
app.router.add_route("GET", "/{seed}/mothballer/{category}", handle_mothballer)
|
||||
|
|
|
@ -175,6 +175,12 @@ func (ctx *Instance) answerHandler(w http.ResponseWriter, req *http.Request) {
|
|||
}
|
||||
|
||||
func (ctx *Instance) puzzlesHandler(w http.ResponseWriter, req *http.Request) {
|
||||
teamid := req.FormValue("id")
|
||||
if _, err := ctx.TeamName(teamid); err != nil {
|
||||
http.Error(w, "Unauthorized: must provide team ID", http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
w.Write(ctx.jPuzzleList)
|
||||
|
@ -273,13 +279,7 @@ func (ctx *Instance) ServeHTTP(wOrig http.ResponseWriter, r *http.Request) {
|
|||
w: wOrig,
|
||||
statusCode: new(int),
|
||||
}
|
||||
w.Header().Set("WWW-Authenticate", "Basic")
|
||||
_, password, _ := r.BasicAuth()
|
||||
if password != ctx.Password {
|
||||
http.Error(w, "Authentication Required", 401)
|
||||
} else {
|
||||
ctx.mux.ServeHTTP(w, r)
|
||||
}
|
||||
log.Printf(
|
||||
"%s %s %s %d\n",
|
||||
r.RemoteAddr,
|
||||
|
|
|
@ -19,7 +19,6 @@ type Instance struct {
|
|||
MothballDir string
|
||||
StateDir string
|
||||
ResourcesDir string
|
||||
Password string
|
||||
Categories map[string]*Mothball
|
||||
update chan bool
|
||||
jPuzzleList []byte
|
||||
|
@ -27,13 +26,12 @@ type Instance struct {
|
|||
mux *http.ServeMux
|
||||
}
|
||||
|
||||
func NewInstance(base, mothballDir, stateDir, resourcesDir, password string) (*Instance, error) {
|
||||
func NewInstance(base, mothballDir, stateDir, resourcesDir string) (*Instance, error) {
|
||||
ctx := &Instance{
|
||||
Base: strings.TrimRight(base, "/"),
|
||||
MothballDir: mothballDir,
|
||||
StateDir: stateDir,
|
||||
ResourcesDir: resourcesDir,
|
||||
Password: password,
|
||||
Categories: map[string]*Mothball{},
|
||||
update: make(chan bool, 10),
|
||||
mux: http.NewServeMux(),
|
||||
|
|
|
@ -37,11 +37,6 @@ func main() {
|
|||
"/theme",
|
||||
"Path to static theme resources (HTML, images, css, ...)",
|
||||
)
|
||||
password := flag.String(
|
||||
"password",
|
||||
"sesame",
|
||||
"Pass Word (in the 1920s sense) to view the site. Not a secure passphrase.",
|
||||
)
|
||||
maintenanceInterval := flag.Duration(
|
||||
"maint",
|
||||
20*time.Second,
|
||||
|
@ -58,7 +53,7 @@ func main() {
|
|||
log.Fatal(err)
|
||||
}
|
||||
|
||||
ctx, err := NewInstance(*base, *mothballDir, *stateDir, *themeDir, *password)
|
||||
ctx, err := NewInstance(*base, *mothballDir, *stateDir, *themeDir)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
<html>
|
||||
<head>
|
||||
<title>MOTH</title>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width">
|
||||
<link rel="stylesheet" href="basic.css">
|
||||
<script src="moth.js"></script>
|
||||
|
@ -11,11 +12,11 @@
|
|||
<section>
|
||||
<div id="messages"></div>
|
||||
|
||||
<div id="login">
|
||||
<form id="login">
|
||||
Team name: <input name="name">
|
||||
Team ID: <input name="id"> <br>
|
||||
<button id="submit">Sign In</button>
|
||||
</div>
|
||||
<input type="submit" value="Sign In">
|
||||
</form>
|
||||
|
||||
<div id="puzzles"></div>
|
||||
|
||||
|
@ -23,6 +24,7 @@
|
|||
<nav>
|
||||
<ul>
|
||||
<li><a href="scoreboard.html">Scoreboard</a></li>
|
||||
<li><a href="logout.html">Sign Out</a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
</body>
|
||||
|
|
|
@ -15,7 +15,7 @@ sessionStorage.removeItem("id")
|
|||
</section>
|
||||
<nav>
|
||||
<ul>
|
||||
<li><a href="index.html">login</a></li>
|
||||
<li><a href="index.html">Sign In</a></li>
|
||||
<li><a href="scoreboard.html">Scoreboard</a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
|
|
|
@ -64,6 +64,7 @@ function renderPuzzles(obj) {
|
|||
i.appendChild(a)
|
||||
a.textContent = points
|
||||
a.href = "puzzle.html?cat=" + cat + "&points=" + points + "&pid=" + id
|
||||
a.target = "_blank"
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -76,13 +77,10 @@ function renderPuzzles(obj) {
|
|||
container.firstChild.remove()
|
||||
}
|
||||
container.appendChild(puzzlesElement)
|
||||
container.style.display = "none"
|
||||
|
||||
document.getElementById("login").style.display = "block"
|
||||
}
|
||||
|
||||
function heartbeat(teamId) {
|
||||
rpc("puzzles.json", {teamid: teamId})
|
||||
rpc("puzzles.json", {id: teamId})
|
||||
.then(resp => {
|
||||
if (resp.ok) {
|
||||
resp.json()
|
||||
|
@ -103,16 +101,20 @@ function showPuzzles(teamId) {
|
|||
let spinner = document.createElement("span")
|
||||
spinner.classList.add("spinner")
|
||||
|
||||
sessionStorage.setItem("id", teamId)
|
||||
|
||||
document.getElementById("login").style.display = "none"
|
||||
document.getElementById("puzzles").appendChild(spinner)
|
||||
heartbeat(teamId)
|
||||
setInterval(e => { heartbeat(teamId) }, 40000)
|
||||
}
|
||||
|
||||
function login() {
|
||||
function login(e) {
|
||||
let name = document.querySelector("[name=name]").value
|
||||
let id = document.querySelector("[name=id]").value
|
||||
|
||||
e.preventDefault()
|
||||
|
||||
rpc("register", {
|
||||
name: name,
|
||||
id: id,
|
||||
|
@ -158,7 +160,13 @@ function toast(message, timeout=5000) {
|
|||
}
|
||||
|
||||
function init() {
|
||||
document.getElementById("submit").addEventListener("click", login)
|
||||
// Already signed in?
|
||||
let id = sessionStorage.getItem("id")
|
||||
if (id) {
|
||||
showPuzzles(id)
|
||||
}
|
||||
|
||||
document.getElementById("login").addEventListener("submit", login)
|
||||
}
|
||||
|
||||
if (document.readyState === "loading") {
|
||||
|
|
|
@ -94,7 +94,7 @@ if (document.readyState === "loading") {
|
|||
<div id="devel"></div>
|
||||
<nav>
|
||||
<ul>
|
||||
<li><a href="puzzle-list.html">Puzzles</a></li>
|
||||
<li><a href="index.html">Puzzles</a></li>
|
||||
<li><a href="scoreboard.html">Scoreboard</a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
|
|
|
@ -128,7 +128,7 @@ if (document.readyState === "loading") {
|
|||
</section>
|
||||
<nav>
|
||||
<ul>
|
||||
<li><a href="puzzle-list.html">Puzzles</a></li>
|
||||
<li><a href="index.html">Puzzles</a></li>
|
||||
<li><a href="scoreboard.html">Scoreboard</a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
|
|
Loading…
Reference in New Issue