Repos / s4g / 37137f627e
commit 37137f627e2f999be9d317d79ae6c6a486c0f147
Author: Nhân <hi@imnhan.com>
Date:   Wed Jul 12 17:04:40 2023 +0700

    fix custom root web path
    
    The preview server now transparently works regardless of Root setting.

diff --git a/livereload/livereload.go b/livereload/livereload.go
index 4031248..6bd7e02 100644
--- a/livereload/livereload.go
+++ b/livereload/livereload.go
@@ -43,56 +43,59 @@ func init() {
 	)
 }
 
+func handleFunc(w http.ResponseWriter, r *http.Request) {
+	clientId := r.Header.Get(clientIdHeader)
+	state.mut.RLock()
+	shouldReload, ok := state.clients[clientId]
+	state.mut.RUnlock()
+
+	// New client: add client to state, don't reload
+	if !ok {
+		//fmt.Println("New livereload client:", clientId)
+		state.mut.Lock()
+		state.clients[clientId] = false
+		state.mut.Unlock()
+		w.Write(dontReload)
+		return
+	}
+
+	// Existing client:
+	if shouldReload {
+		w.Write(pleaseReload)
+		// On reload, the browser tab will generate another client ID,
+		// so we can safely delete the old client ID now:
+		state.mut.Lock()
+		delete(state.clients, clientId)
+		state.mut.Unlock()
+	} else {
+		w.Write(dontReload)
+	}
+}
+
 // For html pages, insert a script tag to enable livereload
