// Cortris is a simple url shortener // Copyright (c) 2020, Adalricus Ovicula // // Permission to use, copy, modify, and/or distribute this software for any // purpose with or without fee is hereby granted, provided that the above // copyright notice and this permission notice appear in all copies. // // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR // ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. package main import ( "database/sql" "html/template" "log" "math/rand" "net/http" "time" "github.com/gorilla/mux" _ "github.com/mattn/go-sqlite3" ) func main() { rand.Seed(time.Now().UnixNano()) r := mux.NewRouter() r.HandleFunc("/", indexH).Methods("GET") r.HandleFunc("/short/", sURLH).Methods("POST") r.HandleFunc("/{sURL}/", urlH).Methods("GET") r.PathPrefix("/static/"). Handler(http.FileServer(http.Dir("./"))) log.Println("listening on port 8080...") srv := &http.Server{ Handler: r, Addr: "127.0.0.1:8080", WriteTimeout: 8 * time.Second, ReadTimeout: 8 * time.Second, } log.Fatal(srv.ListenAndServe()) } const dbL = "/home/adalricus/projects/db/adalricus.db" const alphaNum = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" type shortP struct { ShortURL string URL string } func indexH(w http.ResponseWriter, r *http.Request) { t, _ := template.ParseFiles("templates/index.html") t.Execute(w, nil) } // This is the POST endpoint func sURLH(w http.ResponseWriter, r *http.Request) { if err := r.ParseForm(); err != nil { errChk(err) } url := r.FormValue("url") db, err := sql.Open("sqlite3", dbL) errChk(err) defer db.Close() // Checks if url is already in the database stmt, err := db.Prepare("SELECT shortURL FROM cortris WHERE url = ?") errChk(err) defer stmt.Close() var shortURL string _ = stmt.QueryRow(url).Scan(&shortURL) // If short url doesn't exist then create it if len(shortURL) == 0 { shortURL = randS(8) tx, err := db.Begin() errChk(err) stmt, err := tx.Prepare("INSERT INTO cortris(url, shortURL, time) VALUES(?, ?, ?)") errChk(err) defer stmt.Close() _, err = stmt.Exec(url, shortURL, time.Now().UTC()) errChk(err) tx.Commit() } p := shortP{ ShortURL: shortURL, URL: url} t, _ := template.ParseFiles("templates/short.html") t.Execute(w, p) } // This handles short urls func urlH(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) shortURL := vars["sURL"] db, err := sql.Open("sqlite3", dbL) errChk(err) defer db.Close() stmt, err := db.Prepare("SELECT url FROM cortris WHERE shortURL = ?") errChk(err) var url string _ = stmt.QueryRow(shortURL).Scan(&url) if len(url) != 0 { http.Redirect(w, r, url, http.StatusSeeOther) } } func errChk(err error) { if err != nil { log.Fatal(err) } } func randS(n int) string { b := make([]byte, n) for i := range b { b[i] = alphaNum[rand.Intn(len(alphaNum))] } return string(b) }