summaryrefslogtreecommitdiff
path: root/srv/src/api/api.go
diff options
context:
space:
mode:
authorBrian Picciano <mediocregopher@gmail.com>2022-05-20 11:17:31 -0600
committerBrian Picciano <mediocregopher@gmail.com>2022-05-20 11:17:31 -0600
commit09acb111a2b22f5794541fac175b024dd0f9100e (patch)
tree11d4578a42ad4aea968b42a2689f64c799f9176e /srv/src/api/api.go
parentf69ed83de73bbfc4b7af0931de6ced8cf12dea61 (diff)
Rename api package to http
Diffstat (limited to 'srv/src/api/api.go')
-rw-r--r--srv/src/api/api.go249
1 files changed, 0 insertions, 249 deletions
diff --git a/srv/src/api/api.go b/srv/src/api/api.go
deleted file mode 100644
index 191a7bf..0000000
--- a/srv/src/api/api.go
+++ /dev/null
@@ -1,249 +0,0 @@
-// Package api implements the HTTP-based api for the mediocre-blog.
-package api
-
-import (
- "context"
- "errors"
- "fmt"
- "html/template"
- "net"
- "net/http"
- "net/http/httputil"
- "net/url"
- "os"
-
- "github.com/mediocregopher/blog.mediocregopher.com/srv/api/apiutil"
- "github.com/mediocregopher/blog.mediocregopher.com/srv/cfg"
- "github.com/mediocregopher/blog.mediocregopher.com/srv/chat"
- "github.com/mediocregopher/blog.mediocregopher.com/srv/mailinglist"
- "github.com/mediocregopher/blog.mediocregopher.com/srv/post"
- "github.com/mediocregopher/blog.mediocregopher.com/srv/pow"
- "github.com/mediocregopher/mediocre-go-lib/v2/mctx"
- "github.com/mediocregopher/mediocre-go-lib/v2/mlog"
-)
-
-// Params are used to instantiate a new API instance. All fields are required
-// unless otherwise noted.
-type Params struct {
- Logger *mlog.Logger
- PowManager pow.Manager
-
- // PathPrefix, if given, will be prefixed to all url paths which are
- // rendered by the API's templating system.
- PathPrefix string
-
- PostStore post.Store
- PostAssetStore post.AssetStore
-
- MailingList mailinglist.MailingList
-
- GlobalRoom chat.Room
- UserIDCalculator *chat.UserIDCalculator
-
- // ListenProto and ListenAddr are passed into net.Listen to create the
- // API's listener. Both "tcp" and "unix" protocols are explicitly
- // supported.
- ListenProto, ListenAddr string
-
- // StaticDir and StaticProxy are mutually exclusive.
- //
- // If StaticDir is set then that directory on the filesystem will be used to
- // serve the static site.
- //
- // Otherwise if StaticProxy is set all requests for the static site will be
- // reverse-proxied there.
- StaticDir string
- StaticProxy *url.URL
-
- // AuthUsers keys are usernames which are allowed to edit server-side data,
- // and the values are the password hash which accompanies those users. The
- // password hash must have been produced by NewPasswordHash.
- AuthUsers map[string]string
-}
-
-// SetupCfg implement the cfg.Cfger interface.
-func (p *Params) SetupCfg(cfg *cfg.Cfg) {
-
- cfg.StringVar(&p.ListenProto, "listen-proto", "tcp", "Protocol to listen for HTTP requests with")
- cfg.StringVar(&p.ListenAddr, "listen-addr", ":4000", "Address/path to listen for HTTP requests on")
-
- cfg.StringVar(&p.StaticDir, "static-dir", "", "Directory from which static files are served (mutually exclusive with -static-proxy-url)")
- staticProxyURLStr := cfg.String("static-proxy-url", "", "HTTP address from which static files are served (mutually exclusive with -static-dir)")
-
- cfg.OnInit(func(ctx context.Context) error {
- if *staticProxyURLStr != "" {
- var err error
- if p.StaticProxy, err = url.Parse(*staticProxyURLStr); err != nil {
- return fmt.Errorf("parsing -static-proxy-url: %w", err)
- }
-
- } else if p.StaticDir == "" {
- return errors.New("-static-dir or -static-proxy-url is required")
- }
-
- return nil
- })
-}
-
-// Annotate implements mctx.Annotator interface.
-func (p *Params) Annotate(a mctx.Annotations) {
- a["listenProto"] = p.ListenProto
- a["listenAddr"] = p.ListenAddr
-
- if p.StaticProxy != nil {
- a["staticProxy"] = p.StaticProxy.String()
- return
- }
-
- a["staticDir"] = p.StaticDir
-}
-
-// API will listen on the port configured for it, and serve HTTP requests for
-// the mediocre-blog.
-type API interface {
- Shutdown(ctx context.Context) error
-}
-
-type api struct {
- params Params
- srv *http.Server
-
- redirectTpl *template.Template
-}
-
-// New initializes and returns a new API instance, including setting up all
-// listening ports.
-func New(params Params) (API, error) {
-
- l, err := net.Listen(params.ListenProto, params.ListenAddr)
- if err != nil {
- return nil, fmt.Errorf("creating listen socket: %w", err)
- }
-
- if params.ListenProto == "unix" {
- if err := os.Chmod(params.ListenAddr, 0777); err != nil {
- return nil, fmt.Errorf("chmod-ing unix socket: %w", err)
- }
- }
-
- a := &api{
- params: params,
- }
-
- a.redirectTpl = a.mustParseTpl("redirect.html")
-
- a.srv = &http.Server{Handler: a.handler()}
-
- go func() {
-
- err := a.srv.Serve(l)
- if err != nil && !errors.Is(err, http.ErrServerClosed) {
- ctx := mctx.Annotate(context.Background(), a.params)
- params.Logger.Fatal(ctx, "serving http server", err)
- }
- }()
-
- return a, nil
-}
-
-func (a *api) Shutdown(ctx context.Context) error {
- if err := a.srv.Shutdown(ctx); err != nil {
- return err
- }
-
- return nil
-}
-
-func (a *api) handler() http.Handler {
-
- var staticHandler http.Handler
- if a.params.StaticDir != "" {
- staticHandler = http.FileServer(http.Dir(a.params.StaticDir))
- } else {
- staticHandler = httputil.NewSingleHostReverseProxy(a.params.StaticProxy)
- }
-
- // sugar
-
- requirePow := func(h http.Handler) http.Handler {
- return a.requirePowMiddleware(h)
- }
-
- formMiddleware := func(h http.Handler) http.Handler {
- h = checkCSRFMiddleware(h)
- h = disallowGetMiddleware(h)
- h = logReqMiddleware(h)
- h = addResponseHeaders(map[string]string{
- "Cache-Control": "no-store, max-age=0",
- "Pragma": "no-cache",
- "Expires": "0",
- }, h)
- return h
- }
-
- auther := NewAuther(a.params.AuthUsers)
-
- mux := http.NewServeMux()
-
- mux.Handle("/", staticHandler)
-
- {
- apiMux := http.NewServeMux()
- apiMux.Handle("/pow/challenge", a.newPowChallengeHandler())
- apiMux.Handle("/pow/check",
- requirePow(
- http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {}),
- ),
- )
-
- apiMux.Handle("/mailinglist/subscribe", requirePow(a.mailingListSubscribeHandler()))
- apiMux.Handle("/mailinglist/finalize", a.mailingListFinalizeHandler())
- apiMux.Handle("/mailinglist/unsubscribe", a.mailingListUnsubscribeHandler())
-
- apiMux.Handle("/chat/global/", http.StripPrefix("/chat/global", newChatHandler(
- a.params.GlobalRoom,
- a.params.UserIDCalculator,
- a.requirePowMiddleware,
- )))
-
- mux.Handle("/api/", http.StripPrefix("/api", formMiddleware(apiMux)))
- }
-
- {
- v2Mux := http.NewServeMux()
- v2Mux.Handle("/follow.html", a.renderDumbTplHandler("follow.html"))
- v2Mux.Handle("/posts/", http.StripPrefix("/posts",
- apiutil.MethodMux(map[string]http.Handler{
- "GET": a.renderPostHandler(),
- "EDIT": a.editPostHandler(),
- "POST": authMiddleware(auther,
- formMiddleware(a.postPostHandler()),
- ),
- "DELETE": authMiddleware(auther,
- formMiddleware(a.deletePostHandler()),
- ),
- "PREVIEW": formMiddleware(a.previewPostHandler()),
- }),
- ))
- v2Mux.Handle("/assets/", http.StripPrefix("/assets",
- apiutil.MethodMux(map[string]http.Handler{
- "GET": a.getPostAssetHandler(),
- "POST": authMiddleware(auther,
- formMiddleware(a.postPostAssetHandler()),
- ),
- "DELETE": authMiddleware(auther,
- formMiddleware(a.deletePostAssetHandler()),
- ),
- }),
- ))
- v2Mux.Handle("/", a.renderIndexHandler())
-
- mux.Handle("/v2/", http.StripPrefix("/v2", v2Mux))
- }
-
- var globalHandler http.Handler = mux
- globalHandler = setCSRFMiddleware(globalHandler)
- globalHandler = setLoggerMiddleware(a.params.Logger, globalHandler)
-
- return globalHandler
-}