diff options
| author | Raymaekers Luca <luca@spacehb.net> | 2025-11-06 00:37:27 +0100 |
|---|---|---|
| committer | Raymaekers Luca <luca@spacehb.net> | 2025-11-06 00:37:27 +0100 |
| commit | e71b10f30e1fb10c203fd11314b6296631a39bfd (patch) | |
| tree | 389c0687f16cfbe67049693f2cb8b72096168155 /main.go | |
| parent | e4d96353278d953c7746318e536f40b637476199 (diff) | |
Fixed bug where local storage key would not be set.; Added slow and internal mode options
Diffstat (limited to 'main.go')
| -rw-r--r-- | main.go | 405 |
1 files changed, 244 insertions, 161 deletions
@@ -33,16 +33,16 @@ package main //- Libraries import ( "encoding/gob" - "encoding/json" + "encoding/json" "errors" + "flag" "fmt" "html/template" - "io" "math/rand" "net/http" "os/signal" + "strconv" "strings" - "strconv" _ "embed" "log" @@ -58,20 +58,16 @@ type Person struct { Token int64 } -type PageData struct { - People []Person - KeyID int64 -} - //- Globals -var DEBUG = true -var local_storage_key_id int64 +var nil_person = Person{Name: "nil"} +var global_local_storage_key int64 +var global_local_storage_key_initialized = false //go:embed index.tmpl.html var page_html string -//- Globals +// - Globals var global_people = []Person{ {Name: "Nawel"}, {Name: "Tobias"}, @@ -85,11 +81,18 @@ var global_people = []Person{ {Name: "Yves"}, {Name: "Marthe"}, } +var global_people_intialized = false + +var global_version int = 2 +var global_data_file_name string = "people.gob" -var global_version int = 1 -var data_file_name string = "people.gob" +func EncodeWrapper(encoder *gob.Encoder, value any, logger *log.Logger) { + if err := encoder.Encode(value); err != nil { + logger.Fatalln(err) + } +} -//- Serializing +// - Serializing func EncodeData(logger *log.Logger, file_name string) { file, err := os.Create(file_name) if err != nil { @@ -97,16 +100,21 @@ func EncodeData(logger *log.Logger, file_name string) { } defer file.Close() - enc := gob.NewEncoder(file) - if err := enc.Encode(global_version); err != nil { - logger.Fatalln(err) - } - if err := enc.Encode(global_people); err != nil { + encoder := gob.NewEncoder(file) + + EncodeWrapper(encoder, global_version, logger) + EncodeWrapper(encoder, global_local_storage_key, logger) + EncodeWrapper(encoder, global_people, logger) + + logger.Printf("saved %d people.\n", len(global_people)) +} + +func DecodeWrapper(decoder *gob.Decoder, value any, logger *log.Logger) { + if err := decoder.Decode(value); err != nil { logger.Fatalln(err) } } - func DecodeData(logger *log.Logger, file_name string) { file, err := os.Open(file_name) if errors.Is(err, os.ErrNotExist) { @@ -115,34 +123,36 @@ func DecodeData(logger *log.Logger, file_name string) { if err != nil { logger.Fatalln(err) } - EncodeData(logger, file_name) + EncodeData(logger, file_name) } else if err != nil { logger.Fatalln(err) } else { - dec := gob.NewDecoder(file) + decoder := gob.NewDecoder(file) var version int - if err := dec.Decode(&version); err != io.EOF && err != nil { - logger.Fatalln(err) - } - if version != global_version { - logger.Fatalf("Version mismatch for datafile@%d != package@%d\n", version, global_version) - } + DecodeWrapper(decoder, &version, logger) + logger.Printf("datafile@%d program@%d\n", version, global_version) - if err := dec.Decode(&global_people); err != nil && err != io.EOF { - logger.Fatalln(err) + // NOTE(luca): this will automatically migrate v1 to v2 + if version == 2 { + DecodeWrapper(decoder, &global_local_storage_key, logger) + global_local_storage_key_initialized = true } + DecodeWrapper(decoder, &global_people, logger) + logger.Printf("Imported %d people.\n", len(global_people)) + global_people_intialized = true + if err := file.Close(); err != nil { logger.Fatalln(err) } } } -//- Person +// - Person func (person Person) String() string { var digits string if person.Token > 99999 { @@ -151,17 +161,22 @@ func (person Person) String() string { digits = fmt.Sprintf("%d", person.Token) } - return fmt.Sprintf("%s_%s(%t)\n%s\n", - person.Name, digits, person.HasPicked, person.Wishlist) + return fmt.Sprintf("%s_%s(%t)", person.Name, digits, person.HasPicked) } -func TemplateToString(page_html string, people []Person, seed int64) string { +func TemplateToString(page_html string, people []Person, seed int64, internal bool) string { var buf strings.Builder - template_response, err := template.New("roulette").Parse(page_html) + template_response, err := template.New("tirage").Parse(page_html) if err != nil { fmt.Println(err) } - err = template_response.ExecuteTemplate(&buf, "roulette", PageData{people, local_storage_key_id}) + + type PageData struct { + People []Person + Key int64 + Internal bool + } + err = template_response.ExecuteTemplate(&buf, "tirage", PageData{People: people, Key: global_local_storage_key, Internal: internal}) if err != nil { fmt.Println(err) } @@ -225,163 +240,231 @@ func ShufflePeople(rand *rand.Rand, people []Person, logger *log.Logger) { } } +func HttpError(logger *log.Logger, message string, person *Person, writer http.ResponseWriter, request *http.Request) { + logger.Printf("Error for %s: %s | %s %s %s %s\n", + person.Name, message, + request.RemoteAddr, request.Method, request.URL, request.Form) + http.Error(writer, message, http.StatusNotFound) +} // - Main func main() { - logger := log.New(os.Stdout, "[noel] ", log.Ldate|log.Ltime) + var did_work bool + var internal, slow bool + var serve, shuffle, unpickall, show_people bool + var add_person, remove_person, unpick string + var set_local_storage_key int64 + + flag.BoolVar(&serve, "serve", false, "run http server") + flag.BoolVar(&internal, "internal", false, "run commands in internal mode") + flag.BoolVar(&slow, "slow", false, "run commands in slow mode") + flag.BoolVar(&shuffle, "shuffle", false, "shuffle people again") + flag.BoolVar(&unpickall, "unpickall", false, "unpick all people") + flag.BoolVar(&show_people, "show_people", false, "show people") + flag.StringVar(&add_person, "add_person", "", "add person by name") + flag.StringVar(&remove_person, "remove_person", "", "remove person by name") + flag.StringVar(&unpick, "unpick", "", "unpick person by name") + flag.Int64Var(&set_local_storage_key, "set_local_storage_key", 0, "Set the local storage key") + + flag.Parse() - seed := rand.Int63() - // seed = 1623876946084255669 + logger := log.New(os.Stdout, "[noel] ", log.Ldate|log.Ltime) + var seed int64 + if internal { + seed = rand.Int63() + } else { + seed = 7967946373046491984 + } logger.Println("seed:", seed) source := rand.NewSource(seed) seeded_rand := rand.New(source) rand.Seed(seed) - // Init people - { - ShufflePeople(seeded_rand, global_people, logger) - - local_storage_key_id = rand.Int63() - for index := range global_people { - global_people[index].Token = seeded_rand.Int63() / 10000 - } - - DecodeData(logger, data_file_name) + DecodeData(logger, global_data_file_name) - fmt.Println(global_people) - } + if unpickall { + for index := range global_people { + global_people[index].HasPicked = false + } + logger.Println("Unpicked all.") + did_work = true + } - go func() { - c := make(chan os.Signal, 1) - signal.Notify(c, os.Interrupt) - <-c + if shuffle { + ShufflePeople(seeded_rand, global_people, logger) + logger.Println("Shuffled people.") + did_work = true + } - EncodeData(logger, data_file_name) + if show_people { + for _, person := range global_people { + fmt.Printf("%12s[%d] %t\n", person.Name, person.Token, person.HasPicked) + } + } - logger.Println("data saved.") - os.Exit(0) - }() + if len(unpick) > 0 { + logger.Println("unpick:", unpick) + found, person := FindPersonByName(global_people, unpick) + if found { + person.HasPicked = false + logger.Println(person) + } else { + logger.Fatalln("No such person") + } + did_work = true + } - defer EncodeData(logger, data_file_name) + if len(add_person) > 0 { + logger.Println("add:", add_person) + did_work = true + } - http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("./assets")))) + if len(remove_person) > 0 { + logger.Println("remove:", remove_person) + did_work = true + } - // TODO: replace these by command line flags - // http.HandleFunc("/unpickall/", func(writer http.ResponseWriter, request *http.Request) { - // for index := range global_people { - // global_people[index].HasPicked = false - // } - // fmt.Fprintln(writer, "Done.") - // }) + if set_local_storage_key != 0 { + global_local_storage_key = set_local_storage_key + global_local_storage_key_initialized = true + did_work = true + } - // http.HandleFunc("/shuffle/", func(writer http.ResponseWriter, request *http.Request) { - // ShufflePeople(seeded_rand, global_people, logger) - // - // fmt.Fprintln(writer, "Done.") - // }) + if serve { + if !global_local_storage_key_initialized { + global_local_storage_key = rand.Int63() + global_local_storage_key_initialized = true + } - // http.HandleFunc("/addlola/", func(writer http.ResponseWriter, request *http.Request) { - // global_people = append(global_people, Person{Name:"Lola", Token:seeded_rand.Int63()}) - // - // fmt.Fprintln(writer, "Done.") - // }) + if !global_people_intialized { + ShufflePeople(seeded_rand, global_people, logger) - http.HandleFunc("/list/", func(writer http.ResponseWriter, request *http.Request) { - if request.Method == http.MethodPost { - name := request.FormValue("name") - text := request.FormValue("text") - token := request.FormValue("token") + for index := range global_people { + // NOTE(luca): since javascript cannot handle big numbers we crop them + global_people[index].Token = seeded_rand.Int63() / 10000 + } - logger.Println("Edit wishlist of", name) - found, person := FindPersonByName(global_people, name) + global_people_intialized = true + } - if found { - tokenString := strconv.FormatInt(person.Token, 10) - if token == tokenString { - person.Wishlist = text - person.HasPicked = true - logger.Println(global_people) - - fmt.Fprintln(writer, "ok") - } else { - http.Error(writer, "invalid token", http.StatusNotFound) - } - } else { - http.Error(writer, "no such person", http.StatusNotFound) + logger.Println("local storage key:", global_local_storage_key) + logger.Println(global_people) + + go func() { + c := make(chan os.Signal, 1) + signal.Notify(c, os.Interrupt) + <-c + + EncodeData(logger, global_data_file_name) + + logger.Println("data saved.") + os.Exit(0) + }() + + http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("./assets")))) + + http.HandleFunc("/list/", func(writer http.ResponseWriter, request *http.Request) { + if request.Method == http.MethodPost { + name := request.FormValue("name") + text := request.FormValue("text") + token := request.FormValue("token") + + logger.Println("Edit wishlist of", name) + found, person := FindPersonByName(global_people, name) + + if found { + tokenString := strconv.FormatInt(person.Token, 10) + if token == tokenString { + person.Wishlist = text + person.HasPicked = true + + fmt.Fprintln(writer, "ok") + } else { + HttpError(logger, "invalid token", person, writer, request) + } + } else { + HttpError(logger, "no such person", &nil_person, writer, request) + } + } else if request.Method == http.MethodGet { + params := request.URL.Query() + name := params.Get("name") + token := params.Get("token") + + found, person := FindPersonByName(global_people, name) + + if found { + tokenString := strconv.FormatInt(person.Token, 10) + + if token == tokenString { + fmt.Fprint(writer, global_people[person.Other].Wishlist) + } else { + HttpError(logger, "invalid token", person, writer, request) + } + } else { + HttpError(logger, "no such person", &nil_person, writer, request) + } } - } else if request.Method == http.MethodGet { + }) + + http.HandleFunc("/choose/", func(writer http.ResponseWriter, request *http.Request) { params := request.URL.Query() name := params.Get("name") - token := params.Get("token") - - found, person := FindPersonByName(global_people, name) - + found, person := FindPersonByName(global_people, name) if found { - logger.Println(len(token)) - tokenString := strconv.FormatInt(person.Token, 10) - - if token == tokenString { - fmt.Fprint(writer, global_people[person.Other].Wishlist) - } else { - http.Error(writer, "invalid token", http.StatusNotFound) - } + if !person.HasPicked { + person.HasPicked = true + + type Response struct { + Token int64 + ThisWishlist string + OtherName string + OtherWishlist string + } + other_person := global_people[person.Other] + + response := Response{person.Token, person.Wishlist, other_person.Name, other_person.Wishlist} + + json.NewEncoder(writer).Encode(response) + } else { + HttpError(logger, "person already picked", person, writer, request) + } } else { - http.Error(writer, "no such person", http.StatusNotFound) + HttpError(logger, "no such person", &nil_person, writer, request) } - } - }) - - http.HandleFunc("/choose/", func(writer http.ResponseWriter, request *http.Request) { - params := request.URL.Query() - name := params.Get("name") - found, person := FindPersonByName(global_people, name) - if found { - if !person.HasPicked { - person.HasPicked = true - - type Response struct { - Token int64 - ThisWishlist string - OtherName string - OtherWishlist string - } - other_person := global_people[person.Other] - - response := Response{person.Token, person.Wishlist, other_person.Name, other_person.Wishlist} - - json.NewEncoder(writer).Encode(response) - } else { - http.Error(writer, "person already picked", http.StatusNotFound) - } - } else { - http.Error(writer, "no such person", http.StatusNotFound) - } - }) - - // Execute the template before-hand since the contents won't change. - response := TemplateToString(page_html, global_people, seed) - http.HandleFunc("/", func(writer http.ResponseWriter, request *http.Request) { - if DEBUG { - buffer, err := os.ReadFile("index.tmpl.html") - if err != nil { - fmt.Print(err) + }) + + // Execute the template before-hand since the contents won't change. + response := TemplateToString(page_html, global_people, seed, internal) + http.HandleFunc("/", func(writer http.ResponseWriter, request *http.Request) { + if slow { + buffer, err := os.ReadFile("index.tmpl.html") + if err == nil { + file_contents := string(buffer) + response = TemplateToString(file_contents, global_people, seed, internal) + } else { + logger.Println(err) + } } - file_contents := string(buffer) - response = TemplateToString(file_contents, global_people, seed) + fmt.Fprint(writer, response) + }) + + var address string + if internal { + address = "0.0.0.0:15118" + } else { + address = "localhost:15118" + } + logger.Printf("Listening on http://%s\n", address) + if err := http.ListenAndServe(address, nil); err != nil { + panic(err) } - fmt.Fprint(writer, response) - }) - var address string - if DEBUG { - address = "0.0.0.0:15118" - } else { - address = "localhost:15118" + did_work = true } - logger.Printf("Listening on http://%s\n", address) - if err := http.ListenAndServe(address, nil); err != nil { - panic(err) + + if did_work { + EncodeData(logger, global_data_file_name) } return |
