about summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--config.toml7
-rw-r--r--content/post/searchix.md33
-rw-r--r--fly.toml3
-rw-r--r--go.mod1
-rw-r--r--go.sum2
-rw-r--r--internal/config/config.go3
-rw-r--r--internal/server/server.go32
-rw-r--r--internal/server/tls.go39
-rw-r--r--internal/vcs/repository.go34
-rw-r--r--templates/page.templ9
-rw-r--r--templates/style.css8
11 files changed, 137 insertions, 34 deletions
diff --git a/config.toml b/config.toml
index 979669f..a1302ee 100644
--- a/config.toml
+++ b/config.toml
@@ -13,14 +13,15 @@ domains = [
   "alanpearce.uk",
   "www.alanpearce.uk",
   "aln.pe",
-  "www.aln.pe",
-  "alanpearce-eu.fly.dev",
+  "*.aln.pe",
 ]
 
 oidc_host = "https://id.alanpearce.eu/"
 
 goatcounter = "https://stats.alanpearce.eu/count"
 
+wildcard_domain = "aln.pe"
+
 [[taxonomies]]
   name = "tags"
   feed = true
@@ -51,6 +52,8 @@ goatcounter = "https://stats.alanpearce.eu/count"
     "'sha256-dCSzNS1o8vygl80V2G2nPTiSOUNvyDnW+06hHS4ZdHQ='",
     ## atom.xml style
     "'sha256-CFhPA4p8skr5MyhReF+Tk2GzmYzXdECg8zm+o7EOVPI=",
