about summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--.ko.yaml4
-rw-r--r--config.toml1
-rw-r--r--go.mod2
-rw-r--r--go.sum4
-rw-r--r--internal/listenfd/listenfd.go67
-rw-r--r--internal/server/server.go54
-rw-r--r--internal/server/tcp.go2
-rw-r--r--internal/server/tls.go40
-rw-r--r--static/external-link.svg11
-rw-r--r--templates/page.templ2
-rw-r--r--templates/style.css7
11 files changed, 65 insertions, 129 deletions
diff --git a/.ko.yaml b/.ko.yaml
index 27d9e7d..15ce912 100644
--- a/.ko.yaml
+++ b/.ko.yaml
@@ -7,5 +7,5 @@ builds:
       - -tags
       - embed
     ldflags:
-      - -X internal/server.CommitSHA={{ .Git.FullCommit }}
-      - -X internal/server.ShortSHA={{ .Git.ShortCommit }}
+      - -X go.alanpearce.eu/website/internal/server.CommitSHA={{ .Git.FullCommit }}
+      - -X go.alanpearce.eu/website/internal/server.ShortSHA={{ .Git.ShortCommit }}
diff --git a/config.toml b/config.toml
index f6bf8ed..979669f 100644
--- a/config.toml
+++ b/config.toml
@@ -67,6 +67,7 @@ goatcounter = "https://stats.alanpearce.eu/count"
   x-content-type-options = "nosniff"
   referrer-policy = "strict-origin-when-cross-origin"
   strict-transport-security = "max-age=63072000; includeSubDomains; preload"
+  cross-origin-resource-policy = "same-site"
 
 [[menus.main]]
   name = "Home"
