summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRaymaekers Luca <luca@spacehb.net>2025-11-06 00:37:27 +0100
committerRaymaekers Luca <luca@spacehb.net>2025-11-06 00:37:27 +0100
commite71b10f30e1fb10c203fd11314b6296631a39bfd (patch)
tree389c0687f16cfbe67049693f2cb8b72096168155
parente4d96353278d953c7746318e536f40b637476199 (diff)
Fixed bug where local storage key would not be set.; Added slow and internal mode options
-rw-r--r--index.tmpl.html28
-rw-r--r--main.go405
2 files changed, 259 insertions, 174 deletions
diff --git a/index.tmpl.html b/index.tmpl.html
index f339e4d..cedd378 100644
--- a/index.tmpl.html
+++ b/index.tmpl.html
@@ -67,7 +67,7 @@ let people =
{"name": "{{.Name}}", "has_picked": {{.HasPicked}},}, {{ end }}
];
-let local_storage_key = "{{.KeyID}}";
+let local_storage_key = "{{.Key}}";
let global_all_data = {
initialized: false,
@@ -97,11 +97,12 @@ function findPersonByName(picking_person_name) {
//- Pages
function setPageChoose() {
document.body.innerHTML = `
-<div>
- <h1>Qui es-tu?</h1>
- <div class="buttons">
- </div>
-</div>`;
+ <div>
+ <h1>Qui es-tu?</h1>
+ <div class="buttons">
+ </div>
+ </div>
+ `;
let buttons = document.querySelector("div.buttons");
for(let index = 0; index < people.length; index += 1)
@@ -204,17 +205,18 @@ function setPageThisPerson() {
document.body.innerHTML = `
<main class="container">
- <h1>Noël-An de &nbsp;<span class="name">${global_all_data.thisName}</span></h1>
+ <h1>Noël-An de &nbsp;<span class="name">${global_all_data.thisName}</span></h1>
<div class="buttons">
<button id="${showPersonButtonID}">Voir qui j'ai choisi</button>
<button id="${writeLetterButtonID}">Ma liste des souhaits</button>
</div>
- <h4><b>Infos:</b></h4>
- <ul>
- <li>Ne dévoilez à persone qui vous avez tiré au sort!</li>
- <li>Vous pouvez écrire une liste de souhaits, la personne ayant tiré votre nom y a accès.</li>
- <li>Il n'est pas obligé de respecter la liste ou d'en écrire une. Elle sert pour inspirer des idées de cadeaux.</li>
- </ul>
+ <h4><b>Infos:</b></h4>
+ <ul>
+ <li>Ne dévoilez à persone qui vous avez tiré au sort!</li>
+ <li>Vous pouvez écrire une liste de souhaits, la personne ayant tiré votre nom y a accès.</li>
+ <li>Il n'est pas obligé de respecter la liste ou d'en écrire une. Elle sert pour inspirer des idées de cadeaux.</li>
+ <li>Si vous avez un soucis n'hésitez-pas à me contacter !</li>
+ </ul>
</main>
`;
diff --git a/main.go b/main.go
index 1d63898..e3fd489 100644
--- a/main.go
+++ b/main.go
@@ -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