diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..166db3a --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,30 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "Launch", + "type": "go", + "request": "launch", + "mode": "auto", + "program": "${fileDirname}", + "env": {}, + "args": [] + }, + { + "name": "MOTHd", + "type": "go", + "request": "launch", + "mode": "auto", + "program": "${workspaceFolder}/cmd/mothd", + "env": {}, + "args": [ + "--state", "/tmp/state", + "--puzzles", "${workspaceFolder}/example-puzzles", + "--theme", "${workspaceFolder}/theme", + ] + } + ] +} \ No newline at end of file diff --git a/TODO.md b/TODO.md index a05e17a..665f4ed 100644 --- a/TODO.md +++ b/TODO.md @@ -1,3 +1,5 @@ * Figure out how to log JSend short text in addition to HTTP code * We've got logic in state.go and httpd.go that is neither httpd nor state specific. Pull this into some other file that means "here are the brains of the server". +* Get Bo's answer pattern anchors working again +* Are we logging every transaction now? diff --git a/cmd/mothd/__debug_bin b/cmd/mothd/__debug_bin new file mode 100755 index 0000000..10b302f Binary files /dev/null and b/cmd/mothd/__debug_bin differ diff --git a/cmd/mothd/main.go b/cmd/mothd/main.go index f457edf..718348b 100644 --- a/cmd/mothd/main.go +++ b/cmd/mothd/main.go @@ -27,7 +27,7 @@ func main() { puzzlePath := flag.String( "puzzles", "", - "Path to puzzles tree; if specified, enables development mode", + "Path to puzzles tree (enables development mode)", ) refreshInterval := flag.Duration( "refresh", diff --git a/cmd/transpile/main.go b/cmd/transpile/main.go index 119a381..84f3293 100644 --- a/cmd/transpile/main.go +++ b/cmd/transpile/main.go @@ -14,84 +14,101 @@ import ( "github.com/spf13/afero" ) -// T contains everything required for a transpilation invocation (across the nation). +// T represents the state of things type T struct { - // What action to take - w io.Writer - Cat string - Points int - Answer string - Filename string - Fs afero.Fs + Stdout io.Writer + Stderr io.Writer + Args []string + BaseFs afero.Fs + fs afero.Fs + filename string + answer string } -// ParseArgs parses command-line arguments into T, returning the action to take. -// BUG(neale): CLI arguments are not related to how the CLI will be used. -func (t *T) ParseArgs() string { - action := flag.String("action", "inventory", "Action to take: must be 'inventory', 'open', 'answer', or 'mothball'") - flag.StringVar(&t.Cat, "cat", "", "Puzzle category") - flag.IntVar(&t.Points, "points", 0, "Puzzle point value") - flag.StringVar(&t.Answer, "answer", "", "Answer to check for correctness, for 'answer' action") - flag.StringVar(&t.Filename, "filename", "", "Filename, for 'open' action") - basedir := flag.String("basedir", ".", "Base directory containing all puzzles") - flag.Parse() +// Command is a function invoked by the user +type Command func() error - osfs := afero.NewOsFs() - t.Fs = afero.NewBasePathFs(osfs, *basedir) - - return *action +func nothing() error { + return nil } -// Handle performs the requested action -func (t *T) Handle(action string) error { - switch action { - case "inventory": - return t.PrintInventory() - case "open": - return t.Open() - case "mothball": - return t.Mothball() - default: - return fmt.Errorf("Unimplemented action: %s", action) +// ParseArgs parses arguments and runs the appropriate action. +func (t *T) ParseArgs() (Command, error) { + var cmd Command + + if len(t.Args) == 1 { + fmt.Fprintln(t.Stderr, "Usage: transpile COMMAND [flags]") + fmt.Fprintln(t.Stderr, "") + fmt.Fprintln(t.Stderr, " mothball: Compile a mothball") + fmt.Fprintln(t.Stderr, " inventory: Show category inventory") + fmt.Fprintln(t.Stderr, " open: Open a file for a puzzle") + fmt.Fprintln(t.Stderr, " answer: Check correctness of an answer") + return nothing, nil } + + flags := flag.NewFlagSet(t.Args[1], flag.ContinueOnError) + directory := flags.String("dir", "", "Work directory") + + switch t.Args[1] { + case "mothball": + cmd = t.DumpMothball + case "inventory": + cmd = t.PrintInventory + case "open": + flags.StringVar(&t.filename, "file", "puzzle.json", "Filename to open") + cmd = t.DumpFile + case "answer": + flags.StringVar(&t.answer, "answer", "", "Answer to check") + cmd = t.CheckAnswer + default: + return nothing, fmt.Errorf("%s is not a valid command", t.Args[1]) + } + + flags.SetOutput(t.Stderr) + if err := flags.Parse(t.Args[2:]); err != nil { + return nothing, err + } + if *directory != "" { + t.fs = afero.NewBasePathFs(t.BaseFs, *directory) + } else { + t.fs = t.BaseFs + } + log.Println(t.Args, t.fs) + + return cmd, nil } // PrintInventory prints a puzzle inventory to stdout func (t *T) PrintInventory() error { - inv := make(map[string][]int) - - dirEnts, err := afero.ReadDir(t.Fs, ".") + inv, err := transpile.FsInventory(t.fs) if err != nil { return err } - for _, ent := range dirEnts { - if ent.IsDir() { - c := t.NewCategory(ent.Name()) - if puzzles, err := c.Inventory(); err != nil { - log.Print(err) - continue - } else { - sort.Ints(puzzles) - inv[ent.Name()] = puzzles - } - } - } - m := json.NewEncoder(t.w) - if err := m.Encode(inv); err != nil { - return err + cats := make([]string, 0, len(inv)) + for cat := range inv { + cats = append(cats, cat) + } + sort.Strings(cats) + for _, cat := range cats { + puzzles := inv[cat] + fmt.Fprint(t.Stdout, cat) + for _, p := range puzzles { + fmt.Fprint(t.Stdout, " ", p) + } + fmt.Fprintln(t.Stdout) } return nil } -// Open writes a file to the writer. -func (t *T) Open() error { - c := t.NewCategory(t.Cat) +// DumpFile writes a file to the writer. +func (t *T) DumpFile() error { + puzzle := transpile.NewFsPuzzle(t.fs) - switch t.Filename { + switch t.filename { case "puzzle.json", "": // BUG(neale): we need a way to tell the transpiler to strip answers - p, err := c.Puzzle(t.Points) + p, err := puzzle.Puzzle() if err != nil { return err } @@ -99,14 +116,14 @@ func (t *T) Open() error { if err != nil { return err } - t.w.Write(jp) + t.Stdout.Write(jp) default: - f, err := c.Open(t.Points, t.Filename) + f, err := puzzle.Open(t.filename) if err != nil { return err } defer f.Close() - if _, err := io.Copy(t.w, f); err != nil { + if _, err := io.Copy(t.Stdout, f); err != nil { return err } } @@ -114,32 +131,43 @@ func (t *T) Open() error { return nil } -// Mothball writes a mothball to the writer. -func (t *T) Mothball() error { - c := t.NewCategory(t.Cat) +// DumpMothball writes a mothball to the writer. +func (t *T) DumpMothball() error { + c := transpile.NewFsCategory(t.fs, "") mb, err := transpile.Mothball(c) if err != nil { return err } - if _, err := io.Copy(t.w, mb); err != nil { + if _, err := io.Copy(t.Stdout, mb); err != nil { return err } return nil } -// NewCategory returns a new Fs-backed category. -func (t *T) NewCategory(name string) transpile.Category { - return transpile.NewFsCategory(t.Fs, name) +// CheckAnswer prints whether an answer is correct. +func (t *T) CheckAnswer() error { + c := transpile.NewFsPuzzle(t.fs) + if c.Answer(t.answer) { + fmt.Fprintln(t.Stdout, "correct") + } else { + fmt.Fprintln(t.Stdout, "wrong") + } + return nil } func main() { // XXX: Convert puzzle.py to standalone thingies t := &T{ - w: os.Stdout, + Stdout: os.Stdout, + Stderr: os.Stderr, + Args: os.Args, } - action := t.ParseArgs() - if err := t.Handle(action); err != nil { + cmd, err := t.ParseArgs() + if err != nil { + log.Fatal(err) + } + if err := cmd(); err != nil { log.Fatal(err) } } diff --git a/cmd/transpile/main_test.go b/cmd/transpile/main_test.go index 77a6bdb..67c353a 100644 --- a/cmd/transpile/main_test.go +++ b/cmd/transpile/main_test.go @@ -3,7 +3,7 @@ package main import ( "bytes" "encoding/json" - "strings" + "log" "testing" "github.com/dirtbags/moth/pkg/transpile" @@ -23,62 +23,51 @@ pre: --- YAML body `) -var testMothRfc822 = []byte(`author: test -Author: Arthur -author: Fred Flintstone -answer: RFC822 answer - -RFC822 body -`) func newTestFs() afero.Fs { fs := afero.NewMemMapFs() afero.WriteFile(fs, "cat0/1/puzzle.md", testMothYaml, 0644) - afero.WriteFile(fs, "cat0/1/moo.txt", []byte("Moo."), 0644) - afero.WriteFile(fs, "cat0/2/puzzle.md", testMothRfc822, 0644) + afero.WriteFile(fs, "cat0/1/moo.txt", testMothYaml, 0644) + afero.WriteFile(fs, "cat0/2/puzzle.moth", testMothYaml, 0644) afero.WriteFile(fs, "cat0/3/puzzle.moth", testMothYaml, 0644) afero.WriteFile(fs, "cat0/4/puzzle.md", testMothYaml, 0644) afero.WriteFile(fs, "cat0/5/puzzle.md", testMothYaml, 0644) - afero.WriteFile(fs, "cat0/10/puzzle.md", []byte(`--- -Answers: - - moo -Authors: - - bad field ---- -body -`), 0644) - afero.WriteFile(fs, "cat0/20/puzzle.md", []byte("Answer: no\nBadField: yes\n\nbody\n"), 0644) - afero.WriteFile(fs, "cat0/21/puzzle.md", []byte("Answer: broken\nSpooon\n"), 0644) - afero.WriteFile(fs, "cat0/22/puzzle.md", []byte("---\nanswers:\n - pencil\npre:\n unused-field: Spooon\n---\nSpoon?\n"), 0644) - afero.WriteFile(fs, "cat1/93/puzzle.md", []byte("Answer: no\n\nbody"), 0644) - afero.WriteFile(fs, "cat1/barney/puzzle.md", testMothYaml, 0644) + afero.WriteFile(fs, "cat0/10/puzzle.md", testMothYaml, 0644) afero.WriteFile(fs, "unbroken/1/puzzle.md", testMothYaml, 0644) - afero.WriteFile(fs, "unbroken/1/moo.txt", []byte("Moo."), 0644) - afero.WriteFile(fs, "unbroken/2/puzzle.md", testMothRfc822, 0644) + afero.WriteFile(fs, "unbroken/2/puzzle.md", testMothYaml, 0644) return fs } +func (tp T) Run(args ...string) error { + tp.Args = append([]string{"transpile"}, args...) + command, err := tp.ParseArgs() + log.Println(tp.fs) + if err != nil { + return err + } + return command() +} + func TestEverything(t *testing.T) { stdout := new(bytes.Buffer) + stderr := new(bytes.Buffer) tp := T{ - w: stdout, - Fs: newTestFs(), + Stdout: stdout, + Stderr: stderr, + BaseFs: newTestFs(), } - if err := tp.Handle("inventory"); err != nil { + if err := tp.Run("inventory"); err != nil { t.Error(err) } - if strings.TrimSpace(stdout.String()) != `{"cat0":[1,2,3,4,5,10,20,21,22],"cat1":[93],"unbroken":[1,2]}` { + if stdout.String() != "cat0 1 2 3 4 5 10\nunbroken 1 2\n" { t.Errorf("Bad inventory: %#v", stdout.String()) } stdout.Reset() - tp.Cat = "cat0" - tp.Points = 1 - if err := tp.Handle("open"); err != nil { + if err := tp.Run("open", "-dir=cat0/1"); err != nil { t.Error(err) } - p := transpile.Puzzle{} if err := json.Unmarshal(stdout.Bytes(), &p); err != nil { t.Error(err) @@ -88,8 +77,7 @@ func TestEverything(t *testing.T) { } stdout.Reset() - tp.Filename = "moo.txt" - if err := tp.Handle("open"); err != nil { + if err := tp.Run("open", "-dir=cat0/1", "-file=moo.txt"); err != nil { t.Error(err) } if stdout.String() != "Moo." { @@ -97,8 +85,9 @@ func TestEverything(t *testing.T) { } stdout.Reset() - tp.Cat = "unbroken" - if err := tp.Handle("mothball"); err != nil { + if err := tp.Run("mothball", "-dir=cat0"); err != nil { + t.Log(tp.BaseFs) + t.Log(tp.fs) t.Error(err) } if stdout.Len() < 200 { diff --git a/example-puzzles/example/3/mkpuzzle b/example-puzzles/example/3/mkpuzzle new file mode 100755 index 0000000..b23b47f --- /dev/null +++ b/example-puzzles/example/3/mkpuzzle @@ -0,0 +1,34 @@ +#! /bin/sh + +number=$(seq 20 500 | shuf -n 1) +answer=$(echo $(grep -v "['A-Z]" /usr/share/dict/words | shuf -n 4)) + +case "$1:$2" in + :) + cat <Dynamic puzzles are provided with a JSON-generating mkpuzzles program in the puzzle directory.

", + "Attachments": ["salad.jpg"] + }, + "Answers": [ + "$answer" + ], + "Debug": { + "Summary": "Dynamic puzzles", + "Hints": [ + "Check the debug output to get the answer." + ], + "Errors": [], + "Log": [ + "$number is a positive integer" + ] + } +} +EOT + ;; + -file:salad.jpg) + cat salad.jpg + ;; +esac diff --git a/pkg/transpile/category.go b/pkg/transpile/category.go index 22c57f2..fb67339 100644 --- a/pkg/transpile/category.go +++ b/pkg/transpile/category.go @@ -90,12 +90,12 @@ func (c FsCategory) Inventory() ([]int, error) { // Puzzle returns a Puzzle structure for the given point value. func (c FsCategory) Puzzle(points int) (Puzzle, error) { - return NewFsPuzzle(c.fs, points).Puzzle() + return NewFsPuzzlePoints(c.fs, points).Puzzle() } // Open returns an io.ReadCloser for the given filename. func (c FsCategory) Open(points int, filename string) (ReadSeekCloser, error) { - return NewFsPuzzle(c.fs, points).Open(filename) + return NewFsPuzzlePoints(c.fs, points).Open(filename) } // Answer checks whether an answer is correct. diff --git a/pkg/transpile/inventory.go b/pkg/transpile/inventory.go index 946d146..8c6f750 100644 --- a/pkg/transpile/inventory.go +++ b/pkg/transpile/inventory.go @@ -12,7 +12,7 @@ type Inventory map[string][]int // FsInventory returns a mapping of category names to puzzle point values. func FsInventory(fs afero.Fs) (Inventory, error) { - dirEnts, err := afero.ReadDir(fs, ".") + dirEnts, err := afero.ReadDir(fs, "") if err != nil { log.Print(err) return nil, err diff --git a/pkg/transpile/mothball.go b/pkg/transpile/mothball.go index 1ef87f2..2126f9f 100644 --- a/pkg/transpile/mothball.go +++ b/pkg/transpile/mothball.go @@ -6,6 +6,7 @@ import ( "encoding/json" "fmt" "io" + "log" ) // Mothball packages a Category up for a production server run. @@ -35,6 +36,7 @@ func Mothball(c Category) (*bytes.Reader, error) { if err != nil { return nil, err } + log.Println(puzzlePath) puzzle, err := c.Puzzle(points) if err != nil { return nil, err diff --git a/pkg/transpile/puzzle.go b/pkg/transpile/puzzle.go index 965aa33..006b021 100644 --- a/pkg/transpile/puzzle.go +++ b/pkg/transpile/puzzle.go @@ -11,6 +11,7 @@ import ( "log" "net/mail" "os/exec" + "path" "strconv" "strings" "time" @@ -110,24 +111,40 @@ type PuzzleProvider interface { Answer(answer string) bool } -// NewFsPuzzle returns a new FsPuzzle for points. -func NewFsPuzzle(fs afero.Fs, points int) PuzzleProvider { - pfs := NewRecursiveBasePathFs(fs, strconv.Itoa(points)) - if info, err := pfs.Stat("mkpuzzle"); (err == nil) && (info.Mode()&0100 != 0) { - if command, err := pfs.RealPath(info.Name()); err != nil { - log.Println("Unable to resolve full path to", info.Name(), pfs) - } else { - return FsCommandPuzzle{ - fs: pfs, - command: command, - timeout: 2 * time.Second, +// NewFsPuzzle returns a new FsPuzzle. +func NewFsPuzzle(fs afero.Fs) PuzzleProvider { + var command string + + if info, err := fs.Stat("mkpuzzle"); (err == nil) && (info.Mode()&0100 != 0) { + // Try to get the actual path to the executable + if pfs, ok := fs.(*RecursiveBasePathFs); ok { + if command, err = pfs.RealPath(info.Name()); err != nil { + log.Println("Unable to resolve full path to", info.Name(), pfs) + } + } else if pfs, ok := fs.(*afero.BasePathFs); ok { + if command, err = pfs.RealPath(info.Name()); err != nil { + log.Println("Unable to resolve full path to", info.Name(), pfs) } } } - return FsPuzzle{ - fs: pfs, + if command != "" { + return FsCommandPuzzle{ + fs: fs, + command: command, + timeout: 2 * time.Second, + } } + + return FsPuzzle{ + fs: fs, + } + +} + +// NewFsPuzzlePoints returns a new FsPuzzle for points. +func NewFsPuzzlePoints(fs afero.Fs, points int) PuzzleProvider { + return NewFsPuzzle(NewRecursiveBasePathFs(fs, strconv.Itoa(points))) } // FsPuzzle is a single puzzle's directory. @@ -332,6 +349,7 @@ func (fp FsCommandPuzzle) Puzzle() (Puzzle, error) { defer cancel() cmd := exec.CommandContext(ctx, fp.command) + cmd.Dir = path.Dir(fp.command) stdout, err := cmd.Output() if err != nil { return Puzzle{}, err @@ -364,6 +382,7 @@ func (fp FsCommandPuzzle) Open(filename string) (ReadSeekCloser, error) { defer cancel() cmd := exec.CommandContext(ctx, fp.command, "-file", filename) + cmd.Dir = path.Dir(fp.command) out, err := cmd.Output() buf := nopCloser{bytes.NewReader(out)} if err != nil { @@ -379,6 +398,7 @@ func (fp FsCommandPuzzle) Answer(answer string) bool { defer cancel() cmd := exec.CommandContext(ctx, fp.command, "-answer", answer) + cmd.Dir = path.Dir(fp.command) out, err := cmd.Output() if err != nil { log.Printf("ERROR: checking answer: %s", err) diff --git a/pkg/transpile/puzzle_test.go b/pkg/transpile/puzzle_test.go index 7cc1bb9..914624e 100644 --- a/pkg/transpile/puzzle_test.go +++ b/pkg/transpile/puzzle_test.go @@ -13,7 +13,7 @@ func TestPuzzle(t *testing.T) { catFs := NewRecursiveBasePathFs(puzzleFs, "cat0") { - pd := NewFsPuzzle(catFs, 1) + pd := NewFsPuzzlePoints(catFs, 1) p, err := pd.Puzzle() if err != nil { t.Error(err) @@ -31,7 +31,7 @@ func TestPuzzle(t *testing.T) { } { - p, err := NewFsPuzzle(catFs, 2).Puzzle() + p, err := NewFsPuzzlePoints(catFs, 2).Puzzle() if err != nil { t.Error(err) } @@ -46,21 +46,21 @@ func TestPuzzle(t *testing.T) { } } - if _, err := NewFsPuzzle(catFs, 3).Puzzle(); err != nil { + if _, err := NewFsPuzzlePoints(catFs, 3).Puzzle(); err != nil { t.Error("Legacy `puzzle.moth` file:", err) } - if _, err := NewFsPuzzle(catFs, 99).Puzzle(); err == nil { + if _, err := NewFsPuzzlePoints(catFs, 99).Puzzle(); err == nil { t.Error("Non-existent puzzle", err) } - if _, err := NewFsPuzzle(catFs, 10).Puzzle(); err == nil { + if _, err := NewFsPuzzlePoints(catFs, 10).Puzzle(); err == nil { t.Error("Broken YAML") } - if _, err := NewFsPuzzle(catFs, 20).Puzzle(); err == nil { + if _, err := NewFsPuzzlePoints(catFs, 20).Puzzle(); err == nil { t.Error("Bad RFC822 header") } - if _, err := NewFsPuzzle(catFs, 21).Puzzle(); err == nil { + if _, err := NewFsPuzzlePoints(catFs, 21).Puzzle(); err == nil { t.Error("Boken RFC822 header") } @@ -69,7 +69,7 @@ func TestPuzzle(t *testing.T) { if err := afero.WriteFile(fs, "1/mkpuzzle", []byte("bleat"), 0755); err != nil { t.Error(err) } - p := NewFsPuzzle(fs, 1) + p := NewFsPuzzlePoints(fs, 1) if _, ok := p.(FsCommandPuzzle); !ok { t.Error("We didn't get an FsCommandPuzzle") } @@ -82,15 +82,15 @@ func TestPuzzle(t *testing.T) { func TestFsPuzzle(t *testing.T) { catFs := NewRecursiveBasePathFs(NewRecursiveBasePathFs(afero.NewOsFs(), "testdata"), "static") - if _, err := NewFsPuzzle(catFs, 1).Puzzle(); err != nil { + if _, err := NewFsPuzzlePoints(catFs, 1).Puzzle(); err != nil { t.Error(err) } - if _, err := NewFsPuzzle(catFs, 2).Puzzle(); err != nil { + if _, err := NewFsPuzzlePoints(catFs, 2).Puzzle(); err != nil { t.Error(err) } - mkpuzzleDir := NewFsPuzzle(catFs, 3) + mkpuzzleDir := NewFsPuzzlePoints(catFs, 3) if _, err := mkpuzzleDir.Puzzle(); err != nil { t.Error(err) } diff --git a/theme/basic.css b/theme/basic.css index e7bb81e..f00195e 100644 --- a/theme/basic.css +++ b/theme/basic.css @@ -91,9 +91,15 @@ input:invalid { #devel { - background-color: #c88; + background-color: #eee; color: black; } +#devel .string { + color: #9c27b0; +} +#devel .body { + background-color: #ffc107; +} .kvpair { border: solid black 2px; } diff --git a/theme/index.html b/theme/index.html index f72f5e1..488e452 100644 --- a/theme/index.html +++ b/theme/index.html @@ -5,7 +5,6 @@ - diff --git a/theme/moth-pwa.js b/theme/moth-pwa.js deleted file mode 100644 index 780bd1d..0000000 --- a/theme/moth-pwa.js +++ /dev/null @@ -1,17 +0,0 @@ -function pwa_init() { - if ('serviceWorker' in navigator) { - navigator.serviceWorker.register("./sw.js").then(function(reg) { - }) - .catch(err => { - console.warn("Error while registering service worker", err) - }) - } else { - console.log("Service workers not supported. Some offline functionality may not work") - } -} - -if (document.readyState === "loading") { - document.addEventListener("DOMContentLoaded", pwa_init) -} else { - pwa_init() -} diff --git a/theme/puzzle.html b/theme/puzzle.html index a7be166..37206f6 100644 --- a/theme/puzzle.html +++ b/theme/puzzle.html @@ -5,7 +5,6 @@ - diff --git a/theme/sw.js b/theme/sw.js deleted file mode 100644 index 8bdb2b0..0000000 --- a/theme/sw.js +++ /dev/null @@ -1,49 +0,0 @@ -var cacheName = "moth:v1" -var content = [ - "index.html", - "basic.css", - "puzzle.js", - "puzzle.html", - "scoreboard.html", - "moth.js", - "sw.js", - "points.json", -] - -self.addEventListener("install", function(e) { - e.waitUntil( - caches.open(cacheName).then(function(cache) { - return cache.addAll(content).then( - function() { - self.skipWaiting() - }) - }) - ) -}) - -/* Attempt to fetch live resources, first, then fall back to cache */ -self.addEventListener('fetch', function(event) { - let cache_used = false - - event.respondWith( - fetch(event.request) - .catch(function(evt) { - //console.log("Falling back to cache for " + event.request.url) - cache_used = true - return caches.match(event.request, {ignoreSearch: true}) - }).then(function(res) { - if (res && res.ok) { - let res_clone = res.clone() - if (! cache_used && event.request.method == "GET" ) { - caches.open(cacheName).then(function(cache) { - cache.put(event.request, res_clone) - //console.log("Storing " + event.request.url + " in cache") - }) - } - return res - } else { - console.log("Failed to retrieve resource") - } - }) - ) -})