From 37d93881e84b5cec0f05068c4b215f468cede0e0 Mon Sep 17 00:00:00 2001 From: Richard Huang Date: Mon, 11 May 2020 22:52:45 -0700 Subject: [PATCH] [server/handlers] Implement short url creation --- handlers/handlers.go | 35 +++++++++++++++++++++++++++++++++++ server.go | 1 + static/index.html | 2 +- utils/utils.go | 31 +++++++++++++++++++++++++++++++ 4 files changed, 68 insertions(+), 1 deletion(-) create mode 100644 utils/utils.go diff --git a/handlers/handlers.go b/handlers/handlers.go index a631642..8bd6c51 100644 --- a/handlers/handlers.go +++ b/handlers/handlers.go @@ -1,8 +1,10 @@ package handlers import ( + "../utils" "encoding/json" "fmt" + "log" "net/http" "github.com/gorilla/mux" @@ -50,3 +52,36 @@ func (a *App) GetURL(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "No such link") } } + +func (a *App) CreateShortURL(w http.ResponseWriter, r *http.Request) { + if err := r.ParseForm(); err != nil { + log.Fatal("Could not parse JSON") + } + reqURL := r.PostFormValue("url") + alias := r.PostFormValue("alias") + // Verify URL is valid + if !utils.IsValidUrl(reqURL) { + w.WriteHeader(http.StatusBadRequest) + fmt.Fprintf(w, "Invalid URL") + return + } + // Check if URL entry already exists + var urlEntry URLEntry + a.DB.Where("url = ?", reqURL).Find(&urlEntry) + if urlEntry.URL != "" { + alias = urlEntry.Alias + } else { + // Check if alias is already taken + for alias == "" || !a.DB.Where("alias = ?", alias).First(&urlEntry).RecordNotFound() { + alias = utils.RandString(6) + } + newURLEntry := &URLEntry{URL: reqURL, Alias: alias} + a.DB.Create(newURLEntry) + } + + // Write HTTP Response + shortlink := r.Host + "/s/" + alias + w.WriteHeader(http.StatusCreated) + w.Header().Set("Location", shortlink) + fmt.Fprintf(w, shortlink) +} diff --git a/server.go b/server.go index 5994fc3..ef1737b 100644 --- a/server.go +++ b/server.go @@ -14,6 +14,7 @@ func main() { r := mux.NewRouter() r.Handle("/", http.FileServer(http.Dir("./static"))).Methods("GET") + r.HandleFunc("/", a.CreateShortURL).Methods("POST") r.HandleFunc("/all", a.ListAll).Methods("GET") r.HandleFunc("/s/{alias:.*}", a.GetURL).Methods("GET") http.Handle("/", r) diff --git a/static/index.html b/static/index.html index b07c832..8bde7a1 100644 --- a/static/index.html +++ b/static/index.html @@ -10,7 +10,7 @@
- +

diff --git a/utils/utils.go b/utils/utils.go new file mode 100644 index 0000000..a72709f --- /dev/null +++ b/utils/utils.go @@ -0,0 +1,31 @@ +package utils + +import ( + "math/rand" + "net/url" + "time" +) + +// Charset for random string generator +const Charset = "abcdefghijklmnopqrstuvwxyz" + + "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" + +// Tests a string to determine if it is a well-structured url or not +func IsValidUrl(str string) bool { + u, err := url.Parse(str) + return err == nil && u.Scheme != "" && u.Host != "" +} + +// Generate a random alphanumeric string of given length +func RandString(length int) string { + return randStringWithCharset(length, Charset) +} + +func randStringWithCharset(length int, charset string) string { + seededRand := rand.New(rand.NewSource(time.Now().UnixNano())) + b := make([]byte, length) + for i := range b { + b[i] = charset[seededRand.Intn(len(charset))] + } + return string(b) +}