commit 919883326b24d230c8b2c44105cabfa14ed3a205 Author: Jim Myhrberg Date: Sat Jun 25 22:38:57 2016 +0100 Initial commit diff --git a/main.go b/main.go new file mode 100644 index 0000000..bdc6049 --- /dev/null +++ b/main.go @@ -0,0 +1,29 @@ +package main + +import ( + "os" + + "github.com/iris-contrib/middleware/logger" + "github.com/kataras/iris" +) + +func main() { + shortner := NewShortner() + defer shortner.Close() + + routes := Routes{Shortner: shortner} + + iris.Use(logger.New(iris.Logger)) + iris.Get("/set/:key/:value", routes.Set) + iris.Get("/get/:key", routes.Get) + iris.Get("/shorten/:url", routes.Shorten) + iris.Get("/lookup/:uid", routes.Lookup) + iris.Get("/", routes.Root) + + port := os.Getenv("PORT") + if port == "" { + port = "8080" + } + + iris.Listen(":" + port) +} diff --git a/routes.go b/routes.go new file mode 100644 index 0000000..dcc2034 --- /dev/null +++ b/routes.go @@ -0,0 +1,54 @@ +package main + +import "github.com/kataras/iris" + +// Routes handles HTTP requests from iris. +type Routes struct { + Shortner *Shortner +} + +// Root returns the root. +func (r *Routes) Root(c *iris.Context) { + c.Write("hello world") +} + +// Get fetches a value for a given key. +func (r *Routes) Get(c *iris.Context) { + key := c.Param("key") + value := r.Shortner.Get([]byte(key)) + c.Write(string(value)) +} + +// Set writes a given value to the specified key. +func (r *Routes) Set(c *iris.Context) { + key := []byte(c.Param("key")) + value := []byte(c.Param("value")) + err := r.Shortner.Set(key, value) + + if err == nil { + c.Write("OK") + } else { + c.Log("ERROR: ", err.Error(), "\n") + c.Write("Oops >_<") + } +} + +// Shorten attempts to shorten a URL to a UID +func (r *Routes) Shorten(c *iris.Context) { + url := []byte(c.Param("url")) + uid, err := r.Shortner.Shorten(url) + + if err == nil { + c.Write("Shortened: %s", uid) + } else { + c.Log("ERROR: ", err.Error(), "\n") + c.Write("Oops >_<") + } +} + +// Lookup attempts to fetch previously shortened URLs based on UID +func (r *Routes) Lookup(c *iris.Context) { + uid := []byte(c.Param("uid")) + url := r.Shortner.Lookup(uid) + c.Write("URL: %s", url) +} diff --git a/shortner.go b/shortner.go new file mode 100644 index 0000000..c4f7a11 --- /dev/null +++ b/shortner.go @@ -0,0 +1,100 @@ +package main + +import ( + "log" + "strconv" + + "github.com/jbenet/go-base58" + "github.com/syndtr/goleveldb/leveldb" +) + +// NewShortner returns a new Shortner. +func NewShortner() *Shortner { + db, err := leveldb.OpenFile("shortner.db", nil) + if err != nil { + log.Fatal(err) + } + + return &Shortner{ + Db: db, + KeyPrefix: []byte("url:"), + } +} + +// Shortner shortens URLs. +type Shortner struct { + Db *leveldb.DB + KeyPrefix []byte +} + +// Close calls Close() on the goleveldb. +func (s *Shortner) Close() { + err := s.Db.Close() + if err != nil { + log.Fatal(err) + } +} + +// Get reads from the database. +func (s *Shortner) Get(key []byte) []byte { + value, _ := s.Db.Get(key, nil) + return value +} + +// Set writes to the database. +func (s *Shortner) Set(key []byte, value []byte) error { + return s.Db.Put(key, value, nil) +} + +// Shorten shortens given URL +func (s *Shortner) Shorten(url []byte) ([]byte, error) { + uid, err := s.newUID() + if err != nil { + return []byte{}, err + } + + key := s.makeKey(uid) + err = s.Set(key, url) + if err != nil { + return []byte{}, err + } + + return uid, nil +} + +// Lookup attempts to fetch the value for given UID +func (s *Shortner) Lookup(uid []byte) []byte { + return s.Get(s.makeKey(uid)) +} + +func (s *Shortner) makeKey(uid []byte) []byte { + return append(s.KeyPrefix, uid...) +} + +func (s *Shortner) newUID() ([]byte, error) { + key := []byte("index") + + tx, err := s.Db.OpenTransaction() + defer tx.Commit() + + if err != nil { + return []byte{}, err + } + + index, _ := tx.Get(key, nil) + if index == nil { + index = []byte("1") + } + + num, _ := strconv.Atoi(string(index)) + num++ + index = []byte(strconv.Itoa(num)) + + err = tx.Put(key, index, nil) + if err != nil { + return []byte{}, err + } + + uid := base58.Encode(index) + return []byte(uid), nil +}