Add http to https redirect
Signed-off-by: Bryan Frimin <bryan@getprobo.com>
This commit is contained in:
@@ -12,11 +12,11 @@
|
|||||||
// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||||
// PERFORMANCE OF THIS SOFTWARE.
|
// PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
|
||||||
// Package server provides functionality for serving the SPA frontend.
|
|
||||||
package server
|
package server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"errors"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
@@ -33,10 +33,10 @@ import (
|
|||||||
"github.com/getprobo/probo/pkg/server/web"
|
"github.com/getprobo/probo/pkg/server/web"
|
||||||
trust_pkg "github.com/getprobo/probo/pkg/trust"
|
trust_pkg "github.com/getprobo/probo/pkg/trust"
|
||||||
"github.com/go-chi/chi/v5"
|
"github.com/go-chi/chi/v5"
|
||||||
|
"go.gearno.de/kit/httpserver"
|
||||||
"go.gearno.de/kit/log"
|
"go.gearno.de/kit/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Config holds the configuration for the server
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
AllowedOrigins []string
|
AllowedOrigins []string
|
||||||
ExtraHeaderFields map[string]string
|
ExtraHeaderFields map[string]string
|
||||||
@@ -53,7 +53,6 @@ type Config struct {
|
|||||||
Logger *log.Logger
|
Logger *log.Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
// Server represents the main server that handles both API and frontend requests
|
|
||||||
type Server struct {
|
type Server struct {
|
||||||
apiServer *api.Server
|
apiServer *api.Server
|
||||||
webServer *web.Server
|
webServer *web.Server
|
||||||
@@ -64,9 +63,7 @@ type Server struct {
|
|||||||
logger *log.Logger
|
logger *log.Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewServer creates a new server instance
|
|
||||||
func NewServer(cfg Config) (*Server, error) {
|
func NewServer(cfg Config) (*Server, error) {
|
||||||
// Create API server
|
|
||||||
apiCfg := api.Config{
|
apiCfg := api.Config{
|
||||||
AllowedOrigins: cfg.AllowedOrigins,
|
AllowedOrigins: cfg.AllowedOrigins,
|
||||||
Probo: cfg.Probo,
|
Probo: cfg.Probo,
|
||||||
@@ -85,19 +82,16 @@ func NewServer(cfg Config) (*Server, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create web server for console SPA
|
|
||||||
webServer, err := web.NewServer()
|
webServer, err := web.NewServer()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create trust server for trust SPA
|
|
||||||
trustServer, err := trust.NewServer()
|
trustServer, err := trust.NewServer()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create main router
|
|
||||||
router := chi.NewRouter()
|
router := chi.NewRouter()
|
||||||
|
|
||||||
server := &Server{
|
server := &Server{
|
||||||
@@ -110,42 +104,56 @@ func NewServer(cfg Config) (*Server, error) {
|
|||||||
logger: cfg.Logger,
|
logger: cfg.Logger,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set up routes
|
|
||||||
server.setupRoutes()
|
server.setupRoutes()
|
||||||
|
|
||||||
return server, nil
|
return server, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// setupRoutes configures the routing for the server
|
|
||||||
func (s *Server) setupRoutes() {
|
func (s *Server) setupRoutes() {
|
||||||
// API routes
|
|
||||||
s.router.Mount("/api", s.apiServer)
|
s.router.Mount("/api", s.apiServer)
|
||||||
|
|
||||||
// Trust center routes by slug or ID
|
|
||||||
s.router.Route("/trust/{slugOrId}", func(r chi.Router) {
|
s.router.Route("/trust/{slugOrId}", func(r chi.Router) {
|
||||||
r.Use(s.loadTrustCenterBySlugOrID)
|
r.Use(s.loadTrustCenterBySlugOrID)
|
||||||
r.Use(s.stripTrustPrefix)
|
r.Use(s.stripTrustPrefix)
|
||||||
r.Mount("/", s.trustCenterRouter())
|
r.Mount("/", s.trustCenterRouter())
|
||||||
})
|
})
|
||||||
|
|
||||||
// Console SPA (catch-all)
|
|
||||||
s.router.Mount("/", s.webServer)
|
s.router.Mount("/", s.webServer)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ServeHTTP implements the http.Handler interface
|
|
||||||
func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
s.setExtraHeaders(w)
|
s.setExtraHeaders(w)
|
||||||
s.router.ServeHTTP(w, r)
|
s.router.ServeHTTP(w, r)
|
||||||
}
|
}
|
||||||
|
|
||||||
// setExtraHeaders adds configured extra headers to the response
|
|
||||||
func (s *Server) setExtraHeaders(w http.ResponseWriter) {
|
func (s *Server) setExtraHeaders(w http.ResponseWriter) {
|
||||||
for key, value := range s.extraHeaderFields {
|
for key, value := range s.extraHeaderFields {
|
||||||
w.Header().Set(key, value)
|
w.Header().Set(key, value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// loadTrustCenterBySlugOrID middleware loads trust center info from slug or ID and adds to context
|
func (s *Server) handleCustomDomain404(w http.ResponseWriter, r *http.Request) {
|
||||||
|
ctx := r.Context()
|
||||||
|
|
||||||
|
if r.TLS == nil {
|
||||||
|
domain := r.Host
|
||||||
|
|
||||||
|
_, err := s.proboService.LoadOrganizationByDomain(ctx, domain)
|
||||||
|
if err == nil {
|
||||||
|
httpsURL := "https://" + r.Host + r.URL.RequestURI()
|
||||||
|
s.logger.InfoCtx(ctx, "404 on HTTP custom domain, redirecting to HTTPS",
|
||||||
|
log.String("domain", domain),
|
||||||
|
log.String("from", r.URL.RequestURI()),
|
||||||
|
log.String("to", httpsURL),
|
||||||
|
)
|
||||||
|
http.Redirect(w, r, httpsURL, http.StatusMovedPermanently)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
httpserver.RenderError(w, http.StatusNotFound, errors.New("not found"))
|
||||||
|
}
|
||||||
|
|
||||||
func (s *Server) loadTrustCenterBySlugOrID(next http.Handler) http.Handler {
|
func (s *Server) loadTrustCenterBySlugOrID(next http.Handler) http.Handler {
|
||||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
ctx := r.Context()
|
ctx := r.Context()
|
||||||
@@ -206,7 +214,6 @@ func (s *Server) loadTrustCenterBySlugOrID(next http.Handler) http.Handler {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// loadTrustCenterByDomain middleware loads trust center info from custom domain and adds to context
|
|
||||||
func (s *Server) loadTrustCenterByDomain(next http.Handler) http.Handler {
|
func (s *Server) loadTrustCenterByDomain(next http.Handler) http.Handler {
|
||||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
ctx := r.Context()
|
ctx := r.Context()
|
||||||
@@ -243,22 +250,18 @@ func (s *Server) loadTrustCenterByDomain(next http.Handler) http.Handler {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// addTrustCenterToContext adds trust center identification to context
|
|
||||||
func (s *Server) addTrustCenterToContext(ctx context.Context, tenantID, organizationID interface{}) context.Context {
|
func (s *Server) addTrustCenterToContext(ctx context.Context, tenantID, organizationID interface{}) context.Context {
|
||||||
ctx = context.WithValue(ctx, trust_v1.CustomDomainTenantIDKey, tenantID)
|
ctx = context.WithValue(ctx, trust_v1.CustomDomainTenantIDKey, tenantID)
|
||||||
ctx = context.WithValue(ctx, trust_v1.CustomDomainOrganizationIDKey, organizationID)
|
ctx = context.WithValue(ctx, trust_v1.CustomDomainOrganizationIDKey, organizationID)
|
||||||
return ctx
|
return ctx
|
||||||
}
|
}
|
||||||
|
|
||||||
// stripTrustPrefix middleware strips /trust/{slugOrId} from the path
|
|
||||||
func (s *Server) stripTrustPrefix(next http.Handler) http.Handler {
|
func (s *Server) stripTrustPrefix(next http.Handler) http.Handler {
|
||||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
slugOrId := chi.URLParam(r, "slugOrId")
|
slugOrId := chi.URLParam(r, "slugOrId")
|
||||||
prefix := "/trust/" + slugOrId
|
prefix := "/trust/" + slugOrId
|
||||||
|
|
||||||
// Strip the prefix from the path
|
|
||||||
if r.URL.Path == prefix {
|
if r.URL.Path == prefix {
|
||||||
// Redirect to trailing slash for proper asset resolution
|
|
||||||
http.Redirect(w, r, prefix+"/", http.StatusMovedPermanently)
|
http.Redirect(w, r, prefix+"/", http.StatusMovedPermanently)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -272,24 +275,18 @@ func (s *Server) stripTrustPrefix(next http.Handler) http.Handler {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// trustCenterRouter returns a router for trust center content (API + frontend)
|
|
||||||
func (s *Server) trustCenterRouter() chi.Router {
|
func (s *Server) trustCenterRouter() chi.Router {
|
||||||
r := chi.NewRouter()
|
r := chi.NewRouter()
|
||||||
|
|
||||||
// Trust API routes
|
|
||||||
r.Mount("/api/trust/v1", s.apiServer.TrustAPIHandler())
|
r.Mount("/api/trust/v1", s.apiServer.TrustAPIHandler())
|
||||||
|
|
||||||
// Trust center frontend (catch-all)
|
|
||||||
r.Handle("/*", s.trustServer)
|
r.Handle("/*", s.trustServer)
|
||||||
|
|
||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
|
|
||||||
// TrustCenterHandler returns an HTTP handler for serving trust centers on custom domains
|
|
||||||
func (s *Server) TrustCenterHandler() http.Handler {
|
func (s *Server) TrustCenterHandler() http.Handler {
|
||||||
r := chi.NewRouter()
|
r := chi.NewRouter()
|
||||||
|
|
||||||
// Set security headers
|
|
||||||
r.Use(func(next http.Handler) http.Handler {
|
r.Use(func(next http.Handler) http.Handler {
|
||||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
w.Header().Set("Strict-Transport-Security", "max-age=31536000; preload")
|
w.Header().Set("Strict-Transport-Security", "max-age=31536000; preload")
|
||||||
@@ -298,10 +295,9 @@ func (s *Server) TrustCenterHandler() http.Handler {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
// Load organization by custom domain
|
|
||||||
r.Use(s.loadTrustCenterByDomain)
|
r.Use(s.loadTrustCenterByDomain)
|
||||||
|
r.NotFound(s.handleCustomDomain404)
|
||||||
|
|
||||||
// Mount trust center content
|
|
||||||
r.Mount("/", s.trustCenterRouter())
|
r.Mount("/", s.trustCenterRouter())
|
||||||
|
|
||||||
return r
|
return r
|
||||||
|
|||||||
Reference in New Issue
Block a user