lens @ d6cf67b3d7747b6274d92e394d75d348060fa5f5

feat: Add static file to output bin

Now the final binary has a standalone web server including necessary
static file.
 1diff --git a/README.md b/README.md
 2index 0940ae4032c1b21ce7b111cc8f20bd972167f0dd..6103dbfc3432ba7615a19ab7d4026389bd854e9b 100644
 3--- a/README.md
 4+++ b/README.md
 5@@ -7,7 +7,7 @@
 6 * Thumbnail system
 7 * Initial setup process
 8     * Also allow setup user config file
 9-* Single binary output
10+* ~~Single binary output~~
11 * Better worker pool. Allow cron job and ui config
12 * Alpine package and demo site
13 * Single image viewer and show exif info (not sure how yet)
14diff --git a/cmd/server/main.go b/cmd/server/main.go
15index 0fa5fea9d42fe4a4c8ea1c67444e034b938f68e9..4ca39de019df146d6b4348036a23abaa1087d090 100644
16--- a/cmd/server/main.go
17+++ b/cmd/server/main.go
18@@ -16,6 +16,7 @@ 	"gorm.io/driver/postgres"
19 	"gorm.io/driver/sqlite"
20 	"gorm.io/gorm"
21 
22+	"git.sr.ht/~gabrielgio/img"
23 	"git.sr.ht/~gabrielgio/img/pkg/components/auth"
24 	"git.sr.ht/~gabrielgio/img/pkg/components/filesystem"
25 	"git.sr.ht/~gabrielgio/img/pkg/components/media"
26@@ -69,8 +70,7 @@ 		panic("failed to decode key database: " + err.Error())
27 	}
28 
29 	r := router.New()
30-	r.ServeFiles("/static/{filepath:*}", "./static")
31-	r.NotFound = ext.NotFoundHTML
32+	r.GET("/static/{filepath:*}", ext.FileServer(img.StaticFS, "static/"))
33 
34 	authMiddleware := ext.NewAuthMiddleware(hexKey, logger.WithField("context", "auth"))
35 	logMiddleware := ext.NewLogMiddleare(logger.WithField("context", "http"))
36diff --git a/pkg/ext/fileserver.go b/pkg/ext/fileserver.go
37new file mode 100644
38index 0000000000000000000000000000000000000000..fdea08eb941ad2e92b15c9646aa4c4455c47d6a7
39--- /dev/null
40+++ b/pkg/ext/fileserver.go
41@@ -0,0 +1,18 @@
42+package ext
43+
44+import (
45+	"io/fs"
46+
47+	"github.com/valyala/fasthttp"
48+)
49+
50+type FileSystem interface {
51+	Open(name string) (fs.File, error)
52+}
53+
54+func FileServer(rootFS FileSystem, rootPath string) fasthttp.RequestHandler {
55+	return func(r *fasthttp.RequestCtx) {
56+		path := r.UserValue("filepath").(string)
57+		r.SendFile(rootPath + path)
58+	}
59+}
60diff --git a/pkg/worker/list_processor_test.go b/pkg/worker/list_processor_test.go
61index 35672f3b1e9b807fde0698ea752a7763777b7cfa..ce3ff0a3d531d278ac4afa5157d7fd8c3149acf8 100644
62--- a/pkg/worker/list_processor_test.go
63+++ b/pkg/worker/list_processor_test.go
64@@ -9,8 +9,9 @@ 	"math/rand"
65 	"sync"
66 	"testing"
67 
68+	"github.com/sirupsen/logrus"
69+
70 	"git.sr.ht/~gabrielgio/img/pkg/testkit"
71-	"github.com/sirupsen/logrus"
72 )
73 
74 type (
75diff --git a/tmpl.go b/static.go
76rename from tmpl.go
77rename to static.go
78index b11f9624a8e640836f0cef52fc7fffada179b776..1c6a086b026205651a0d837e10588b65c5df8aaf 100644
79--- a/tmpl.go
80+++ b/static.go
81@@ -7,10 +7,15 @@ 	"html/template"
82 	"io"
83 )
84 
85-//go:embed templates/*.html
86-var TemplateFS embed.FS
87+var (
88+	//go:embed templates/*.html
89+	TemplateFS embed.FS
90+
91+	//go:embed static/*
92+	StaticFS embed.FS
93 
94-var Template *template.Template
95+	Template *template.Template
96+)
97 
98 type HTMLView[T any] struct {
99 	Title    string