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() }