github.com/quickfeed/quickfeed@v0.0.0-20240507093252-ed8ca812a09c/web/webserver.go (about)

     1  package web
     2  
     3  import (
     4  	"net/http"
     5  	"time"
     6  
     7  	"connectrpc.com/connect"
     8  	"github.com/quickfeed/quickfeed/internal/rand"
     9  	"github.com/quickfeed/quickfeed/qf/qfconnect"
    10  	"github.com/quickfeed/quickfeed/web/auth"
    11  	"github.com/quickfeed/quickfeed/web/hooks"
    12  	"github.com/quickfeed/quickfeed/web/interceptor"
    13  	"golang.org/x/oauth2"
    14  )
    15  
    16  const (
    17  	// streamTimeout is the timeout for the submission stream.
    18  	streamTimeout = 15 * time.Minute
    19  )
    20  
    21  func (s *QuickFeedService) NewQuickFeedHandler(tm *auth.TokenManager) (string, http.Handler) {
    22  	interceptors := connect.WithInterceptors(
    23  		interceptor.NewMetricsInterceptor(),
    24  		interceptor.NewValidationInterceptor(s.logger),
    25  		interceptor.NewTokenAuthInterceptor(s.logger, tm, s.db),
    26  		interceptor.NewUserInterceptor(s.logger, tm),
    27  		interceptor.NewAccessControlInterceptor(tm),
    28  		interceptor.NewTokenInterceptor(tm),
    29  	)
    30  	return qfconnect.NewQuickFeedServiceHandler(s, interceptors)
    31  }
    32  
    33  // RegisterRouter registers http endpoints for authentication API and scm provider webhooks.
    34  func (s *QuickFeedService) RegisterRouter(tm *auth.TokenManager, authConfig *oauth2.Config, public string) *http.ServeMux {
    35  	// Serve static files.
    36  	router := http.NewServeMux()
    37  	assets := http.FileServer(http.Dir(public + "/assets")) // skipcq: GO-S1034
    38  	dist := http.FileServer(http.Dir(public + "/dist"))     // skipcq: GO-S1034
    39  
    40  	router.Handle("/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    41  		http.ServeFile(w, r, public+"/assets/index.html")
    42  	}))
    43  	paths, handler := s.NewQuickFeedHandler(tm)
    44  	router.Handle(paths, controller(handler, streamTimeout))
    45  
    46  	router.Handle(auth.Assets, http.StripPrefix(auth.Assets, assets))
    47  	router.Handle(auth.Static, http.StripPrefix(auth.Static, dist))
    48  	// Register auth endpoints.
    49  	callbackSecret := rand.String()
    50  	router.HandleFunc(auth.Auth, auth.OAuth2Login(s.logger, authConfig, callbackSecret))
    51  	router.HandleFunc(auth.Callback, auth.OAuth2Callback(s.logger, s.db, tm, authConfig, callbackSecret))
    52  	router.HandleFunc(auth.Logout, auth.OAuth2Logout())
    53  
    54  	// Register hooks.
    55  	ghHook := hooks.NewGitHubWebHook(s.logger, s.db, s.scmMgr, s.runner, s.bh.Secret, s.streams, tm)
    56  	router.HandleFunc(auth.Hook, ghHook.Handle())
    57  
    58  	return router
    59  }
    60  
    61  // controller is a wrapper for the QuickFeedService handler that sets a write deadline for the submission stream.
    62  // TODO: Remove this when connect-go finally supports deadlines.
    63  // TODO: https://github.com/connectrpc/connect-go/issues/604
    64  func controller(h http.Handler, timeout time.Duration) http.Handler {
    65  	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    66  		if r.URL.Path == qfconnect.QuickFeedServiceSubmissionStreamProcedure {
    67  			control := http.NewResponseController(w)
    68  			_ = control.SetWriteDeadline(time.Now().Add(timeout))
    69  		}
    70  		h.ServeHTTP(w, r)
    71  	})
    72  }