web.go 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116
  1. package web
  2. import (
  3. "context"
  4. "errors"
  5. "fmt"
  6. "log"
  7. "net/http"
  8. "time"
  9. "github.com/spf13/viper"
  10. "github.com/kellegous/go/internal"
  11. "github.com/kellegous/go/internal/backend"
  12. )
  13. // The default handler responds to most requests. It is responsible for the
  14. // shortcut redirects and for sending unmapped shortcuts to the edit page.
  15. func getDefault(
  16. backend backend.Backend,
  17. assets http.Handler,
  18. w http.ResponseWriter,
  19. r *http.Request,
  20. ) {
  21. p := parseName("/", r.URL.Path)
  22. if p == "" {
  23. r.URL.Path = "/s/"
  24. assets.ServeHTTP(w, r)
  25. // http.Redirect(w, r, "/edit/", http.StatusTemporaryRedirect)
  26. return
  27. }
  28. ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
  29. defer cancel()
  30. rt, err := backend.Get(ctx, p)
  31. if errors.Is(err, internal.ErrRouteNotFound) {
  32. http.Redirect(w, r,
  33. fmt.Sprintf("/edit/%s", cleanName(p)),
  34. http.StatusTemporaryRedirect)
  35. return
  36. } else if err != nil {
  37. log.Panic(err)
  38. }
  39. http.Redirect(w, r,
  40. rt.URL,
  41. http.StatusTemporaryRedirect)
  42. }
  43. // ListenAndServe sets up all web routes, binds the port and handles incoming
  44. // web requests.
  45. func ListenAndServe(
  46. backend backend.Backend,
  47. assets http.Handler,
  48. ) error {
  49. addr := viper.GetString("addr")
  50. admin := viper.GetBool("admin")
  51. version := viper.GetString("version")
  52. host := viper.GetString("host")
  53. mux := http.NewServeMux()
  54. mux.HandleFunc("/api/url/", func(w http.ResponseWriter, r *http.Request) {
  55. apiURL(backend, host, w, r)
  56. })
  57. mux.HandleFunc("/api/urls/", func(w http.ResponseWriter, r *http.Request) {
  58. apiURLs(backend, host, w, r)
  59. })
  60. mux.HandleFunc("/api/config", func(w http.ResponseWriter, r *http.Request) {
  61. writeJSON(w, struct {
  62. Host string `json:"host"`
  63. }{host}, http.StatusOK)
  64. })
  65. mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
  66. getDefault(backend, assets, w, r)
  67. })
  68. mux.HandleFunc("/edit/", func(w http.ResponseWriter, r *http.Request) {
  69. p := parseName("/edit/", r.URL.Path)
  70. // if this is a banned name, just redirect to the local URI. That'll show em.
  71. if isBannedName(p) {
  72. http.Redirect(w, r, fmt.Sprintf("/%s", p), http.StatusTemporaryRedirect)
  73. return
  74. }
  75. r.URL.Path = "/s/edit/"
  76. assets.ServeHTTP(w, r)
  77. })
  78. mux.HandleFunc("/links/", func(w http.ResponseWriter, r *http.Request) {
  79. http.Redirect(w, r, "/", http.StatusPermanentRedirect)
  80. })
  81. mux.Handle("/s/", assets)
  82. mux.HandleFunc("/version", func(w http.ResponseWriter, r *http.Request) {
  83. fmt.Fprintln(w, version)
  84. })
  85. mux.HandleFunc("/healthz", func(w http.ResponseWriter, r *http.Request) {
  86. fmt.Fprintln(w, "👍")
  87. })
  88. // TODO(knorton): Remove the admin handler.
  89. if admin {
  90. mux.Handle("/admin/", &adminHandler{backend})
  91. }
  92. return http.ListenAndServe(addr, mux)
  93. }