kls/handler.go

136 lines
2.9 KiB
Go
Raw Permalink Normal View History

2022-03-20 00:47:01 +00:00
package main
2022-03-20 01:49:41 +00:00
import (
"context"
"crypto/subtle"
"embed"
"fmt"
"html/template"
"log"
"net/http"
"strings"
2022-03-20 00:47:01 +00:00
2023-09-13 05:37:04 +00:00
"git.wntrmute.dev/kyle/goutils/config"
2022-03-20 01:49:41 +00:00
"git.wntrmute.dev/kyle/kls/links"
"github.com/jackc/pgx/v4/pgxpool"
)
2022-03-20 00:47:01 +00:00
2022-03-20 01:49:41 +00:00
type server struct {
db *pgxpool.Pool
2022-03-20 00:47:01 +00:00
}
2022-03-20 01:49:41 +00:00
//go:embed templates/*.tpl
var templateFiles embed.FS
2022-03-20 00:47:01 +00:00
2022-03-20 01:49:41 +00:00
var templates = template.Must(template.ParseFS(templateFiles, "templates/*.tpl"))
type page struct {
Short string
2022-03-20 22:03:39 +00:00
URLs []*links.URL
2022-03-20 01:49:41 +00:00
}
2022-03-20 22:03:39 +00:00
func (srv *server) servePage(w http.ResponseWriter, p page, tpl string) {
err := templates.ExecuteTemplate(w, tpl, p)
2022-03-20 01:49:41 +00:00
if err != nil {
log.Printf("error executing template: %s", err)
http.Error(w, fmt.Sprintf("template execution failed: %s", err.Error()),
http.StatusInternalServerError)
return
}
}
func (srv *server) postURL(w http.ResponseWriter, r *http.Request) {
err := r.ParseForm()
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
2023-09-13 05:37:04 +00:00
isRawURL := boolean(r.FormValue("rawp"))
2022-03-20 01:49:41 +00:00
url := r.FormValue("value")
if len(url) == 0 {
http.Error(w, "invalid URL", http.StatusBadRequest)
return
}
2023-09-13 05:37:04 +00:00
url, err = links.CleanString(url, isRawURL)
2022-03-20 22:03:39 +00:00
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
2022-03-20 01:49:41 +00:00
ctx := context.Background()
short, err := links.StoreURL(ctx, srv.db, url)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
2022-03-20 22:03:39 +00:00
srv.servePage(w, page{Short: short}, "index.tpl")
2022-03-20 01:49:41 +00:00
}
func (srv *server) redirect(w http.ResponseWriter, r *http.Request) {
short := strings.TrimPrefix(r.URL.Path, "/")
u, err := links.RetrieveURL(context.Background(), srv.db, short)
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
http.Redirect(w, r, u, http.StatusFound)
}
2022-03-20 22:03:39 +00:00
func (srv *server) listAll(w http.ResponseWriter, r *http.Request) {
allPosts, err := links.FetchAll(context.Background(), srv.db)
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
p := page{
URLs: allPosts,
}
srv.servePage(w, p, "list.tpl")
}
2022-03-20 01:49:41 +00:00
func (srv *server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if r.Method == http.MethodGet && links.ValidShortCode.MatchString(r.URL.Path) {
srv.redirect(w, r)
return
}
user, pass, ok := r.BasicAuth()
username := config.Get("HTTP_USER")
password := config.Get("HTTP_PASS")
if !ok || subtle.ConstantTimeCompare([]byte(user), []byte(username)) != 1 || subtle.ConstantTimeCompare([]byte(pass), []byte(password)) != 1 {
2023-09-13 05:37:37 +00:00
w.Header().Set("WWW-Authenticate", `Basic realm="kls"`)
2022-03-20 01:49:41 +00:00
w.WriteHeader(401)
w.Write([]byte("Unauthorised.\n"))
return
}
if r.Method == http.MethodPost {
srv.postURL(w, r)
return
}
2022-03-20 22:03:39 +00:00
if r.URL.Path == "/list" {
srv.listAll(w, r)
return
}
srv.servePage(w, page{}, "index.tpl")
2022-03-20 00:47:01 +00:00
}
2023-09-13 05:37:04 +00:00
func boolean(s string) bool {
switch s {
case "on", "true", "True", "yes", "Yes":
return true
default:
return false
}
}