diff --git a/go.mod b/go.mod
index 05bb1fa..1c07814 100644
--- a/go.mod
+++ b/go.mod
@@ -26,7 +26,7 @@ require (
 	github.com/stefanfritsch/goldmark-fences v1.0.0
 	github.com/yuin/goldmark v1.7.4
 	gitlab.com/tozd/go/errors v0.8.1
-	go.alanpearce.eu/x v0.0.0-20240630201241-61dffc8ded60
+	go.alanpearce.eu/x v0.0.0-20240703154602-00de54be50eb
 )
 
 require (
diff --git a/go.sum b/go.sum
index bbafba5..c11d666 100644
--- a/go.sum
+++ b/go.sum
@@ -225,8 +225,8 @@ github.com/zeebo/pcg v1.0.1 h1:lyqfGeWiv4ahac6ttHs+I5hwtH/+1mrhlCtVNQM2kHo=
 github.com/zeebo/pcg v1.0.1/go.mod h1:09F0S9iiKrwn9rlI5yjLkmrug154/YRW6KnnXVDM/l4=
 gitlab.com/tozd/go/errors v0.8.1 h1:RfylffRAsl3PbDdHNUBEkTleTCiL/RIT+Ef8p0HRNCI=
 gitlab.com/tozd/go/errors v0.8.1/go.mod h1:PvIdUMLpPwxr+KEBxghQaCMydHXGYdJQn/PhdMqYREY=
-go.alanpearce.eu/x v0.0.0-20240630201241-61dffc8ded60 h1:a4PJum9vqboETepA81QlBXAqgC9Zeu3F9EojnGiHauA=
-go.alanpearce.eu/x v0.0.0-20240630201241-61dffc8ded60/go.mod h1:GaYgUfXSlaHBvdrInLYyKDMKo2Bmx1+IIFrlnZkZW+A=
+go.alanpearce.eu/x v0.0.0-20240703154602-00de54be50eb h1:jJawVAo7/RFNbXGAyBSpX8ADb63G/nBideRgveBvmpA=
+go.alanpearce.eu/x v0.0.0-20240703154602-00de54be50eb/go.mod h1:FRM6J9HMQ/RV2Q5j+6RKBYWh/YNeEUriGSqDRchiHuQ=
 go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
 go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
 go.uber.org/atomic v1.8.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
diff --git a/internal/listenfd/listenfd.go b/internal/listenfd/listenfd.go
deleted file mode 100644
index 5287898..0000000
--- a/internal/listenfd/listenfd.go
+++ /dev/null
@@ -1,67 +0,0 @@
-package listenfd
-
-import (
-	"crypto/tls"
-	"net"
-	"os"
-	"strconv"
-
-	"go.alanpearce.eu/x/log"
-
-	"gitlab.com/tozd/go/errors"
-)
-
-const fdStart = 3
-
-func GetListener(i uint64, addr string, log *log.Logger) (l net.Listener, err error) {
-	l, err = getFDSocket(i)
-	if err != nil {
-		log.Warn("could not create listener from listenfd", "error", err)
-	}
-
-	log.Debug("listener from listenfd?", "passed", l != nil)
-	if l == nil {
-		l, err = net.Listen("tcp", addr)
-		if err != nil {
-			return nil, errors.Wrap(err, "could not create listener")
-		}
-	}
-
-	return
-}
-
-func GetListenerTLS(
-	i uint64,
-	addr string,
-	config *tls.Config,
-	log *log.Logger,
-) (l net.Listener, err error) {
-	l, err = GetListener(i, addr, log)
-	if err != nil {
-		return nil, err
-	}
-
-	return tls.NewListener(l, config), nil
-}
-
-func getFDSocket(i uint64) (net.Listener, error) {
-	lfds, present := os.LookupEnv("LISTEN_FDS")
-	if !present {
-		return nil, nil
-	}
-
-	fds, err := strconv.ParseUint(lfds, 10, 32)
-	if err != nil {
-		return nil, errors.Wrap(err, "could not parse LISTEN_FDS")
-	}
-	if i >= fds {
-		return nil, errors.Errorf("only %d fds available, requested index %d", fds, i)
-	}
-
-	l, err := net.FileListener(os.NewFile(uintptr(i+fdStart), ""))
-	if err != nil {
-		return nil, errors.Wrap(err, "could not create listener")
-	}
-
-	return l, nil
-}
diff --git a/internal/server/server.go b/internal/server/server.go
index 8523bc9..b174c0c 100644
--- a/internal/server/server.go
+++ b/internal/server/server.go
@@ -46,10 +46,9 @@ type Config struct {
 
 type Server struct {
 	*http.Server
-	redirectServer *http.Server
-	runtimeConfig  *Config
-	config         *cfg.Config
-	log            *log.Logger
+	runtimeConfig *Config
+	config        *cfg.Config
+	log           *log.Logger
 }
 
 func applyDevModeOverrides(config *cfg.Config, runtimeConfig *Config) {
@@ -110,20 +109,24 @@ func New(runtimeConfig *Config, log *log.Logger) (*Server, error) {
 		if err != nil {
 			return nil, err
 		}
-		_, err = vcs.CloneOrUpdate(vcsConfig, log.Named("vcs"))
-		if err != nil {
-			return nil, err
-		}
-		err = os.Chdir(runtimeConfig.Root)
-		if err != nil {
-			return nil, err
-		}
+		if vcsConfig.LocalPath != "" {
+			_, err = vcs.CloneOrUpdate(vcsConfig, log.Named("vcs"))
+			if err != nil {
+				return nil, err
+			}
+			err = os.Chdir(runtimeConfig.Root)
+			if err != nil {
+				return nil, err
+			}
 
-		builderConfig.Source = vcsConfig.LocalPath
+			builderConfig.Source = vcsConfig.LocalPath
 
-		publicDir := filepath.Join(runtimeConfig.Root, "public")
-		builderConfig.Destination = publicDir
-		runtimeConfig.Root = publicDir
+			publicDir := filepath.Join(runtimeConfig.Root, "public")
+			builderConfig.Destination = publicDir
+			runtimeConfig.Root = publicDir
+		} else {
+			log.Warn("in production mode without VCS configuration")
+		}
 	}
 
 	config, err := cfg.GetConfig(builderConfig.Source, log.Named("config"))
@@ -180,15 +183,13 @@ func New(runtimeConfig *Config, log *log.Logger) (*Server, error) {
 		return nil, errors.Wrap(err, "could not create website mux")
 	}
 
-	rMux := http.NewServeMux()
-	rMux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
-		path, _ := website.CanonicalisePath(r.URL.Path)
-		newURL := config.BaseURL.JoinPath(path)
-		http.Redirect(w, r, newURL.String(), 301)
-	})
 	if runtimeConfig.Redirect {
 		loggingMux.Handle(config.BaseURL.Hostname()+"/", mux)
-		loggingMux.Handle("/", rMux)
+		loggingMux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
+			path, _ := website.CanonicalisePath(r.URL.Path)
+			newURL := config.BaseURL.JoinPath(path)
+			http.Redirect(w, r, newURL.String(), http.StatusMovedPermanently)
+		})
 	} else {
 		loggingMux.Handle("/", mux)
 	}
@@ -211,13 +212,6 @@ func New(runtimeConfig *Config, log *log.Logger) (*Server, error) {
 			IdleTimeout:       10 * time.Minute,
 			Handler:           top,
 		},
-		redirectServer: &http.Server{
-			ReadHeaderTimeout: 10 * time.Second,
-			ReadTimeout:       1 * time.Minute,
-			WriteTimeout:      2 * time.Minute,
-			IdleTimeout:       10 * time.Minute,
-			Handler:           rMux,
-		},
 		log:           log,
 		config:        config,
 		runtimeConfig: runtimeConfig,
