main.go 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200
  1. package main
  2. import (
  3. "bytes"
  4. "encoding/base64"
  5. "encoding/binary"
  6. "encoding/json"
  7. "flag"
  8. "fmt"
  9. "log"
  10. "math/rand"
  11. "net/http"
  12. "strings"
  13. "time"
  14. "github.com/syndtr/goleveldb/leveldb"
  15. "github.com/kellegous/go/context"
  16. )
  17. func MakeName() string {
  18. var buf bytes.Buffer
  19. binary.Write(&buf, binary.LittleEndian, rand.Int63())
  20. return base64.URLEncoding.EncodeToString(buf.Bytes())
  21. }
  22. func ParseName(base, path string) string {
  23. t := path[len(base):]
  24. ix := strings.Index(t, "/")
  25. if ix == -1 {
  26. return t
  27. } else {
  28. return t[:ix]
  29. }
  30. }
  31. func WriteJson(w http.ResponseWriter, data interface{}, status int) {
  32. w.Header().Set("Content-Type", "application/json;charset=utf-8")
  33. if err := json.NewEncoder(w).Encode(data); err != nil {
  34. log.Panic(err)
  35. }
  36. }
  37. func WriteJsonError(w http.ResponseWriter, error string, status int) {
  38. WriteJson(w, map[string]interface{}{
  39. "error": error,
  40. }, status)
  41. }
  42. func WriteJsonRoute(w http.ResponseWriter, name string, rt *context.Route) {
  43. res := struct {
  44. Name string `json:"name"`
  45. URL string `json:"url"`
  46. Time time.Time `json:"time"`
  47. }{
  48. name,
  49. rt.URL,
  50. rt.Time,
  51. }
  52. WriteJson(w, &res, http.StatusOK)
  53. }
  54. func ServeAsset(w http.ResponseWriter, r *http.Request, name string) {
  55. n, err := AssetInfo(name)
  56. if err != nil {
  57. http.NotFound(w, r)
  58. return
  59. }
  60. a, err := Asset(name)
  61. if err != nil {
  62. http.NotFound(w, r)
  63. return
  64. }
  65. http.ServeContent(w, r, n.Name(), n.ModTime(), bytes.NewReader(a))
  66. }
  67. type DefaultHandler struct {
  68. ctx *context.Context
  69. }
  70. func (h *DefaultHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
  71. p := ParseName("/", r.URL.Path)
  72. if p == "" {
  73. http.Redirect(w, r,
  74. fmt.Sprintf("/edit/%s", MakeName()),
  75. http.StatusTemporaryRedirect)
  76. return
  77. }
  78. rt, err := h.ctx.Get(p)
  79. if err == leveldb.ErrNotFound {
  80. http.Redirect(w, r,
  81. fmt.Sprintf("/edit/%s", p),
  82. http.StatusTemporaryRedirect)
  83. return
  84. } else if err != nil {
  85. log.Panic(err)
  86. }
  87. http.Redirect(w, r,
  88. rt.URL,
  89. http.StatusTemporaryRedirect)
  90. }
  91. type EditHandler struct {
  92. ctx *context.Context
  93. }
  94. func (h *EditHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
  95. p := ParseName("/edit/", r.URL.Path)
  96. if p == "" {
  97. http.Redirect(w, r,
  98. fmt.Sprintf("/edit/%s", MakeName()),
  99. http.StatusTemporaryRedirect)
  100. return
  101. }
  102. ServeAsset(w, r, "index.html")
  103. }
  104. type ApiHandler struct {
  105. ctx *context.Context
  106. }
  107. func (h *ApiHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
  108. p := ParseName("/api/url/", r.URL.Path)
  109. if p == "" {
  110. WriteJsonError(w,
  111. http.StatusText(http.StatusNotFound),
  112. http.StatusNotFound)
  113. return
  114. }
  115. if r.Method == "POST" {
  116. var req struct {
  117. URL string `json:"url"`
  118. }
  119. if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
  120. WriteJsonError(w, "invalid json", http.StatusBadRequest)
  121. return
  122. }
  123. if req.URL == "" {
  124. WriteJsonError(w, "url required", http.StatusBadRequest)
  125. return
  126. }
  127. rt := context.Route{
  128. URL: req.URL,
  129. Time: time.Now(),
  130. }
  131. if err := h.ctx.Put(p, &rt); err != nil {
  132. log.Panic(err)
  133. }
  134. WriteJsonRoute(w, p, &rt)
  135. } else if r.Method == "GET" {
  136. rt, err := h.ctx.Get(p)
  137. if err == leveldb.ErrNotFound {
  138. WriteJsonError(w, "no such route", http.StatusNotFound)
  139. return
  140. } else if err != nil {
  141. log.Panic(err)
  142. }
  143. WriteJsonRoute(w, p, rt)
  144. } else {
  145. WriteJsonError(w,
  146. http.StatusText(http.StatusMethodNotAllowed),
  147. http.StatusMethodNotAllowed)
  148. }
  149. }
  150. func main() {
  151. flagData := flag.String("data", "data", "data")
  152. flagAddr := flag.String("addr", ":8067", "addr")
  153. flag.Parse()
  154. ctx, err := context.Open(*flagData)
  155. if err != nil {
  156. log.Panic(err)
  157. }
  158. mux := http.NewServeMux()
  159. mux.Handle("/", &DefaultHandler{ctx})
  160. mux.Handle("/edit/", &EditHandler{ctx})
  161. mux.Handle("/api/url/", &ApiHandler{ctx})
  162. mux.HandleFunc("/s/", func(w http.ResponseWriter, r *http.Request) {
  163. ServeAsset(w, r, r.URL.Path[len("/s/"):])
  164. })
  165. log.Panic(http.ListenAndServe(*flagAddr, mux))
  166. }