+    ## cv style
+    "'sha256-FzrPQ6x2ugtwEN5peA8OqpIvrV3uXqkpfUMHG8P2uT0='",
   ]
   frame-ancestors = [
     "https://kagi.com",
diff --git a/content/post/searchix.md b/content/post/searchix.md
new file mode 100644
index 0000000..6e1962b
--- /dev/null
+++ b/content/post/searchix.md
@@ -0,0 +1,33 @@
+---
+title: "Announcing Searchix: Nix ecosystem search"
+date: 2024-07-11T15:05:00+02:00
+taxonomies:
+  tags: [nix, searchix]
+---
+
+I decided to create a search tool for NixOS, nix-darwin and
+home-manager options and packages, potentially with more sources to
+come.
+
+I called it [Searchix](https://searchix.alanpearce.eu/)
+
+For NixOS options and packages,
+[search.nixos.org](https://search.nixos.org) already exists (and works
+better than Searchix), whereas for home-manager and nix-darwin, I
+couldn't manage to find a web-based search tool that's still
+online[^1]. So I wrote one.
+
+What I'm happy about with it is that it doesn't _require_ JavaScript,
+but, if it's enabled, makes things a little bit better.  It's only my
+second project in Golang, so I still have things to learn, but I
+definitely think I will enjoy using this language further.
+
+It still has quite a bit of room for improvement, but I've been using
+it quite a lot recently, which leads me to think that other people
+might like to use it, too.
+
+If anyone would like to contribute, raise an issue, or host an
+instance themselves, the [project site and source code are on sr.ht](https://sr.ht/~alanpearce/searchix/).
+
+[^1]: I found [Home Manager Option Search](https://home-manager-options.extranix.com/) after I
+    started this project, but nothing for nix-darwin.
diff --git a/fly.toml b/fly.toml
index 7508dd6..ef6e3ea 100644
--- a/fly.toml
+++ b/fly.toml
@@ -18,6 +18,7 @@ primary_region = "ams"
   PRODUCTION = "true"
   VCS_LOCAL_PATH = "/data/website"
   VCS_REMOTE_URL = "https://git.alanpearce.eu/website.git"
+  ACME_SERVER_URL = "https://acme.alanpearce.eu"
 
 [[services]]
   internal_port = 8080
@@ -42,7 +43,7 @@ primary_region = "ams"
   [[services.http_checks]]
     grace_period = "10s"
     protocol = "https"
-    tls_server_name = "alanpearce-eu.fly.dev"
+    tls_server_name = "alanpearce.eu"
     interval = "10s"
     method = "GET"
     timeout = "1s"
diff --git a/go.mod b/go.mod
index 1c07814..b472a9e 100644
--- a/go.mod
+++ b/go.mod
@@ -20,6 +20,7 @@ require (
 	github.com/fsnotify/fsnotify v1.7.0
 	github.com/go-git/go-git/v5 v5.12.0
 	github.com/kevinpollet/nego v0.0.0-20211010160919-a65cd48cee43
+	github.com/libdns/acmedns v0.2.0
 	github.com/osdevisnot/sorvor v0.4.4
 	github.com/pberkel/caddy-storage-redis v1.2.0
 	github.com/snabb/sitemap v1.0.4
diff --git a/go.sum b/go.sum
index c11d666..7838b77 100644
--- a/go.sum
+++ b/go.sum
@@ -137,6 +137,8 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
 github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
 github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
 github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
+github.com/libdns/acmedns v0.2.0 h1:zTXdHZwe3r2issdVRyqt5/4X2yHpiBVmFnTrwBA29ik=
+github.com/libdns/acmedns v0.2.0/go.mod h1:XlKHilQQK/IGHYY//vCb903PdG4Wc/XnDQzcMp2hV3g=
 github.com/libdns/libdns v0.2.2 h1:O6ws7bAfRPaBsgAYt8MDe2HcNBGC29hkZ9MX2eUSX3s=
 github.com/libdns/libdns v0.2.2/go.mod h1:4Bj9+5CQiNMVGf87wjX4CY3HQJypUHRuLvlsfsZqLWQ=
 github.com/mholt/acmez/v2 v2.0.1 h1:3/3N0u1pLjMK4sNEAFSI+bcvzbPhRpY383sy1kLHJ6k=
diff --git a/internal/config/config.go b/internal/config/config.go
index 47d5de8..7ccad85 100644
--- a/internal/config/config.go
+++ b/internal/config/config.go
@@ -42,7 +42,8 @@ type Config struct {
 	OriginalDomain   string `toml:"original_domain"`
 	GoatCounter      URL    `toml:"goatcounter"`
 	Domains          []string
-	OIDCHost         URL `toml:"oidc_host"`
+	WildcardDomain   string `toml:"wildcard_domain"`
+	OIDCHost         URL    `toml:"oidc_host"`
 	Taxonomies       []Taxonomy
 	CSP              *CSP `toml:"content-security-policy"`
 	Extra            struct {
diff --git a/internal/server/server.go b/internal/server/server.go
index b174c0c..269ed9e 100644
--- a/internal/server/server.go
+++ b/internal/server/server.go
@@ -8,6 +8,7 @@ import (
 	"net/url"
 	"os"
 	"path/filepath"
+	"regexp"
 	"slices"
 	"strconv"
 	"strings"
@@ -74,7 +75,6 @@ func applyDevModeOverrides(config *cfg.Config, runtimeConfig *Config) {
 }
 
 func updateCSPHashes(config *cfg.Config, r *builder.Result) {
-	clear(config.CSP.StyleSrc)
 	for i, h := range r.Hashes {
 		config.CSP.StyleSrc[i] = fmt.Sprintf("'%s'", h)
 	}
@@ -184,21 +184,35 @@ func New(runtimeConfig *Config, log *log.Logger) (*Server, error) {
 	}
 
 	if runtimeConfig.Redirect {
+		re := regexp.MustCompile(
+			"^(.*)\\." + strings.ReplaceAll(config.WildcardDomain, ".", `\.`) + "$",
+		)
+		replace := "${1}." + config.Domains[0]
 		loggingMux.Handle(config.BaseURL.Hostname()+"/", mux)
 		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)
+			if slices.Contains(config.Domains, r.Host) {
+				path, _ := website.CanonicalisePath(r.URL.Path)
+				newURL := config.BaseURL.JoinPath(path)
+				http.Redirect(w, r, newURL.String(), http.StatusMovedPermanently)
+			} else {
+				url := config.BaseURL
+				url.Host = re.ReplaceAllString(r.Host, replace)
+				http.Redirect(w, r, url.String(), http.StatusTemporaryRedirect)
+			}
 		})
 	} else {
 		loggingMux.Handle("/", mux)
 	}
 
-	top.Handle("/",
-		serverHeaderHandler(
-			wrapHandlerWithLogging(loggingMux, log),
-		),
-	)
+	if runtimeConfig.Development {
+		top.Handle("/",
+			serverHeaderHandler(
+				wrapHandlerWithLogging(loggingMux, log),
+			),
+		)
+	} else {
+		top.Handle("/", serverHeaderHandler(loggingMux))
+	}
 
 	top.HandleFunc("/health", func(w http.ResponseWriter, _ *http.Request) {
 		w.WriteHeader(http.StatusNoContent)
diff --git a/internal/server/tls.go b/internal/server/tls.go
index 9481b6a..4d52b8d 100644
--- a/internal/server/tls.go
+++ b/internal/server/tls.go
@@ -12,6 +12,7 @@ import (
 	"github.com/ardanlabs/conf/v3"
 	"github.com/caddyserver/caddy/v2"
 	"github.com/caddyserver/certmagic"
+	"github.com/libdns/acmedns"
 	certmagic_redis "github.com/pberkel/caddy-storage-redis"
 	"gitlab.com/tozd/go/errors"
 )
@@ -24,6 +25,13 @@ type redisConfig struct {
 	KeyPrefix     string `conf:"default:certmagic"`
 }
 
+type acmeConfig struct {
+	Username  string `conf:"required"`
+	Password  string `conf:"required"`
+	Subdomain string `conf:"required"`
+	ServerURL string `conf:"env:SERVER_URL,default:https://acme.alanpearce.eu"`
+}
+
 func (s *Server) serveTLS() (err error) {
 	log := s.log.Named("tls")
 
@@ -32,10 +40,7 @@ func (s *Server) serveTLS() (err error) {
 	cfg := certmagic.NewDefault()
 	cfg.DefaultServerName = s.config.Domains[0]
 
-	issuer := &certmagic.DefaultACME
-	certmagic.DefaultACME.Agreed = true
-	certmagic.DefaultACME.Email = s.config.Email
-	certmagic.DefaultACME.Logger = certmagic.Default.Logger
+	var issuer *certmagic.ACMEIssuer
 
 	if s.runtimeConfig.Development {
 		ca := s.runtimeConfig.ACMECA
@@ -63,8 +68,8 @@ func (s *Server) serveTLS() (err error) {
 			ListenHost:              s.runtimeConfig.ListenAddress,
 			AltHTTPPort:             s.runtimeConfig.Port,
 			AltTLSALPNPort:          s.runtimeConfig.TLSPort,
+			Logger:                  certmagic.Default.Logger,
 		})
-		cfg.Issuers[0] = issuer
 	} else {
 		rc := &redisConfig{}
 		_, err = conf.Parse("REDIS", rc)
@@ -72,6 +77,27 @@ func (s *Server) serveTLS() (err error) {
 			return errors.Wrap(err, "could not parse redis config")
 		}
 
+		acme := &acmedns.Provider{}
+		_, err = conf.Parse("ACME", acme)
+		if err != nil {
+			return errors.Wrap(err, "could not parse ACME config")
+		}
+
+		issuer = certmagic.NewACMEIssuer(cfg, certmagic.ACMEIssuer{
+			CA:     certmagic.LetsEncryptProductionCA,
+			Email:  s.config.Email,
+			Agreed: true,
+			Logger: certmagic.Default.Logger,
+			DNS01Solver: &certmagic.DNS01Solver{
+				DNSManager: certmagic.DNSManager{
+					DNSProvider: acme,
+					Logger:      certmagic.Default.Logger,
+				},
+			},
+		})
+
+		log.Info("acme", "username", acme.Username, "subdomain", acme.Subdomain, "server_url", acme.ServerURL)
+
 		rs := certmagic_redis.New()
 		rs.Address = []string{rc.Address}
 		rs.Username = rc.Username
@@ -87,6 +113,7 @@ func (s *Server) serveTLS() (err error) {
 			return errors.Wrap(err, "could not provision redis storage")
 		}
 	}
+	cfg.Issuers[0] = issuer
 
 	ln, err := listenfd.GetListener(
 		1,
@@ -137,7 +164,7 @@ func (s *Server) serveTLS() (err error) {
 		"https_port",
 		s.runtimeConfig.TLSPort,
 	)
-	err = cfg.ManageSync(context.TODO(), s.config.Domains)
+	err = cfg.ManageAsync(context.TODO(), s.config.Domains)
 	if err != nil {
 		return errors.Wrap(err, "could not enable TLS")
 	}
diff --git a/internal/vcs/repository.go b/internal/vcs/repository.go
index e034ea4..5950e53 100644
--- a/internal/vcs/repository.go
+++ b/internal/vcs/repository.go
@@ -7,6 +7,7 @@ import (
 	"go.alanpearce.eu/x/log"
 
 	"github.com/go-git/go-git/v5"
+	"github.com/go-git/go-git/v5/plumbing"
 	"gitlab.com/tozd/go/errors"
 )
 
@@ -61,13 +62,8 @@ func (r *Repository) Update() (bool, error) {
 	}
 
 	r.log.Info("updating from", "rev", head.Hash().String())
-	wt, err := r.repo.Worktree()
-	if err != nil {
-		return false, err
-	}
-	err = wt.Pull(&git.PullOptions{
-		SingleBranch: true,
-		Force:        true,
+	err = r.repo.Fetch(&git.FetchOptions{
+		Prune: true,
 	})
 	if err != nil {
 		if errors.Is(err, git.NoErrAlreadyUpToDate) {
@@ -79,11 +75,31 @@ func (r *Repository) Update() (bool, error) {
 		return false, err
 	}
 
-	head, err = r.repo.Head()
+	rem, err := r.repo.Remote("origin")
 	if err != nil {
 		return false, err
 	}
-	r.log.Info("updated to", "rev", head.Hash().String())
+	refs, err := rem.List(&git.ListOptions{
+		Timeout: 5,
+	})
+
+	var hash plumbing.Hash
+	for _, ref := range refs {
+		if ref.Name() == plumbing.Main {
+			hash = ref.Hash()
+		}
+	}
+
+	wt, err := r.repo.Worktree()
+	if err != nil {
+		return false, err
+	}
+	wt.Checkout(&git.CheckoutOptions{
+		Hash:  hash,
+		Force: true,
+	})
+
+	r.log.Info("updated to", "rev", hash)
 
 	return true, r.Clean(wt)
 }
diff --git a/templates/page.templ b/templates/page.templ
index fb91b23..39dd263 100644
--- a/templates/page.templ
+++ b/templates/page.templ
@@ -4,6 +4,7 @@ import (
 	"io/fs"
 
 	"go.alanpearce.eu/website/internal/config"
+	"net/url"
 )
 
 var (
@@ -86,8 +87,12 @@ templ Page(site *config.Config, page PageSettings) {
 	</html>
 }
 
-func mkURL(u config.URL, path string, title string) string {
-	q := u.Query()
+func mkURL(original config.URL, path string, title string) string {
+	ou := *original.URL
+	u := config.URL{
+		URL: &ou,
+	}
+	q := url.Values{}
 	q.Add("p", path)
 	q.Add("t", title)
 	u.RawQuery = q.Encode()
diff --git a/templates/style.css b/templates/style.css
index 84ce1ce..e9a2955 100644
--- a/templates/style.css
+++ b/templates/style.css
@@ -188,13 +188,13 @@ ul.h-feed li a:visited {
 
 .tags {
   font-size: small;
-}
-
-.p-categories,
-ul.tags {
   display: inline-block;
   padding-inline-start: 0;
 }
+.p-categories {
+  padding-inline-start: 1ex;
+}
+
 .tags li {
   list-style: none;
   display: inline-block;