diff --git a/README.md b/README.md index 379670d..51c0c92 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ Configuration can be done with a simple `config.{json|yaml|toml}` file in the wo These are the required config values: -* `password`: Password to create or delete short links +* `password`: Password to create, update or delete short links * `shortUrl`: The short base URL (without trailing slash!) * `defaultUrl`: The default URL to which should be redirected when no slug is specified @@ -22,17 +22,18 @@ See the `example-config.yaml` file for an example configuration. The preferred authentication method is Basic Authentication. If you try to create, modify or delete a short link, in the browser a popup will appear asking for username and password. Enter just the password you configured. Alternatively you can append a URL query parameter `password` with your configured password. -## Create a new short link +## Usage -To create a new short link, call "`shortUrl` + `/s?url=` + URL to shorten". If you want, you can append `&slug=` with the preferred slug. +You can either create, update or delete short links using a browser by entering the short URL and the path for the method. Or you can make a HTTP `POST` request with the parameters. -## Update a short link - -To update a short link, call "`shortUrl` + `/d?slug=` + slug to update + `&new=` + new long URL" - -## Delete a short link - -To delete a short link, call "`shortUrl` + `/d?slug=` + slug to delete". +- Create a new short link: `/s` + - `url`: URL to shorten + - (optional) `slug`: the preferred slug +- Update a short link: `/u` + - `slug`: slug to update + - `new`: new long URL +- Delete a short link: `/d` + - `slug`: slug to delete ## License diff --git a/main.go b/main.go index 86bf4a2..dcbd257 100644 --- a/main.go +++ b/main.go @@ -6,6 +6,7 @@ import ( _ "github.com/mattn/go-sqlite3" "github.com/rubenv/sql-migrate" "github.com/spf13/viper" + "html/template" "log" "math/rand" "net/http" @@ -50,9 +51,12 @@ func main() { }() r := mux.NewRouter() - r.HandleFunc("/s", ShortenHandler) - r.HandleFunc("/u", UpdateHandler) - r.HandleFunc("/d", DeleteHandler) + r.HandleFunc("/s", ShortenFormHandler).Methods(http.MethodGet) + r.HandleFunc("/s", ShortenHandler).Methods(http.MethodPost) + r.HandleFunc("/u", UpdateFormHandler).Methods(http.MethodGet) + r.HandleFunc("/u", UpdateHandler).Methods(http.MethodPost) + r.HandleFunc("/d", DeleteFormHandler).Methods(http.MethodGet) + r.HandleFunc("/d", DeleteHandler).Methods(http.MethodPost) r.HandleFunc("/{slug}", ShortenedUrlHandler) r.HandleFunc("/", CatchAllHandler) @@ -76,7 +80,68 @@ func MigrateDatabase() { } } +func ShortenFormHandler(w http.ResponseWriter, r *http.Request) { + _ = r.ParseForm() + + if !checkPassword(w, r) { + return + } + + err := generateForm(w, "Shorten URL", "s", []string{"url", "slug"}) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + } +} + +func UpdateFormHandler(w http.ResponseWriter, r *http.Request) { + _ = r.ParseForm() + + if !checkPassword(w, r) { + return + } + + err := generateForm(w, "Update short link", "u", []string{"slug", "new"}) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + } +} + +func DeleteFormHandler(w http.ResponseWriter, r *http.Request) { + _ = r.ParseForm() + + if !checkPassword(w, r) { + return + } + + err := generateForm(w, "Delete short link", "d", []string{"slug"}) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + } +} + +func generateForm(w http.ResponseWriter, title string, url string, fields []string) error { + tmpl, err := template.New("Form").Parse("{{.Title}}

{{.Title}}

{{range .Fields}}

{{end}}
") + if err != nil { + return err + } + err = tmpl.Execute(w, &struct { + Title string + Url string + Fields []string + }{ + Title: title, + Url: url, + Fields: fields, + }) + if err != nil { + return err + } + return nil +} + func ShortenHandler(w http.ResponseWriter, r *http.Request) { + _ = r.ParseForm() + if !checkPassword(w, r) { return } @@ -85,13 +150,13 @@ func ShortenHandler(w http.ResponseWriter, r *http.Request) { _, _ = w.Write([]byte(viper.GetString("shortUrl") + "/" + slug)) } - requestUrl := r.URL.Query().Get("url") + requestUrl := r.FormValue("url") if requestUrl == "" { http.Error(w, "url parameter not set", http.StatusBadRequest) return } - slug := r.URL.Query().Get("slug") + slug := r.FormValue("slug") manualSlug := false if slug == "" { _ = db.QueryRow("SELECT slug FROM redirect WHERE url = ?", requestUrl).Scan(&slug) @@ -132,17 +197,19 @@ func ShortenHandler(w http.ResponseWriter, r *http.Request) { } func UpdateHandler(w http.ResponseWriter, r *http.Request) { + _ = r.ParseForm() + if !checkPassword(w, r) { return } - slug := r.URL.Query().Get("slug") + slug := r.FormValue("slug") if slug == "" { http.Error(w, "Specify the slug to update", http.StatusBadRequest) return } - newUrl := r.URL.Query().Get("new") + newUrl := r.FormValue("new") if newUrl == "" { http.Error(w, "Specify the new URL", http.StatusBadRequest) return @@ -164,11 +231,13 @@ func UpdateHandler(w http.ResponseWriter, r *http.Request) { } func DeleteHandler(w http.ResponseWriter, r *http.Request) { + _ = r.ParseForm() + if !checkPassword(w, r) { return } - slug := r.URL.Query().Get("slug") + slug := r.FormValue("slug") if slug == "" { http.Error(w, "Specify the slug to delete", http.StatusBadRequest) return @@ -190,7 +259,7 @@ func DeleteHandler(w http.ResponseWriter, r *http.Request) { } func checkPassword(w http.ResponseWriter, r *http.Request) bool { - if r.URL.Query().Get("password") == viper.GetString("password") { + if r.FormValue("password") == viper.GetString("password") { return true } _, pass, ok := r.BasicAuth()