From 874c526849df152a67950617abc7d96309bcd1b4 Mon Sep 17 00:00:00 2001 From: Amolith Date: Mon, 6 Jun 2022 01:03:56 -0400 Subject: [PATCH] Finish lots of things up Big commit because I want in the zone and getting a lot of crap done. This finished implementing the dashboard functionality, all of the API, normalises some logging, and cleans some other stuff up. The dashboard still needs improvements (#7), the default config values need to be written to a file if the file doesn't already exist (#8), and the status messages from the backend need to be displayed in the dashboard (#10). --- createHandler.go | 2 - helperfuncs.go | 55 ++++++++++++++++- readHandler.go | 3 +- root.go | 99 ++++++++++++++++++++++++++----- templates/edit.html | 86 +++++++++++++++++++++++++++ templates/home_authenticated.html | 3 +- updateHandler.go | 49 ++++----------- 7 files changed, 241 insertions(+), 56 deletions(-) create mode 100644 templates/edit.html diff --git a/createHandler.go b/createHandler.go index 6e7bab184afc94e6b0f5d61d36c0c2ccd92103a7..e4a9a3fd31fd39c0319f8fd1729171a3cc01f15c 100644 --- a/createHandler.go +++ b/createHandler.go @@ -37,8 +37,6 @@ func (m *model) createHandler(writer http.ResponseWriter, request *http.Request) return } - log.Println("Saving \"" + url + "\" mapped to \"" + name + "\"") - response := m.create(name, url) writer.Write([]byte(response)) diff --git a/helperfuncs.go b/helperfuncs.go index e314184168df1bccfca5360ba27e5c068603f51b..1ada50b9530e6d7e694792783075105bdecfc037 100644 --- a/helperfuncs.go +++ b/helperfuncs.go @@ -2,6 +2,9 @@ package main import ( "fmt" + "log" + "strings" + "text/template" "github.com/dgraph-io/badger/v3" ) @@ -17,9 +20,19 @@ func (m model) create(name string, url string) string { return fmt.Sprint(err) } + log.Println("\"" + url + "\" mapped to \"" + name + "\"") return fmt.Sprint("URL mapped to ", name, "\n") } +// Update modifies a shortened link +func (m model) update(name string, url string, oldName string) string { + m.create(name, url) + if name != oldName { + m.delete(oldName) + } + return fmt.Sprint("\"" + url + "\" mapped to \"" + name + "\"") +} + // Delete removes a shortened URL from the database given its name. func (m model) delete(name string) string { err := m.database.Update(func(txn *badger.Txn) error { @@ -29,7 +42,8 @@ func (m model) delete(name string) string { return fmt.Sprint(err) } - return fmt.Sprint("\"", name, "\" has been deleted") + log.Println("\"" + name + "\" has been deleted") + return fmt.Sprint("\"" + name + "\" has been deleted\n") } // nameExists returns true if the provided name is already stored in the @@ -47,3 +61,42 @@ func (m model) nameExists(name string) bool { } return false } + +// unauthenticated serves the unauthenticated home page to the visitor +func unauthenticated() string { + home, err := templates.ReadFile("templates/home_unauthenticated.html") + if err != nil { + log.Fatalln(err) + } + return string(home) +} + +// authenticated serves the authenticated dashboard to the user +func (m model) authenticated() string { + dash, err := templates.ReadFile("templates/home_authenticated.html") + if err != nil { + log.Fatalln(err) + } + tmpl, err := template.New("authenticated").Parse(string(dash)) + page := new(strings.Builder) + tmpl.Execute(page, m.genTable()) + return page.String() +} + +type EditFields struct { + URL string + Name string +} + +// update serves the editing interface to the user +func (m model) edit(name string, url string) string { + fields := EditFields{url, name} + editPage, err := templates.ReadFile("templates/edit.html") + if err != nil { + log.Fatalln(err) + } + tmpl, err := template.New("editPage").Parse(string(editPage)) + page := new(strings.Builder) + tmpl.Execute(page, fields) + return page.String() +} diff --git a/readHandler.go b/readHandler.go index bd17fbe4dd34aa762d818d03ac984d98e0de1f26..cf821c45a19cb7267d9688bd051c60ec1a54dd24 100644 --- a/readHandler.go +++ b/readHandler.go @@ -12,9 +12,8 @@ import ( func (m model) readHandler(writer http.ResponseWriter, request *http.Request) { token := request.Header.Get("Authorization") token = strings.TrimPrefix(token, "Bearer ") - cookie, _ := request.Cookie("access_token") - if token != m.AccessToken && cookie.Value != m.AccessToken { + if token != m.AccessToken { http.Error(writer, "401 Unauthorized: You do not have permission to view shortlinks", 403) return } diff --git a/root.go b/root.go index 74705f219dfa9d1804c2f8579721745e26b3ecf5..7496b9564a48c4c9000057743e3a5add409a6b99 100644 --- a/root.go +++ b/root.go @@ -5,9 +5,10 @@ import ( "io" "log" "net/http" + "net/url" "strings" - "text/template" + "github.com/dchest/uniuri" "github.com/dgraph-io/badger/v3" ) @@ -34,22 +35,92 @@ func (m model) root(writer http.ResponseWriter, request *http.Request) { } cookie, err := request.Cookie("access_token") - if err != nil { - home, err := templates.ReadFile("templates/home_unauthenticated.html") - if err != nil { - log.Fatalln(err) + if err != nil || cookie.Value != m.AccessToken { + io.WriteString(writer, unauthenticated()) + return + } + + query := request.URL.Query() + + action := query.Get("action") + if len(action) == 0 { + io.WriteString(writer, m.authenticated()) + } + + destination := query.Get("url") + name := query.Get("name") + oldName := query.Get("oldName") + + var message string + + if action == "create" { + if len(destination) == 0 { + message = "URL field is required" + http.Redirect(writer, request, "/?message="+message, 302) + return } - io.WriteString(writer, string(home)) + + if len(name) == 0 { + name = uniuri.NewLen(4) + for m.nameExists(name) { + name = uniuri.NewLen(4) + log.Println("Generated new name:", name) + } + } else if m.nameExists(name) { + http.Error(writer, "406 Not Acceptable: A shortened URL with this name already exists", 406) + message = "A shortened URL with this name already exists" + http.Redirect(writer, request, "/?message="+message, 302) + return + } + + message = url.QueryEscape(m.create(name, destination)) + http.Redirect(writer, request, "/?message="+message, 302) return } - if cookie.Value == m.AccessToken { - dash, err := templates.ReadFile("templates/home_authenticated.html") - if err != nil { - log.Fatalln(err) + if action == "edit" { + io.WriteString(writer, m.edit(name, destination)) + } + + if action == "update" { + if len(destination) == 0 { + message = "URL field is required" + http.Redirect(writer, request, "/?message="+message, 302) + return + } + + if len(name) == 0 { + message = "Name field is required" + http.Redirect(writer, request, "/?message="+message, 302) + return + } + + if len(oldName) == 0 { + message = "oldName field is required" + http.Redirect(writer, request, "/?message="+message, 302) + return + } + + if len(name) != 0 && oldName != name { + if m.nameExists(name) { + message = "A shortened URL with this name already exists" + http.Redirect(writer, request, "/?message="+message, 302) + return + } + } + + message := url.QueryEscape(m.update(name, destination, oldName)) + http.Redirect(writer, request, "/?message="+message, 302) + } + + if action == "delete" { + if len(name) == 0 { + message = "Name field is required" + http.Redirect(writer, request, "/?message="+message, 302) } - tmpl, err := template.New("authenticated").Parse(string(dash)) - tmpl.Execute(writer, m.genTable()) + message := url.QueryEscape(m.delete(name)) + log.Println(message) + http.Redirect(writer, request, "/?message="+message, 302) } } @@ -67,8 +138,8 @@ func (m model) genTable() string { table = table + fmt.Sprintf(`

%s

%s

- -`, k, v) + EditDelete +`, k, v, k, url.QueryEscape(string(v)), k) return nil }) if err != nil { diff --git a/templates/edit.html b/templates/edit.html new file mode 100644 index 0000000000000000000000000000000000000000..ab6cb7c65c2e50161d7814ae958d6f79ae94ee41 --- /dev/null +++ b/templates/edit.html @@ -0,0 +1,86 @@ + + + + umu + + + + + + +

edit

+
+
+ + +
+
+ + +
+ + + +
+ + diff --git a/templates/home_authenticated.html b/templates/home_authenticated.html index 4064eceed75913e6e25e90370c4758d386cc39de..8758607e5600ebe84d527333a259abbd074e5e0b 100644 --- a/templates/home_authenticated.html +++ b/templates/home_authenticated.html @@ -78,7 +78,8 @@ - + +
Shortened URLs diff --git a/updateHandler.go b/updateHandler.go index 0c79806a68b510fcc030f094b8c3041a14440407..cc996bd4c5c15412920163ef3aff96b1092e61e9 100644 --- a/updateHandler.go +++ b/updateHandler.go @@ -2,11 +2,8 @@ package main import ( "fmt" - "log" "net/http" "strings" - - "github.com/dgraph-io/badger/v3" ) func (m *model) updateHandler(writer http.ResponseWriter, request *http.Request) { @@ -20,53 +17,33 @@ func (m *model) updateHandler(writer http.ResponseWriter, request *http.Request) return } + oldName := query.Get("oldName") name := query.Get("name") - new_name := query.Get("new_name") - new_url := query.Get("new_url") + destination := query.Get("url") + + if len(oldName) == 0 { + http.Error(writer, "400 Bad Request: oldName parameter is required", 400) + return + } if len(name) == 0 { http.Error(writer, "400 Bad Request: name parameter is required", 400) return } - if len(new_name) == 0 && len(new_url) == 0 { - http.Error(writer, "400 Bad Request: You may update either the entry's URL or name but not both", 400) + if len(destination) == 0 { + http.Error(writer, "400 Bad Request: url parameter is required", 400) return } - if len(new_name) != 0 { - if m.nameExists(new_name) { + if len(name) != 0 && oldName != name { + if m.nameExists(name) { http.Error(writer, "406 Not Acceptable: A shortened URL with this name already exists", 406) return } - name = new_name } - var url string - err := m.database.View(func(txn *badger.Txn) error { - value, err := txn.Get([]byte(name)) - if err != nil { - return err - } - url = value.String() - return nil - }) - if err != nil { - log.Println("Error", err) - } - - if len(new_url) != 0 { - url = new_url - } - - log.Println("Mapping \"" + name + "\" to \"" + url + "\"") - - err = m.database.Update(func(txn *badger.Txn) error { - return txn.Set([]byte(name), []byte(url)) - }) - if err != nil { - log.Println(err) - } + m.update(name, destination, oldName) - http.Error(writer, fmt.Sprint("\"", name, "\" mapped to \"", url, "\""), 200) + http.Error(writer, fmt.Sprint("\"", name, "\" mapped to \"", destination, "\""), 200) }