summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--code/index.tmpl.html (renamed from index.tmpl.html)0
-rw-r--r--code/noelan.go548
-rw-r--r--go.mod2
-rw-r--r--main.go537
5 files changed, 552 insertions, 536 deletions
diff --git a/.gitignore b/.gitignore
index ff16add..2406086 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,4 @@
tmp
people.gob
gobs
+noelan
diff --git a/index.tmpl.html b/code/index.tmpl.html
index cedd378..cedd378 100644
--- a/index.tmpl.html
+++ b/code/index.tmpl.html
diff --git a/code/noelan.go b/code/noelan.go
new file mode 100644
index 0000000..e5bbef9
--- /dev/null
+++ b/code/noelan.go
@@ -0,0 +1,548 @@
+// Documentation
+//
+// Secret santa app that's random so people don't have to worry about the picking process.
+// No emails or login, just use the domain.
+//
+// Run it with `go run .`
+//
+//
+// TODOs
+//
+// TODO: Command line flags
+// - serve to serve it
+// - unpick to unpick all
+// - editor (to have one where i can edit the gob file manually)
+//
+// TODO: Make it safer by doing this.
+// 1. Have no ID
+// 2. Choose name
+// 3. Get (and store in local storage)
+// - other person's name & list
+// - this person's name & list
+// - token to make requests
+// 4. Set this ID picked on server
+//
+// -> This data is used to know if the person has picked or not.
+//
+// 1. Already have an ID (in local storage)
+// 2. Get
+// - Wishlists (for sync)
+
+package noelan
+
+//- Libraries
+import (
+ "encoding/gob"
+ "encoding/json"
+ "errors"
+ "flag"
+ "fmt"
+ "html/template"
+ "math/rand"
+ "net/http"
+ "os/signal"
+ "strconv"
+ "strings"
+
+ _ "embed"
+ "io"
+ "log"
+ "os"
+ "time"
+)
+
+// - Types
+type Person struct {
+ Name string
+ Other int
+ Wishlist string
+ HasPicked bool
+ Token int64
+}
+
+//- Globals
+
+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 global_page_html string
+
+// - Globals
+var global_people = []Person{
+ {Name: "Nawel"},
+ {Name: "Tobias"},
+ {Name: "Luca"},
+ {Name: "Lola"},
+ {Name: "Aeris"},
+ {Name: "Lionel"},
+ {Name: "Aurélie"},
+ {Name: "Sean"},
+ {Name: "Émilie"},
+ {Name: "Yves"},
+ {Name: "Marthe"},
+}
+var global_people_initialized = false
+
+var global_version int = 2
+var global_data_file_name string = "people.gob"
+var global_data_directory_name string = "gobs"
+
+func EncodeWrapper(encoder *gob.Encoder, value any, logger *log.Logger) {
+ if err := encoder.Encode(value); err != nil {
+ logger.Fatalln(err)
+ }
+}
+
+// - Serializing
+func EncodeData(logger *log.Logger, file_name string) {
+
+ file, err := os.Create(file_name)
+ if err != nil {
+ logger.Fatalln(err)
+ }
+ defer file.Close()
+
+ 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) {
+ logger.Println("No data imported.")
+ } else if err != nil {
+ logger.Fatalln(err)
+ } else {
+ decoder := gob.NewDecoder(file)
+
+ var version int
+ DecodeWrapper(decoder, &version, logger)
+ logger.Printf("datafile@%d program@%d\n", version, global_version)
+
+ // 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_initialized = true
+
+ if err := file.Close(); err != nil {
+ logger.Fatalln("Error closing file", err)
+ }
+ }
+}
+
+// - Person
+func (person Person) String() string {
+ var digits string
+ if person.Token > 99999 {
+ digits = fmt.Sprintf("%d", person.Token)[:6]
+ } else {
+ digits = fmt.Sprintf("%d", person.Token)
+ }
+
+ return fmt.Sprintf("%s_%s(%t)", person.Name, digits, person.HasPicked)
+}
+
+func TemplateToString(template_contents string, people []Person, internal bool) string {
+ var buf strings.Builder
+ template_response, err := template.New("tirage").Parse(template_contents)
+ if err != nil {
+ fmt.Println(err)
+ }
+
+ 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)
+ }
+ response := buf.String()
+
+ return response
+}
+
+func FindPersonByName(people []Person, name string) (bool, *Person) {
+ var found_person *Person
+ var found bool
+
+ for index, value := range people {
+ if name == value.Name {
+ found_person = &people[index]
+ found = true
+ break
+ }
+ }
+
+ return found, found_person
+}
+
+func FindPersonByOtherName(people []Person, name string) (bool, *Person) {
+ var found_person *Person
+ var found bool
+
+ for index, person := range people {
+ if name == people[person.Other].Name {
+ found = true
+ found_person = &people[index]
+ break
+ }
+ }
+
+ return found, found_person
+}
+
+func ShufflePeople(rand *rand.Rand, people []Person, logger *log.Logger) {
+ // Get a shuffled list that has following constaints
+ // 1. One person cannot choose itself
+ // 2. Every person has picked another person
+ var list []int
+ correct := false
+ for !correct {
+ list = rand.Perm(len(people))
+
+ correct = true
+ for i, version := range list {
+ if version == i {
+ logger.Println("incorrect, need to reshuffle")
+ correct = false
+ break
+ }
+ }
+ }
+
+ // Initialize people
+ for index, value := range list {
+ people[index].Other = value
+ }
+}
+
+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 Run() {
+ var did_work bool
+ var internal, slow, save bool
+ var serve, shuffle, unpickall, show_people, reset_tokens bool
+ var add_person, remove_person, unpick string
+ var set_local_storage_key int64
+
+ flag.BoolVar(&internal, "internal", false, "run commands in internal mode")
+ flag.BoolVar(&slow, "slow", false, "run commands in slow mode")
+ flag.BoolVar(&save, "save", false, "force saving to data file")
+ flag.BoolVar(&shuffle, "shuffle", false, "shuffle people again")
+ flag.BoolVar(&reset_tokens, "reset_tokens", false, "reset tokens of people")
+ 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.BoolVar(&serve, "serve", false, "run http server")
+
+ flag.Parse()
+
+ 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)
+
+ // backup
+ {
+ file, err := os.Open(global_data_file_name)
+ defer file.Close()
+ if errors.Is(err, os.ErrNotExist) {
+ logger.Printf("Cannot create backup: file '%s' not found.\n", global_data_file_name)
+ } else if err != nil {
+ logger.Fatalln(err)
+ } else {
+ _, err := os.Stat(global_data_directory_name)
+ if os.IsNotExist(err) {
+ err = os.MkdirAll(global_data_directory_name, 0755)
+ if err != nil {
+ logger.Printf("Error creating directory: %v\n", err)
+ }
+ logger.Println("Directory created successfully")
+ } else if err != nil {
+ logger.Printf("Error checking directory: %v\n", err)
+ } else {
+ // Directory already exists
+ }
+
+ now := time.Now()
+ formatted_now := now.Format("060102_03_04_05")
+
+ destination_file_name := fmt.Sprintf("./%s/%s__%s", global_data_directory_name, formatted_now, global_data_file_name)
+ destination, err := os.Create(destination_file_name)
+ if err != nil {
+ logger.Fatalln("Error creating copy:", err)
+ }
+ defer destination.Close()
+
+ _, err = io.Copy(destination, file)
+ if err == nil {
+ logger.Println("Created backup.")
+ } else {
+ logger.Fatalln("Error creating backup:", err)
+ }
+ }
+ }
+
+ // Init people
+ {
+ DecodeData(logger, global_data_file_name)
+ if !global_people_initialized {
+ logger.Println("Initialize people.")
+ ShufflePeople(seeded_rand, global_people, logger)
+
+ for index := range global_people {
+ // NOTE(luca): since javascript cannot handle big numbers we crop them
+ global_people[index].Token = seeded_rand.Int63() / 10000
+ }
+
+ global_people_initialized = true
+ }
+
+ }
+
+ if save {
+ did_work = true
+ }
+
+ if len(add_person) > 0 {
+ global_people = append(global_people, Person{Name: add_person})
+ fmt.Println(global_people)
+ did_work = true
+ }
+
+ if len(remove_person) > 0 {
+ for index, person := range global_people {
+ if person.Name == remove_person {
+ global_people = append(global_people[:index], global_people[index+1:]...)
+ break
+ }
+ }
+ logger.Println("remove:", remove_person)
+ did_work = true
+ }
+
+ if reset_tokens {
+ did_work = true
+ // NOTE(luca): Users will need to pick again
+ global_local_storage_key_initialized = false
+
+ 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("reset tokens.")
+ }
+
+ if unpickall {
+ did_work = true
+ for index := range global_people {
+ global_people[index].HasPicked = false
+ }
+ logger.Println("Unpicked all.")
+ }
+
+ if shuffle {
+ did_work = true
+ ShufflePeople(seeded_rand, global_people, logger)
+ logger.Println("Shuffled people.")
+ }
+
+ if show_people {
+ for _, person := range global_people {
+ fmt.Printf("%12s [%15d] %t\n", person.Name, person.Token, person.HasPicked)
+ }
+ }
+
+ if len(unpick) > 0 {
+ did_work = true
+ logger.Println("unpick:", unpick)
+ found, person := FindPersonByName(global_people, unpick)
+ if found {
+ person.HasPicked = false
+ logger.Println(person)
+ } else {
+ logger.Fatalln("No such person")
+ }
+ }
+
+ if set_local_storage_key != 0 {
+ did_work = true
+ global_local_storage_key = set_local_storage_key
+ global_local_storage_key_initialized = true
+ }
+
+ if !global_local_storage_key_initialized {
+ logger.Println("Initialize local storage key.")
+ global_local_storage_key = rand.Int63()
+ global_local_storage_key_initialized = true
+ }
+
+ if serve {
+ did_work = true
+ logger.Println("local storage key:", global_local_storage_key)
+ logger.Println(global_people)
+
+ for index := range global_people {
+ fmt.Print(global_people[index].Other)
+ }
+ fmt.Print("\n")
+
+ go func() {
+ c := make(chan os.Signal, 1)
+ signal.Notify(c, os.Interrupt)
+ <-c
+
+ if did_work {
+ 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)
+ }
+ }
+ })
+
+ 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 {
+ HttpError(logger, "person already picked", person, writer, request)
+ }
+ } else {
+ HttpError(logger, "no such person", &nil_person, writer, request)
+ }
+ })
+
+ // Execute the template before-hand since the contents won't change.
+ response := TemplateToString(global_page_html, global_people, 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, internal)
+ } else {
+ logger.Println(err)
+ }
+ }
+
+ 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)
+ }
+
+ }
+
+ if did_work {
+ EncodeData(logger, global_data_file_name)
+ }
+
+ return
+}
diff --git a/go.mod b/go.mod
index d0d3d78..a98e6bf 100644
--- a/go.mod
+++ b/go.mod
@@ -1,3 +1,3 @@
-module m
+module noelan
go 1.25.3
diff --git a/main.go b/main.go
index 6812724..4a34862 100644
--- a/main.go
+++ b/main.go
@@ -1,540 +1,7 @@
-// Documentation
-//
-// Secret santa app that's random so people don't have to worry about the picking process.
-// No emails or login, just use the domain.
-//
-// Run it with `go run .`
-//
-//
-// TODOs
-//
-// TODO: Command line flags
-// - serve to serve it
-// - unpick to unpick all
-// - editor (to have one where i can edit the gob file manually)
-//
-// TODO: Make it safer by doing this.
-// 1. Have no ID
-// 2. Choose name
-// 3. Get (and store in local storage)
-// - other person's name & list
-// - this person's name & list
-// - token to make requests
-// 4. Set this ID picked on server
-//
-// -> This data is used to know if the person has picked or not.
-//
-// 1. Already have an ID (in local storage)
-// 2. Get
-// - Wishlists (for sync)
-
package main
-//- Libraries
-import (
- "encoding/gob"
- "encoding/json"
- "errors"
- "flag"
- "fmt"
- "html/template"
- "math/rand"
- "net/http"
- "os/signal"
- "strconv"
- "strings"
-
- _ "embed"
- "io"
- "log"
- "os"
- "time"
-)
-
-// - Types
-type Person struct {
- Name string
- Other int
- Wishlist string
- HasPicked bool
- Token int64
-}
-
-//- Globals
-
-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
-var global_people = []Person{
- {Name: "Nawel"},
- {Name: "Tobias"},
- {Name: "Luca"},
- {Name: "Lola"},
- {Name: "Aeris"},
- {Name: "Lionel"},
- {Name: "Aurélie"},
- {Name: "Sean"},
- {Name: "Émilie"},
- {Name: "Yves"},
- {Name: "Marthe"},
-}
-var global_people_initialized = false
-
-var global_version int = 2
-var global_data_file_name string = "people.gob"
-var global_data_directory_name string = "gobs"
-
-func EncodeWrapper(encoder *gob.Encoder, value any, logger *log.Logger) {
- if err := encoder.Encode(value); err != nil {
- logger.Fatalln(err)
- }
-}
-
-// - Serializing
-func EncodeData(logger *log.Logger, file_name string) {
-
- file, err := os.Create(file_name)
- if err != nil {
- logger.Fatalln(err)
- }
- defer file.Close()
-
- 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) {
- logger.Println("Datafile does not exist:", file_name)
- } else if err != nil {
- logger.Fatalln(err)
- } else {
-
- decoder := gob.NewDecoder(file)
-
- var version int
- DecodeWrapper(decoder, &version, logger)
- logger.Printf("datafile@%d program@%d\n", version, global_version)
-
- // 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
- }
+import "noelan/code"
- DecodeWrapper(decoder, &global_people, logger)
-
- logger.Printf("Imported %d people.\n", len(global_people))
-
- global_people_initialized = true
-
- if err := file.Close(); err != nil {
- logger.Fatalln("Error closing file", err)
- }
- }
-}
-
-// - Person
-func (person Person) String() string {
- var digits string
- if person.Token > 99999 {
- digits = fmt.Sprintf("%d", person.Token)[:6]
- } else {
- digits = fmt.Sprintf("%d", person.Token)
- }
-
- return fmt.Sprintf("%s_%s(%t)", person.Name, digits, person.HasPicked)
-}
-
-func TemplateToString(page_html string, people []Person, seed int64, internal bool) string {
- var buf strings.Builder
- template_response, err := template.New("tirage").Parse(page_html)
- if err != nil {
- fmt.Println(err)
- }
-
- 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)
- }
- response := buf.String()
-
- return response
-}
-
-func FindPersonByName(people []Person, name string) (bool, *Person) {
- var found_person *Person
- var found bool
-
- for index, value := range people {
- if name == value.Name {
- found_person = &people[index]
- found = true
- break
- }
- }
-
- return found, found_person
-}
-
-func FindPersonByOtherName(people []Person, name string) (bool, *Person) {
- var found_person *Person
- var found bool
-
- for index, person := range people {
- if name == people[person.Other].Name {
- found = true
- found_person = &people[index]
- break
- }
- }
-
- return found, found_person
-}
-
-func ShufflePeople(rand *rand.Rand, people []Person, logger *log.Logger) {
- // Get a shuffled list that has following constaints
- // 1. One person cannot choose itself
- // 2. Every person has picked another person
- var list []int
- correct := false
- for !correct {
- list = rand.Perm(len(people))
-
- correct = true
- for i, version := range list {
- if version == i {
- logger.Println("incorrect, need to reshuffle")
- correct = false
- break
- }
- }
- }
-
- // Initialize people
- for index, value := range list {
- people[index].Other = value
- }
-}
-
-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() {
- var did_work bool
- var internal, slow bool
- var serve, shuffle, unpickall, show_people, reset_tokens 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(&reset_tokens, "reset_tokens", false, "reset tokens of people")
- 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()
-
- 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)
-
- // backup
- {
- file, err := os.Open(global_data_file_name)
- defer file.Close()
- if errors.Is(err, os.ErrNotExist) {
- logger.Println("Datafile does not exist:", global_data_file_name)
- } else if err != nil {
- logger.Fatalln(err)
- } else {
- _, err := os.Stat(global_data_directory_name)
- if os.IsNotExist(err) {
- err = os.MkdirAll(global_data_directory_name, 0755)
- if err != nil {
- logger.Printf("Error creating directory: %v\n", err)
- }
- logger.Println("Directory created successfully")
- } else if err != nil {
- logger.Printf("Error checking directory: %v\n", err)
- } else {
- // Directory already exists
- }
-
- now := time.Now()
- formatted_now := now.Format("060102_03_04_05")
-
- destination_file_name := fmt.Sprintf("./%s/%s__%s", global_data_directory_name, formatted_now, global_data_file_name)
- destination, err := os.Create(destination_file_name)
- if err != nil {
- logger.Fatalln("Error creating copy:", err)
- }
- defer destination.Close()
-
- _, err = io.Copy(destination, file)
- if err == nil {
- logger.Println("Created backup.")
- } else {
- logger.Fatalln("Error creating backup:", err)
- }
- }
- }
-
- // Init people
- {
- DecodeData(logger, global_data_file_name)
- if !global_people_initialized {
- logger.Println("Initialize people.")
- ShufflePeople(seeded_rand, global_people, logger)
-
- for index := range global_people {
- // NOTE(luca): since javascript cannot handle big numbers we crop them
- global_people[index].Token = seeded_rand.Int63() / 10000
- }
-
- global_people_initialized = true
- }
-
- if !global_local_storage_key_initialized {
- logger.Println("Initialize local storage key.")
- global_local_storage_key = rand.Int63()
- global_local_storage_key_initialized = true
- }
- }
-
- if len(add_person) > 0 {
- global_people = append(global_people, Person{Name: add_person})
- fmt.Println(global_people)
- did_work = true
- }
-
- if len(remove_person) > 0 {
- for index, person := range global_people {
- if person.Name == remove_person {
- global_people = append(global_people[:index], global_people[index+1:]...)
- break
- }
- }
- logger.Println("remove:", remove_person)
- did_work = true
- }
-
- if reset_tokens {
- 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("reset tokens.")
- did_work = true
- }
-
- if unpickall {
- did_work = true
- for index := range global_people {
- global_people[index].HasPicked = false
- }
- logger.Println("Unpicked all.")
- }
-
- if shuffle {
- did_work = true
- ShufflePeople(seeded_rand, global_people, logger)
- logger.Println("Shuffled people.")
- }
-
- if show_people {
- for _, person := range global_people {
- fmt.Printf("%12s [%d] %t\n", person.Name, person.Token, person.HasPicked)
- }
- }
-
- if len(unpick) > 0 {
- did_work = true
- logger.Println("unpick:", unpick)
- found, person := FindPersonByName(global_people, unpick)
- if found {
- person.HasPicked = false
- logger.Println(person)
- } else {
- logger.Fatalln("No such person")
- }
- }
-
- if set_local_storage_key != 0 {
- did_work = true
- global_local_storage_key = set_local_storage_key
- global_local_storage_key_initialized = true
- }
-
- if serve {
- did_work = true
- logger.Println("local storage key:", global_local_storage_key)
- logger.Println(global_people)
-
- for index := range global_people {
- fmt.Print(global_people[index].Other)
- }
- fmt.Print("\n")
-
- go func() {
- c := make(chan os.Signal, 1)
- signal.Notify(c, os.Interrupt)
- <-c
-
- if did_work {
- 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)
- }
- }
- })
-
- 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 {
- HttpError(logger, "person already picked", person, writer, request)
- }
- } else {
- HttpError(logger, "no such person", &nil_person, writer, request)
- }
- })
-
- // 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)
- }
- }
-
- 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)
- }
-
- }
-
- if did_work {
- EncodeData(logger, global_data_file_name)
- }
-
- return
+ noelan.Run()
}