-func Middleware(fsys writablefs.FS, f http.Handler) http.Handler {
+func Middleware(root string, fsys writablefs.FS, f http.Handler) http.Handler {
+
+	// Handle AJAX endpoint
+	http.HandleFunc(endpoint, handleFunc)
+
 	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 		path := r.URL.Path
 
-		// Handle AJAX endpoint
-		if path == endpoint {
-			clientId := r.Header.Get(clientIdHeader)
-			state.mut.RLock()
-			shouldReload, ok := state.clients[clientId]
-			state.mut.RUnlock()
-
-			// New client: add client to state, don't reload
-			if !ok {
-				//fmt.Println("New livereload client:", clientId)
-				state.mut.Lock()
-				state.clients[clientId] = false
-				state.mut.Unlock()
-				w.Write(dontReload)
-				return
-			}
-
-			// Existing client:
-			if shouldReload {
-				w.Write(pleaseReload)
-				// On reload, the browser tab will generate another client ID,
-				// so we can safely delete the old client ID now:
-				state.mut.Lock()
-				delete(state.clients, clientId)
-				state.mut.Unlock()
-			} else {
-				w.Write(dontReload)
-			}
-			return
-		}
-
 		// For non-html requests, fall through to default FileServer handler
 		if !strings.HasSuffix(path, ".html") && !strings.HasSuffix(path, "/") {
 			f.ServeHTTP(w, r)
 			return
 		}
 
-		if strings.HasSuffix(path, "/") {
-			path += "index.html"
+		filePath := path
+
+		if strings.HasSuffix(filePath, "/") {
+			filePath += "index.html"
 		}
 
-		// Filesystem access doesn't expect leading slash "/"
-		path = strings.TrimPrefix(path, "/")
+		filePath = strings.TrimPrefix(filePath, root)
 
-		originalContent, err := fs.ReadFile(fsys, path)
+		originalContent, err := fs.ReadFile(fsys, filePath)
 		if err != nil {
 			f.ServeHTTP(w, r)
 			return
@@ -102,6 +105,7 @@ func Middleware(fsys writablefs.FS, f http.Handler) http.Handler {
 	})
 }
 
+// Tell current browser tabs to reload
 func Trigger() {
 	state.mut.Lock()
 	defer state.mut.Unlock()
diff --git a/main.go b/main.go
index 0b7ad16..6d5f2e7 100644
--- a/main.go
+++ b/main.go
@@ -82,7 +82,7 @@ func handleServeCmd(folder, port string) {
 
 	fsys := writablefs.WriteDirFS(absolutePath)
 
-	regenerate(fsys)
+	site := regenerate(fsys)
 
 	// TODO: only rebuild necessary bits instead of regenerating
 	// the whole thing. To do that I'll probably need to:
@@ -98,18 +98,25 @@ func handleServeCmd(folder, port string) {
 	})
 	defer closeWatcher()
 
-	println("Serving local website at http://localhost:" + port)
-	http.Handle("/", livereload.Middleware(fsys, http.FileServer(http.FS(fsys))))
+	println("Serving local website at http://localhost:" + port + site.Root)
+	http.Handle(
+		site.Root,
+		livereload.Middleware(
+			site.Root,
+			fsys,
+			http.StripPrefix(site.Root, http.FileServer(http.FS(fsys))),
+		),
+	)
 	err = http.ListenAndServe("127.0.0.1:"+port, nil)
 	if err != nil {
 		panic(err)
 	}
 }
 
-func regenerate(fsys writablefs.FS) {
+func regenerate(fsys writablefs.FS) (site SiteMetadata) {
 	defer timer("Took %s")()
 
-	site := ReadSiteMetadata(fsys)
+	site = ReadSiteMetadata(fsys)
 	articles := findArticles(fsys)
 
 	if len(articles) == 0 {
@@ -160,13 +167,15 @@ func regenerate(fsys writablefs.FS) {
 
 	fsys.WriteFile(
 		FeedPath,
-		generateFeed(site, articlesInFeed, site.HomePath+FeedPath),
+		generateFeed(site, articlesInFeed, site.Root+FeedPath),
 	)
 	generatedFiles[FeedPath] = true
 	fmt.Println("Generated", FeedPath)
 
 	DeleteOldGeneratedFiles(fsys, generatedFiles)
 	WriteManifest(fsys, generatedFiles)
+
+	return
 }
 
 type Article struct {
@@ -243,7 +252,7 @@ func (a *Article) WriteHtmlFile(
 		Title:         fmt.Sprintf("%s | %s", a.Title, site.Name),
 		Post:          a,
 		ArticlesInNav: articlesInNav,
-		Feed:          site.HomePath + FeedPath,
+		Feed:          site.Root + FeedPath,
 		Now:           time.Now(),
 		StartYear:     startYear,
 	})
@@ -288,7 +297,7 @@ func WriteHomePage(
 		Title:          fmt.Sprintf("%s - %s", site.Name, site.Tagline),
 		ArticlesInFeed: articlesInFeed,
 		ArticlesInNav:  articlesInNav,
-		Feed:           site.HomePath + FeedPath,
+		Feed:           site.Root + FeedPath,
 		Now:            time.Now(),
 		StartYear:      startYear,
 	})
diff --git a/metadata.go b/metadata.go
index f7fcc42..caadd06 100644
--- a/metadata.go
+++ b/metadata.go
@@ -17,7 +17,7 @@ type SiteMetadata struct {
 	Address      string
 	Name         string
 	Tagline      string
-	HomePath     string
+	Root         string
 	ShowFooter   bool
 	GenerateHome bool
 	AuthorName   string
@@ -36,7 +36,7 @@ type ArticleMetadata struct {
 
 func NewSiteMetadata() SiteMetadata {
 	return SiteMetadata{
-		HomePath:     "/",
+		Root:         "/",
 		ShowFooter:   true,
 		GenerateHome: true,
 	}
@@ -51,6 +51,10 @@ func ReadSiteMetadata(fsys writablefs.FS) SiteMetadata {
 	}
 
 	UnmarshalMetadata(data, &sm)
+
+	// normalize root path to always include leading & trailing slashes
+	sm.Root = fmt.Sprintf("/%s/", strings.Trim(sm.Root, "/"))
+
 	return sm
 }
 
diff --git a/theme/base.tmpl b/theme/base.tmpl
index 6edbe18..5792618 100644
--- a/theme/base.tmpl
+++ b/theme/base.tmpl
@@ -6,7 +6,7 @@
   <title>{{.Title}}</title>
   <meta name="viewport" content="width=device-width, initial-scale=1.0" />
   <link rel="alternate" type="application/atom+xml" title="Atom feed" href="{{.Feed}}">
-  <link rel="stylesheet" href="{{.Site.HomePath}}_theme/base.css">
+  <link rel="stylesheet" href="{{.Site.Root}}_theme/base.css">
   {{- template "head" .}}
 </head>
 
diff --git a/theme/home.tmpl b/theme/home.tmpl
index a928083..d2b8297 100644
--- a/theme/home.tmpl
+++ b/theme/home.tmpl
@@ -9,12 +9,12 @@
 <hr>
 
 <div class="pages">
-  <a href="{{.Site.HomePath}}">Home</a>
+  <a href="{{.Site.Root}}">Home</a>
 {{- range .ArticlesInNav}}
   <a href="{{.WebPath}}">{{.Title}}</a>
 {{- end}}
   <a class="feed-link" href="{{.Feed}}">
-    <img src="{{.Site.HomePath}}_theme/feed.svg" alt="Atom Feed" title="Atom Feed">
+    <img src="{{.Site.Root}}_theme/feed.svg" alt="Atom Feed" title="Atom Feed">
   </a>
 </div>
 
diff --git a/theme/includes.tmpl b/theme/includes.tmpl
index a43ffda..19b5122 100644
--- a/theme/includes.tmpl
+++ b/theme/includes.tmpl
@@ -1,9 +1,9 @@
 {{define "navbar"}}
-<link rel="stylesheet" href="{{.Site.HomePath}}_theme/navbar.css">
+<link rel="stylesheet" href="{{.Site.Root}}_theme/navbar.css">
 <nav>
-  <a href="{{.Site.HomePath}}">Home</a>
+  <a href="{{.Site.Root}}">Home</a>
   {{- range .ArticlesInNav}}
-  <a href="{{$.Site.HomePath}}{{.WebPath}}">{{.Title}}</a>
+  <a href="{{$.Site.Root}}{{.WebPath}}">{{.Title}}</a>
   {{- end}}
 
   {{- if not .Post.PostedAt.IsZero}}
diff --git a/www/_theme/base.tmpl b/www/_theme/base.tmpl
index 6edbe18..5792618 100644
--- a/www/_theme/base.tmpl
+++ b/www/_theme/base.tmpl
@@ -6,7 +6,7 @@
   <title>{{.Title}}</title>
   <meta name="viewport" content="width=device-width, initial-scale=1.0" />
   <link rel="alternate" type="application/atom+xml" title="Atom feed" href="{{.Feed}}">
-  <link rel="stylesheet" href="{{.Site.HomePath}}_theme/base.css">
+  <link rel="stylesheet" href="{{.Site.Root}}_theme/base.css">
   {{- template "head" .}}
 </head>
 
diff --git a/www/_theme/home.tmpl b/www/_theme/home.tmpl
index a928083..d2b8297 100644
--- a/www/_theme/home.tmpl
+++ b/www/_theme/home.tmpl
@@ -9,12 +9,12 @@
 <hr>
 
 <div class="pages">
-  <a href="{{.Site.HomePath}}">Home</a>
+  <a href="{{.Site.Root}}">Home</a>
 {{- range .ArticlesInNav}}
   <a href="{{.WebPath}}">{{.Title}}</a>
 {{- end}}
   <a class="feed-link" href="{{.Feed}}">
-    <img src="{{.Site.HomePath}}_theme/feed.svg" alt="Atom Feed" title="Atom Feed">
+    <img src="{{.Site.Root}}_theme/feed.svg" alt="Atom Feed" title="Atom Feed">
   </a>
 </div>
 
diff --git a/www/_theme/includes.tmpl b/www/_theme/includes.tmpl
index a43ffda..19b5122 100644
--- a/www/_theme/includes.tmpl
+++ b/www/_theme/includes.tmpl
@@ -1,9 +1,9 @@
 {{define "navbar"}}
-<link rel="stylesheet" href="{{.Site.HomePath}}_theme/navbar.css">
+<link rel="stylesheet" href="{{.Site.Root}}_theme/navbar.css">
 <nav>
-  <a href="{{.Site.HomePath}}">Home</a>
+  <a href="{{.Site.Root}}">Home</a>
   {{- range .ArticlesInNav}}
-  <a href="{{$.Site.HomePath}}{{.WebPath}}">{{.Title}}</a>
+  <a href="{{$.Site.Root}}{{.WebPath}}">{{.Title}}</a>
   {{- end}}
 
   {{- if not .Post.PostedAt.IsZero}}
diff --git a/www/about/index.html b/www/about/index.html
index 0ba1993..a7ea93a 100644
--- a/www/about/index.html
+++ b/www/about/index.html
@@ -5,16 +5,16 @@
   <meta charset="utf-8" />
   <title>About | CoolZone</title>
   <meta name="viewport" content="width=device-width, initial-scale=1.0" />
-  <link rel="alternate" type="application/atom+xml" title="Atom feed" href="/feed.xml">
-  <link rel="stylesheet" href="/_theme/base.css">
+  <link rel="alternate" type="application/atom+xml" title="Atom feed" href="/webmaker2000/feed.xml">
+  <link rel="stylesheet" href="/webmaker2000/_theme/base.css">
 </head>
 
 <body>
 
-<link rel="stylesheet" href="/_theme/navbar.css">
+<link rel="stylesheet" href="/webmaker2000/_theme/navbar.css">
 <nav>
-  <a href="/">Home</a>
-  <a href="/about/">About</a>
+  <a href="/webmaker2000/">Home</a>
+  <a href="/webmaker2000/about/">About</a>
 
 </nav>
 <hr class="nav-hr">
diff --git a/www/feed.xml b/www/feed.xml
index 9f59f07..4852045 100644
--- a/www/feed.xml
+++ b/www/feed.xml
@@ -1,7 +1,7 @@
 <feed xmlns="http://www.w3.org/2005/Atom">
   <title>CoolZone</title>
   <id>https://coolzone.example.com/</id>
-  <link rel="self" href="/feed.xml"></link>
+  <link rel="self" href="/webmaker2000/feed.xml"></link>
   <updated>2023-04-05T00:00:00+07:00</updated>
   <author>
     <name>Coolio McCool</name>
diff --git a/www/index.html b/www/index.html
index 5d0ee5d..df81f2b 100644
--- a/www/index.html
+++ b/www/index.html
@@ -5,8 +5,8 @@
   <meta charset="utf-8" />
   <title>CoolZone - Cool people only.</title>
   <meta name="viewport" content="width=device-width, initial-scale=1.0" />
-  <link rel="alternate" type="application/atom+xml" title="Atom feed" href="/feed.xml">
-  <link rel="stylesheet" href="/_theme/base.css">
+  <link rel="alternate" type="application/atom+xml" title="Atom feed" href="/webmaker2000/feed.xml">
+  <link rel="stylesheet" href="/webmaker2000/_theme/base.css">
 </head>
 
 <body>
@@ -19,10 +19,10 @@ <h1 class="site-title">CoolZone</h1>
 <hr>
 
 <div class="pages">
-  <a href="/">Home</a>
+  <a href="/webmaker2000/">Home</a>
   <a href="about/">About</a>
-  <a class="feed-link" href="/feed.xml">
-    <img src="/_theme/feed.svg" alt="Atom Feed" title="Atom Feed">
+  <a class="feed-link" href="/webmaker2000/feed.xml">
+    <img src="/webmaker2000/_theme/feed.svg" alt="Atom Feed" title="Atom Feed">
   </a>
 </div>
 
diff --git a/www/mfws.html b/www/mfws.html
index cc7abef..a45d28d 100644
--- a/www/mfws.html
+++ b/www/mfws.html
@@ -5,16 +5,16 @@
   <meta charset="utf-8" />
   <title>This is a motherfucking website. | CoolZone</title>
   <meta name="viewport" content="width=device-width, initial-scale=1.0" />
-  <link rel="alternate" type="application/atom+xml" title="Atom feed" href="/feed.xml">
-  <link rel="stylesheet" href="/_theme/base.css">
+  <link rel="alternate" type="application/atom+xml" title="Atom feed" href="/webmaker2000/feed.xml">
+  <link rel="stylesheet" href="/webmaker2000/_theme/base.css">
 </head>
 
 <body>
 
-<link rel="stylesheet" href="/_theme/navbar.css">
+<link rel="stylesheet" href="/webmaker2000/_theme/navbar.css">
 <nav>
-  <a href="/">Home</a>
-  <a href="/about/">About</a>
+  <a href="/webmaker2000/">Home</a>
+  <a href="/webmaker2000/about/">About</a>
   <span class="posted-on">
     Posted on
     <time datetime="2023-04-05">
diff --git a/www/scale/index.html b/www/scale/index.html
index 882841b..c22f440 100644
--- a/www/scale/index.html
+++ b/www/scale/index.html
@@ -5,16 +5,16 @@
   <meta charset="utf-8" />
   <title>I&#39;m Going To Scale My Foot Up Your Ass | CoolZone</title>
   <meta name="viewport" content="width=device-width, initial-scale=1.0" />
-  <link rel="alternate" type="application/atom+xml" title="Atom feed" href="/feed.xml">
-  <link rel="stylesheet" href="/_theme/base.css">
+  <link rel="alternate" type="application/atom+xml" title="Atom feed" href="/webmaker2000/feed.xml">
+  <link rel="stylesheet" href="/webmaker2000/_theme/base.css">
 </head>
 
 <body>
 <div class="navbar-container">
-<link rel="stylesheet" href="/_theme/navbar.css">
+<link rel="stylesheet" href="/webmaker2000/_theme/navbar.css">
 <nav>
-  <a href="/">Home</a>
-  <a href="/about/">About</a>
+  <a href="/webmaker2000/">Home</a>
+  <a href="/webmaker2000/about/">About</a>
   <span class="posted-on">
     Posted on
     <time datetime="2008-04-24">
diff --git a/www/website.wbmkr2k b/www/website.wbmkr2k
index 9bd2671..c67de7c 100644
--- a/www/website.wbmkr2k
+++ b/www/website.wbmkr2k
@@ -1,6 +1,7 @@
 Address: https://coolzone.example.com
 Name: CoolZone
 Tagline: Cool people only.
+Root: /webmaker2000/
 
 AuthorName: Coolio McCool
 AuthorURI: https://author.example.com