179
package main
import (
"encoding/json"
"errors"
bolt "go.etcd.io/bbolt"
"math/rand"
"net/http"
"os"
"strings"
"time"
)
var urlDB *bolt.DB
var wordList []string
type urlStruc struct {
isCustom bool
creator string
date time.Time
path string
url string
uses int
}
func fec(err error) { // fatal error check
if err != nil {
println(err.Error())
urlDB.Close()
os.Exit(1)
}
}
func openFiles() {
b, err := os.ReadFile("wordlist")
fec(err)
if len(b) == 0 {
println("wordlist is empty")
os.Exit(1)
}
wordList = strings.Split(string(b), "\n")
var db *bolt.DB
db, err = bolt.Open("urls.db", 0600, nil)
fec(err)
fec(db.Update(func(tx *bolt.Tx) error {
_, err = tx.CreateBucketIfNotExists([]byte("urls"))
return err
}))
urlDB = db
}
func getWords() string {
word := func() string {
return wordList[rand.Intn(len(wordList))]
}
return word() + "-" + word() + "-" + word()
}
func readURL(words string) (urlStruc, error) {
if words == "" {
return urlStruc{}, errors.New("No words provided")
}
var url urlStruc
var err error
err = urlDB.View(func(tx *bolt.Tx) error {
b := tx.Bucket([]byte("urls"))
return json.Unmarshal(b.Get([]byte(words)), &url)
})
return url, err
}
func checkURL(words string) bool {
url, err := readURL(words)
if err == nil && url.url != "" {
return true
}
return false
}
func writeURL(url urlStruc) error {
jsonUrl, err := json.Marshal(url)
if err != nil {
return err
}
return urlDB.Update(func(tx *bolt.Tx) error {
b := tx.Bucket([]byte("urls"))
return b.Put([]byte(url.path), jsonUrl)
})
}
func http404(w http.ResponseWriter) {
w.WriteHeader(404)
b, err := os.ReadFile("html/404.html")
if err != nil {
b = []byte("404 not found")
w.Header().Set("Content-Type", "text/plain")
} else {
w.Header().Set("Content-Type", "text/html")
}
w.Write(b)
}
func http500(w http.ResponseWriter) {
w.WriteHeader(500)
b, err := os.ReadFile("html/500.html")
if err != nil {
b = []byte("500 internal server error")
w.Header().Set("Content-Type", "text/plain")
} else {
w.Header().Set("Content-Type", "text/html")
}
w.Write(b)
}
func intStr(n int) string {
if n == 0 {
return "0"
}
var s, sign string
if n < 0 {
sign = "-"
n = -n
}
for ; n > 0; n /= 10 {
s = string(n%10+int('0')) + s
}
return sign + s
}
func infoPage(w http.ResponseWriter, url urlStruc) {
w.WriteHeader(200)
w.Header().Set("Content-Type", "text/html")
b, err := os.ReadFile("html/info.html")
if err != nil {
http500(w)
return
}
custom := "no"
if url.isCustom {
custom = "yes"
}
s := strings.ReplaceAll(string(b), "{{url}}", url.url)
s = strings.ReplaceAll(s, "{{path}}", url.path)
s = strings.ReplaceAll(s, "{{uses}}", intStr(url.uses))
s = strings.ReplaceAll(s, "{{custom}}", custom)
s = strings.ReplaceAll(s, "{{date}}", url.date.Format("2006-01-02 15:04:05"))
w.Write([]byte(s))
}
func httpHandler(w http.ResponseWriter, r *http.Request) {
if len(r.URL.Path) > 1 {
path := strings.TrimSuffix(r.URL.Path[1:], "/")
var info bool = false
if strings.HasSuffix(path, "/info") {
info = true
path = strings.TrimSuffix(path, "/info")
}
url, err := readURL(path)
if err != nil || url.url == "" {
http404(w)
return
}
if info {
infoPage(w, url)
return
}
http.Redirect(w, r, url.url, http.StatusFound)
return
}
if r.Method == "POST" {
}
}
func main() {
openFiles()
}