web.go 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126
  1. package web
  2. import (
  3. "bytes"
  4. "fmt"
  5. "html/template"
  6. "log"
  7. "net/http"
  8. "github.com/kellegous/go/context"
  9. "github.com/syndtr/goleveldb/leveldb"
  10. )
  11. // Serve a bundled asset over HTTP.
  12. func serveAsset(w http.ResponseWriter, r *http.Request, name string) {
  13. n, err := AssetInfo(name)
  14. if err != nil {
  15. http.NotFound(w, r)
  16. return
  17. }
  18. a, err := Asset(name)
  19. if err != nil {
  20. http.NotFound(w, r)
  21. return
  22. }
  23. http.ServeContent(w, r, n.Name(), n.ModTime(), bytes.NewReader(a))
  24. }
  25. func templateFromAssetFn(fn func() (*asset, error)) (*template.Template, error) {
  26. a, err := fn()
  27. if err != nil {
  28. return nil, err
  29. }
  30. t := template.New(a.info.Name())
  31. return t.Parse(string(a.bytes))
  32. }
  33. // The default handler responds to most requests. It is responsible for the
  34. // shortcut redirects and for sending unmapped shortcuts to the edit page.
  35. func getDefault(ctx *context.Context, w http.ResponseWriter, r *http.Request) {
  36. p := parseName("/", r.URL.Path)
  37. if p == "" {
  38. http.Redirect(w, r, "/edit/", http.StatusTemporaryRedirect)
  39. return
  40. }
  41. rt, err := ctx.Get(p)
  42. if err == leveldb.ErrNotFound {
  43. http.Redirect(w, r,
  44. fmt.Sprintf("/edit/%s", cleanName(p)),
  45. http.StatusTemporaryRedirect)
  46. return
  47. } else if err != nil {
  48. log.Panic(err)
  49. }
  50. http.Redirect(w, r,
  51. rt.URL,
  52. http.StatusTemporaryRedirect)
  53. }
  54. func getLinks(ctx *context.Context, w http.ResponseWriter, r *http.Request) {
  55. t, err := templateFromAssetFn(linksHtml)
  56. if err != nil {
  57. log.Panic(err)
  58. }
  59. rts, err := ctx.GetAll()
  60. if err != nil {
  61. log.Panic(err)
  62. }
  63. if err := t.Execute(w, rts); err != nil {
  64. log.Panic(err)
  65. }
  66. }
  67. // ListenAndServe sets up all web routes, binds the port and handles incoming
  68. // web requests.
  69. func ListenAndServe(addr string, admin bool, version string, ctx *context.Context) error {
  70. mux := http.NewServeMux()
  71. mux.HandleFunc("/api/url/", func(w http.ResponseWriter, r *http.Request) {
  72. apiURL(ctx, w, r)
  73. })
  74. mux.HandleFunc("/api/urls/", func(w http.ResponseWriter, r *http.Request) {
  75. apiURLs(ctx, w, r)
  76. })
  77. mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
  78. getDefault(ctx, w, r)
  79. })
  80. mux.HandleFunc("/edit/", func(w http.ResponseWriter, r *http.Request) {
  81. p := parseName("/edit/", r.URL.Path)
  82. // if this is a banned name, just redirect to the local URI. That'll show em.
  83. if isBannedName(p) {
  84. http.Redirect(w, r, fmt.Sprintf("/%s", p), http.StatusTemporaryRedirect)
  85. return
  86. }
  87. serveAsset(w, r, "edit.html")
  88. })
  89. mux.HandleFunc("/links/", func(w http.ResponseWriter, r *http.Request) {
  90. getLinks(ctx, w, r)
  91. })
  92. mux.HandleFunc("/s/", func(w http.ResponseWriter, r *http.Request) {
  93. serveAsset(w, r, r.URL.Path[len("/s/"):])
  94. })
  95. mux.HandleFunc("/version", func(w http.ResponseWriter, r *http.Request) {
  96. fmt.Fprintln(w, version)
  97. })
  98. mux.HandleFunc("/healthz", func(w http.ResponseWriter, r *http.Request) {
  99. fmt.Fprintln(w, "👍")
  100. })
  101. // TODO(knorton): Remove the admin handler.
  102. if admin {
  103. mux.Handle("/admin/", &adminHandler{ctx})
  104. }
  105. return http.ListenAndServe(addr, mux)
  106. }