/* * Copyright (C) 2021-2024 Seraphim R. Pardee * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package routes import ( "bytes" "fmt" "log" "maps" "math/rand/v2" "slices" "github.com/alecthomas/chroma/v2/quick" "github.com/dgraph-io/badger/v4" "github.com/gofiber/fiber/v2" "github.com/mileusna/useragent" "github.com/mingrammer/cfmt" ) func SetupRoutes(app *fiber.App, db *badger.DB) { app.Post("/", func(c *fiber.Ctx) error { potentialId := "" keyUsed := true invalidLength := false for keyUsed { potentialId = generateId() err := db.View(func(txn *badger.Txn) error { _, err := txn.Get([]byte(potentialId)) if err != nil && err != badger.ErrKeyNotFound { return err } else if err == badger.ErrKeyNotFound { keyUsed = false } return nil }) handleErr(err, "encountered an error when searching for existing keys") } err := db.Update(func(txn *badger.Txn) error { if len(c.Body()) != 0 { cfmt.Infof("📝 [info] making entry at %s\n", potentialId) err := txn.Set([]byte(potentialId), c.Body()) return err } else { invalidLength = true return nil } }) handleErr(err, "encountered an error when adding an entry to the db") if invalidLength { c.SendStatus(400) return c.SendString("cannot make an empty entry") } cfmt.Infof("📝 [info] entry submitted at %s\n", potentialId) return c.SendString(fmt.Sprintln(potentialId)) }) app.Get("/:id?", func(c *fiber.Ctx) error { id := c.Params("id") idNotFound := id == "" if !idNotFound { var itemValue []byte err := db.View(func(txn *badger.Txn) error { item, err := txn.Get([]byte(c.Params("id"))) if err != nil && err != badger.ErrKeyNotFound { return err } else if err == badger.ErrKeyNotFound { idNotFound = true return nil } itemValue, err = item.ValueCopy(nil) return err }) handleErr(err, fmt.Sprintf("unable to fetch %s from db", id)) if idNotFound { c.SendStatus(404) return c.SendString("did not receive valid parameter") } ua := useragent.Parse(string(c.Context().UserAgent())) if ua.IsUnknown() { return c.SendString(string(itemValue)) } queries := c.Queries() if len(queries) == 1 { lang := slices.Collect(maps.Keys(queries))[0] var outputHtml bytes.Buffer err = quick.Highlight(&outputHtml, string(itemValue), lang, "html", "gruvbox") handleErr(err, fmt.Sprintf("unable to syntax highlight %s\n", id)) cfmt.Infof("👀 [info] reading %s with '%s' syntax\n", id, lang) return c.Render("render_syntax", fiber.Map{ "Code": outputHtml.String(), }) } else { cfmt.Infof("👀 [info] reading %s\n", id) return c.Render("render", fiber.Map{ "Code": string(itemValue), }) } } else { ua := useragent.Parse(string(c.Context().UserAgent())) if ua.IsUnknown() { return c.SendString(`there was a farmer, had a dog and bingo was his name-o b-i-n-g-o, b-i-n-g-o, b-i-n-g-o and bingo was his name-o https://sr.ht/~seraphimrp/bingo`) } return c.Render("index", nil) } }) app.Delete("/:id", func(c *fiber.Ctx) error { potentialId := c.Params("id") keyNotFound := false err := db.View(func(txn *badger.Txn) error { _, err := txn.Get([]byte(potentialId)) if err != nil && err != badger.ErrKeyNotFound { return err } else if err == badger.ErrKeyNotFound { keyNotFound = true } return nil }) handleErr(err, "encountered an error when searching for existing keys") if keyNotFound { c.SendStatus(404) return c.SendString("did not receive valid parameter") } err = db.Update(func(txn *badger.Txn) error { cfmt.Infof("🗑️ [info] deleting entry at %s\n", potentialId) err := txn.Delete([]byte(potentialId)) return err }) handleErr(err, "encountered an error when deleting an entry in the db") cfmt.Infof("🗑️ [info] entry deleted at %s\n", potentialId) return c.SendString("success") }) } func generateId() string { // TODO: optimize via https://stackoverflow.com/questions/22892120/how-to-generate-a-random-string-of-a-fixed-length-in-go id := "" characters := "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" for i := 0; i < 6; i++ { n := rand.IntN(len(characters)) id = id + string(characters[n]) } return id } func handleErr(err error, msg string) { if err != nil { cfmt.Errorf("😵 [err] %s\n", msg) log.Fatal(err) } }