diff --git a/internal/server/tcp.go b/internal/server/tcp.go
index 12fdeb2..1627854 100644
--- a/internal/server/tcp.go
+++ b/internal/server/tcp.go
@@ -1,7 +1,7 @@
 package server
 
 import (
-	"go.alanpearce.eu/website/internal/listenfd"
+	"go.alanpearce.eu/x/listenfd"
 )
 
 func (s *Server) serveTCP() error {
diff --git a/internal/server/tls.go b/internal/server/tls.go
index cd2bfb8..9481b6a 100644
--- a/internal/server/tls.go
+++ b/internal/server/tls.go
@@ -7,7 +7,7 @@ import (
 	"net/http"
 	"strconv"
 
-	"go.alanpearce.eu/website/internal/listenfd"
+	"go.alanpearce.eu/x/listenfd"
 
 	"github.com/ardanlabs/conf/v3"
 	"github.com/caddyserver/caddy/v2"
@@ -25,7 +25,6 @@ type redisConfig struct {
 }
 
 func (s *Server) serveTLS() (err error) {
-	var issuer *certmagic.ACMEIssuer
 	log := s.log.Named("tls")
 
 	// setting cfg.Logger is too late somehow
@@ -33,9 +32,10 @@ func (s *Server) serveTLS() (err error) {
 	cfg := certmagic.NewDefault()
 	cfg.DefaultServerName = s.config.Domains[0]
 
-	issuer = &certmagic.DefaultACME
+	issuer := &certmagic.DefaultACME
 	certmagic.DefaultACME.Agreed = true
 	certmagic.DefaultACME.Email = s.config.Email
+	certmagic.DefaultACME.Logger = certmagic.Default.Logger
 
 	if s.runtimeConfig.Development {
 		ca := s.runtimeConfig.ACMECA
@@ -97,12 +97,38 @@ func (s *Server) serveTLS() (err error) {
 		return errors.Wrap(err, "could not bind plain socket")
 	}
 
-	go func(ln net.Listener) {
-		s.redirectServer.Handler = issuer.HTTPChallengeHandler(s.redirectServer.Handler)
-		if err := s.redirectServer.Serve(ln); err != nil && !errors.Is(err, http.ErrServerClosed) {
+	go func(ln net.Listener, srv *http.Server) {
+		httpMux := http.NewServeMux()
+		httpMux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
+			if certmagic.LooksLikeHTTPChallenge(r) && issuer.HandleHTTPChallenge(w, r) {
+				return
+			}
+			url := r.URL
+			url.Scheme = "https"
+			port := s.config.BaseURL.Port()
+			if port == "" {
+				url.Host = r.Host
+			} else {
+				host, _, err := net.SplitHostPort(r.Host)
+				if err != nil {
+					log.Warn("error splitting host and port", "error", err)
+					host = r.Host
+				}
+				url.Host = net.JoinHostPort(host, s.config.BaseURL.Port())
+			}
+			http.Redirect(w, r, url.String(), http.StatusMovedPermanently)
+		})
+		srv.Handler = httpMux
+
+		if err := srv.Serve(ln); err != nil && !errors.Is(err, http.ErrServerClosed) {
 			log.Error("error in http handler", "error", err)
 		}
-	}(ln)
+	}(ln, &http.Server{
+		ReadHeaderTimeout: s.ReadHeaderTimeout,
+		ReadTimeout:       s.ReadTimeout,
+		WriteTimeout:      s.WriteTimeout,
+		IdleTimeout:       s.IdleTimeout,
+	})
 
 	log.Debug(
 		"starting certmagic",
diff --git a/static/external-link.svg b/static/external-link.svg
deleted file mode 100644
index 35b5805..0000000
--- a/static/external-link.svg
+++ /dev/null
@@ -1,11 +0,0 @@
-<svg xmlns="http://www.w3.org/2000/svg"  viewBox="0 0 20 20">
-  <g style="stroke:rgb(35,82,124);stroke-width:1">
-    <line x1="5" y1="5" x2="5" y2="14" />
-    <line x1="14" y1="9" x2="14" y2="14" />
-    <line x1="5" y1="14" x2="14" y2="14" />
-    <line x1="5" y1="5" x2="9" y2="5"  />
-    <line x1="10" y1="2" x2="17" y2="2"  />
-    <line x1="17" y1="2" x2="17" y2="9" />
-    <line x1="10" y1="9" x2="17" y2="2" style="stroke-width:1.5" />
-  </g>
-</svg>
diff --git a/templates/page.templ b/templates/page.templ
index e5cb073..fb91b23 100644
--- a/templates/page.templ
+++ b/templates/page.templ
@@ -70,7 +70,7 @@ templ Page(site *config.Config, page PageSettings) {
 			<footer>
 				Content is
 				<a rel="license" href="http://creativecommons.org/licenses/by/4.0/">CC BY 4.0</a>.
-				<a href="https://git.alanpearce.eu/go.alanpearce.eu/website/">Site source code</a> is
+				<a href="https://git.alanpearce.eu/website/">Site source code</a> is
 				<a href="https://opensource.org/licenses/MIT">MIT</a>
 			</footer>
 			@counter(site, page.Path, page.Title)
diff --git a/templates/style.css b/templates/style.css
index 89fd58f..84ce1ce 100644
--- a/templates/style.css
+++ b/templates/style.css
@@ -11,7 +11,6 @@
   --code-background-color: #f2f2f2;
   --code-color: #222;
   --blockquote-color: #222;
-  --icon-external-link: url("/external-link.svg");
 }
 
 @media (prefers-color-scheme: dark) {
@@ -206,9 +205,3 @@ svg.rss-icon {
   height: 1.5ex;
   width: 1.5ex;
 }
-
-a[target="_blank"]::after {
-  content: "";
-  background: no-repeat var(--icon-external-link);
-  padding-right: 1em;
-}