cortris/main.go

139 lines
3.3 KiB
Go

// Cortris is a simple url shortener
// Copyright (c) 2020, Adalricus Ovicula <adalricus@inventati.org>
//
// 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)
}