git.sophuwu.com > urlshort   
              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()

}