You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
139 lines
3.3 KiB
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)
|
|
}
|