summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--code/index.tmpl.html162
-rw-r--r--code/noelan.go178
2 files changed, 261 insertions, 79 deletions
diff --git a/code/index.tmpl.html b/code/index.tmpl.html
index f60ecf6..4c392bb 100644
--- a/code/index.tmpl.html
+++ b/code/index.tmpl.html
@@ -11,6 +11,8 @@ The `global_all_data` object has all the data necessary for the application to w
TODO(luca): Get rid of PicoCSS, because it does not make a good responsive UI.
+TODO(luca): Check error messages maybe sending too much information. (e.g., "invalid person" can be bruteforced to find the person's names)
+
-->
{{ end }}
@@ -74,6 +76,27 @@ textarea {
}
}
+div#pin-login {
+ margin: 2em 5em 0em 5em;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+}
+div#pin-login > input {
+ display: flex;
+ flex: 5;
+}
+button#pin-login-button {
+ display: flex;
+ flex: 1;
+}
+div.pin-text-container {
+ display: flex;
+ align-items: center;
+}
+div.pin-text-container > span {
+}
+
</style>
<script>
"use strict";
@@ -113,7 +136,21 @@ function findPersonByName(picking_person_name) {
//- Pages
function setPageChoose() {
+ let pinLoginNameID = "pin-login-name";
+ let pinLoginCodeID = "pin-login-code";
+ let pinLoginButtonID = "pin-login-button";
+
document.body.innerHTML = `
+ <div id="pin-login">
+ <form>
+ <fieldset role="group">
+ <input id="${pinLoginNameID}" type="text" placeholder="Nom">
+ <input id="${pinLoginCodeID}" type="text" placeholder="Code">
+ <input id="${pinLoginButtonID}" type="submit" value="connecter">
+ </fieldset>
+ </form>
+ </div>
+
<div class="centered">
<div class="container">
<h1>Qui es-tu?</h1>
@@ -191,6 +228,51 @@ function setPageChoose() {
}); // button click even listener
} // has_picked
} // for loop
+
+ let login_button = document.getElementById(pinLoginButtonID);
+ login_button.addEventListener("click", function(event) {
+ event.preventDefault();
+
+ let name = document.getElementById(pinLoginNameID).value;
+ let code = document.getElementById(pinLoginCodeID).value;
+ console.log("name:", name);
+ console.log("code:", code);
+
+ let messageBody = {
+ name: name,
+ code: code
+ };
+ const postBody = new URLSearchParams(messageBody).toString();
+
+ fetch('/api/pin/', {
+ method: 'POST',
+ headers:
+ {
+ 'Content-Type': 'application/x-www-form-urlencoded'
+ },
+ body: postBody,
+ })
+ .then(function(response) {
+ if (!response.ok) {
+ console.error('Network response was not ok');
+ }
+ return response.json();
+ })
+ .then(function(response) {
+ global_all_data.token = response.Token;
+ global_all_data.thisName = name;
+ global_all_data.thisWishlist = response.ThisWishlist;
+ global_all_data.otherName = response.OtherName;
+ global_all_data.otherWishlist = response.OtherWishlist;
+ global_all_data.initialized = true;
+
+ localStorage.setItem(local_storage_key, JSON.stringify(global_all_data));
+
+ setPageThisPerson();
+ })
+ .catch(function(error) { console.error(error); });
+ });
+
}
function setPageOtherPerson() {
@@ -218,7 +300,7 @@ function setPageOtherPerson() {
});
let wishlist = document.getElementById(wishlistID);
- wishlist.textContent = global_all_data.otherWishlist;
+ wishlist.value = global_all_data.otherWishlist;
fetch(`/api/list/?user=${global_all_data.thisName}&name=${global_all_data.otherName}&token=${global_all_data.token}`)
.then(function(response) {
@@ -228,10 +310,10 @@ function setPageOtherPerson() {
return response.text();
})
.then(function(response) {
- if(wishlist.textContent != response)
+ if(wishlist.value != response)
{
global_all_data.otherWishlist = response;
- wishlist.textContent = response;
+ wishlist.value = response;
localStorage.setItem(local_storage_key, JSON.stringify(global_all_data));
}
})
@@ -243,7 +325,9 @@ function setPageOtherPerson() {
function setPageThisPerson() {
let showPersonButtonID = "show-person";
- let writeLetterButtonID = "write-letter";
+ let wishlistButtonID = "write-wishlist";
+ let getPinButtonID = "get-pin";
+ let pinTextID = "pin-text";
document.body.innerHTML = `
<div class="centered">
@@ -251,7 +335,11 @@ function setPageThisPerson() {
<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>
+ <button id="${wishlistButtonID}">Ma liste des souhaits</button>
+ <button id="${getPinButtonID}">Code PIN</button>
+ <div class="pin-text-container">
+ <span id="${pinTextID}"></span>
+ </div>
</div>
<h4><b>Infos:</b></h4>
<ul>
@@ -264,6 +352,26 @@ function setPageThisPerson() {
</div>
`;
+ let getPinButton = document.getElementById(getPinButtonID);
+ getPinButton.addEventListener("click", function(event) {
+ event.preventDefault();
+
+ fetch(`/api/pin/?name=${global_all_data.thisName}&token=${global_all_data.token}`)
+ .then(function(response) {
+ if (!response.ok) {
+ console.error('Network response was not ok');
+ }
+ return response.text();
+ })
+ .then(function(text_response) {
+ let pinText = document.getElementById(pinTextID);
+ pinText.innerText = text_response;
+ })
+ .catch(function(error) {
+ console.error('There was a problem with the fetch operation:', error);
+ });
+ });
+
let showPersonButton = document.getElementById(showPersonButtonID);
showPersonButton.addEventListener("click", function(event) {
event.preventDefault();
@@ -271,23 +379,23 @@ function setPageThisPerson() {
setPageOtherPerson();
});
- let writeLetterButton = document.getElementById(writeLetterButtonID);
- writeLetterButton.addEventListener("click", function(event) {
+ let writeWishlistButton = document.getElementById(wishlistButtonID);
+ writeWishlistButton.addEventListener("click", async function(event) {
event.preventDefault();
history.pushState({home: "/"}, "", "/");
- let sendLetterButtonID = "send-letter";
- let letterTextAreaID = "letter-text";
+ let sendWishlistButtonID = "send-wishlist";
+ let wishlistTextAreaID = "wishlist-text";
let backButtonID = "back";
document.body.innerHTML = `
<div class="container">
<h1><span class="name">Ma</span>&nbsp;liste des souhaits</h1>
- <textarea id="${letterTextAreaID}" rows="15" cols="40" placeholder="Vide..."></textarea>
+ <textarea id="${wishlistTextAreaID}" rows="15" cols="40" placeholder="Vide..."></textarea>
<div class="buttons">
<button id="${backButtonID}">retour</button>
- <button id="${sendLetterButtonID}">sauvegarder</button>
+ <button id="${sendWishlistButtonID}">sauvegarder</button>
</div>
</div>
`;
@@ -299,10 +407,10 @@ function setPageThisPerson() {
setPageThisPerson();
});
- let letterTextArea = document.getElementById(letterTextAreaID);
- letterTextArea.textContent = global_all_data.thisWishlist;
+ let wishlist = document.getElementById(wishlistTextAreaID);
+ wishlist.value = global_all_data.thisWishlist;
- fetch(`/api/list/?user=${global_all_data.thisName}&name=${global_all_data.thisName}&token=${global_all_data.token}`)
+ await fetch(`/api/list/?user=${global_all_data.thisName}&name=${global_all_data.thisName}&token=${global_all_data.token}`)
.then(function(response) {
if (!response.ok) {
console.error('Network response was not ok');
@@ -310,10 +418,18 @@ function setPageThisPerson() {
return response.text();
})
.then(function(response) {
- if(wishlist.textContent != response)
+ {{ if false }}
+ console.log("response:", response);
+ console.log(typeof(response));
+ console.log("wishlist.value:", wishlist.value);
+ console.log(typeof(wishlist.value));
+ console.log("wishlist.value === response:", wishlist.value === response);
+ {{ end }}
+
+ if(wishlist.value !== response)
{
global_all_data.thisWishlist = response;
- letterTextArea.textContent = response;
+ wishlist.value = response;
localStorage.setItem(local_storage_key, JSON.stringify(global_all_data));
}
})
@@ -321,20 +437,22 @@ function setPageThisPerson() {
console.error('There was a problem with the fetch operation:', error);
});
-
- let sendLetterButton = document.getElementById(sendLetterButtonID);
- sendLetterButton.addEventListener("click", function(event) {
+ let sendWishlistButton = document.getElementById(sendWishlistButtonID);
+ sendWishlistButton.addEventListener("click", function(event) {
event.preventDefault();
+ {{ if false }}
console.log("Sending list of", global_all_data.thisName);
+ console.log("content:", wishlist.value);
+ {{ end }}
- global_all_data.thisWishlist = letterTextArea.value
+ global_all_data.thisWishlist = wishlist.value;
localStorage.setItem(local_storage_key, JSON.stringify(global_all_data));
let messageBody = {
name: global_all_data.thisName,
token: global_all_data.token,
- text: letterTextArea.value,
+ text: wishlist.value,
};
const postBody = new URLSearchParams(messageBody).toString();
@@ -362,7 +480,7 @@ function setPageThisPerson() {
.catch(function(error) { console.error('Error:', error); });
}); // add event listener send button
- }); // add event listener write letter
+ }); // add event listener write wishlist
}
//- Main
diff --git a/code/noelan.go b/code/noelan.go
index 2e01b81..1e168a2 100644
--- a/code/noelan.go
+++ b/code/noelan.go
@@ -34,6 +34,7 @@
// - detect when you are offline
// - display a tooltip saying that you are offline
// TODO(luca): Remove names from here and add them through a config file
+// TODO(luca): Stupid / for scrapers & robots.txt
package noelan
@@ -45,6 +46,7 @@ import (
"flag"
"fmt"
"html/template"
+ "math"
"math/rand"
"net/http"
"os/signal"
@@ -65,6 +67,9 @@ type Person struct {
Wishlist string
HasPicked bool
Token int64
+
+ Code string
+ CodeIsValid bool
}
// - Globals
@@ -76,6 +81,15 @@ const GlobalDataFileName = "people.gob"
const GlobalDataDirectoryName = "gobs"
const GlobalPerson int = 2
+var GlobalMessages = map[string]string{
+ "NotImplemented": "Not implemented yet",
+ "InvalidPersonAndToken": "Invalid person and token combination",
+ "NoSuchPerson": "No such person",
+ "PersonAlreadyPicked": "Person already picked",
+ "InvalidPerson": "Invalid person",
+ "InvalidCode": "Invalid code",
+}
+
var GlobalNilPerson = Person{}
// - Serializing
@@ -182,21 +196,24 @@ func TemplateToString(template_contents string, people []Person, local_storage_k
}
}
- if has_picked_count < len(people) {
- err = template_response.ExecuteTemplate(&buf, "tirage", page_data)
- if err != nil {
- fmt.Println(err)
- }
- response = buf.String()
+ if has_picked_count >= len(people) {
+ page_data.People = []Person{}
+ }
+
+ err = template_response.ExecuteTemplate(&buf, "tirage", page_data)
+ 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
+ found_person := &GlobalNilPerson
+ found := false
for index, value := range people {
if name == value.Name {
@@ -209,6 +226,20 @@ func FindPersonByName(people []Person, name string) (bool, *Person) {
return found, found_person
}
+func FindPersonByNameAndValidate(people []Person, name string, token string) (bool, *Person) {
+ found, person := FindPersonByName(people, name)
+
+ if found {
+ tokenString := strconv.FormatInt(person.Token, 10)
+ if token != tokenString {
+ found = false
+ person = &GlobalNilPerson
+ }
+ }
+
+ return 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
@@ -235,27 +266,22 @@ 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, "ERROR: " + message, http.StatusNotFound)
+ if person == &GlobalNilPerson {
+ logger.Printf("ERROR: %s | %s %s %s %s\n",
+ message,
+ request.RemoteAddr, request.Method, request.URL, request.Form)
+ } else {
+ 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, "ERROR: "+message, http.StatusNotFound)
}
// - Main
func Run() {
- var 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 people = GlobalDefaultPeople
var local_storage_key int64
var people_initialized = false
var local_storage_key_initialized = false
@@ -463,21 +489,16 @@ func Run() {
text := request.FormValue("text")
token := request.FormValue("token")
- logger.Println("Edit wishlist of", name)
- found, person := FindPersonByName(people, name)
+ found, person := FindPersonByNameAndValidate(people, name, token)
if found {
- tokenString := strconv.FormatInt(person.Token, 10)
- if token == tokenString {
- person.Wishlist = text
- person.HasPicked = true
+ logger.Println("Edit wishlist of", name)
+ fmt.Printf("text: %#v\n", text)
+ person.Wishlist = text
- fmt.Fprintln(writer, "ok")
- } else {
- HttpError(logger, "invalid token", person, writer, request)
- }
+ fmt.Fprintln(writer, "ok")
} else {
- HttpError(logger, "no such person", &GlobalNilPerson, writer, request)
+ HttpError(logger, GlobalMessages["InvalidPersonAndToken"], person, writer, request)
}
} else if request.Method == http.MethodGet {
// @api_notes
@@ -491,28 +512,18 @@ func Run() {
name := params.Get("name")
token := params.Get("token")
- found, person := FindPersonByName(people, user)
-
+ found, person := FindPersonByNameAndValidate(people, user, token)
if found {
- tokenString := strconv.FormatInt(person.Token, 10)
-
- if token == tokenString {
-
- var response string
- if people[person.Other].Name == name {
- response = people[person.Other].Wishlist
- fmt.Fprint(writer, response)
- } else if person.Name == user {
- response = person.Wishlist
- fmt.Fprint(writer, response)
- } else {
- HttpError(logger, "invalid person: "+name, person, writer, request)
- }
+ if people[person.Other].Name == name {
+ fmt.Fprint(writer, people[person.Other].Wishlist)
+ } else if person.Name == user {
+ fmt.Fprint(writer, person.Wishlist)
} else {
- HttpError(logger, "invalid token", person, writer, request)
+ HttpError(logger, GlobalMessages["InvalidPerson"]+": "+name, person, writer, request)
}
+
} else {
- HttpError(logger, "no such person", &GlobalNilPerson, writer, request)
+ HttpError(logger, GlobalMessages["InvalidPersonAndToken"], person, writer, request)
}
}
})
@@ -537,13 +548,67 @@ func Run() {
json.NewEncoder(writer).Encode(response)
} else {
- HttpError(logger, "person already picked", person, writer, request)
+ HttpError(logger, GlobalMessages["PersonAlreadyPicked"], person, writer, request)
}
} else {
- HttpError(logger, "no such person", &GlobalNilPerson, writer, request)
+ HttpError(logger, GlobalMessages["NoSuchPerson"], person, writer, request)
}
})
+ http.HandleFunc("/api/pin/", func(writer http.ResponseWriter, request *http.Request) {
+ if request.Method == http.MethodGet {
+
+ params := request.URL.Query()
+ name := params.Get("name")
+ token := params.Get("token")
+ found, person := FindPersonByNameAndValidate(people, name, token)
+ if found {
+ logger.Println("Request pin for", person.Name)
+
+ t := seeded_rand.Float64()
+ // Linear interpolate code to get value between 0 - 999999 and then convert to string with padded zeroes
+ person.Code = fmt.Sprintf("%06d", int(math.Round((t+0)*(999999))))
+ person.CodeIsValid = true
+
+ fmt.Fprint(writer, person.Code)
+ } else {
+ HttpError(logger, GlobalMessages["InvalidPersonAndToken"], person, writer, request)
+ }
+
+ } else if request.Method == http.MethodPost {
+
+ name := request.FormValue("name")
+ code := request.FormValue("code")
+
+ found, person := FindPersonByName(people, name)
+ if found {
+
+ if person.CodeIsValid && code == person.Code {
+ logger.Println("Pin login for", person.Name)
+
+ type Response struct {
+ Token int64
+ ThisWishlist string
+ OtherName string
+ OtherWishlist string
+ }
+
+ other := people[person.Other]
+ response := Response{Token: person.Token, ThisWishlist: person.Wishlist, OtherName: other.Name, OtherWishlist: other.Wishlist}
+ json.NewEncoder(writer).Encode(response)
+
+ person.CodeIsValid = false
+ } else {
+ person.CodeIsValid = false
+ HttpError(logger, GlobalMessages["InvalidCode"], person, writer, request)
+ }
+ } else {
+ HttpError(logger, GlobalMessages["InvalidPerson"], person, writer, request)
+ }
+ }
+
+ })
+
// Execute the template before-hand since the contents won't change.
response := TemplateToString(GlobalPageHTML, people, local_storage_key, internal)
http.HandleFunc("/", func(writer http.ResponseWriter, request *http.Request) {
@@ -570,7 +635,6 @@ func Run() {
if err := http.ListenAndServe(address, nil); err != nil {
panic(err)
}
-
}
if did_work {