lens @ d6cf67b3d7747b6274d92e394d75d348060fa5f5

feat: Add static file to output bin

Now the final binary has a standalone web server including necessary
static file.
diff --git a/README.md b/README.md
index 0940ae4032c1b21ce7b111cc8f20bd972167f0dd..6103dbfc3432ba7615a19ab7d4026389bd854e9b 100644
--- a/README.md
+++ b/README.md
@@ -7,7 +7,7 @@
 * Thumbnail system
 * Initial setup process
     * Also allow setup user config file
-* Single binary output
+* ~~Single binary output~~
 * Better worker pool. Allow cron job and ui config
 * Alpine package and demo site
 * Single image viewer and show exif info (not sure how yet)
diff --git a/cmd/server/main.go b/cmd/server/main.go
index 0fa5fea9d42fe4a4c8ea1c67444e034b938f68e9..4ca39de019df146d6b4348036a23abaa1087d090 100644
--- a/cmd/server/main.go
+++ b/cmd/server/main.go
@@ -16,6 +16,7 @@ 	"gorm.io/driver/postgres"
 	"gorm.io/driver/sqlite"
 	"gorm.io/gorm"
 
+	"git.sr.ht/~gabrielgio/img"
 	"git.sr.ht/~gabrielgio/img/pkg/components/auth"
 	"git.sr.ht/~gabrielgio/img/pkg/components/filesystem"
 	"git.sr.ht/~gabrielgio/img/pkg/components/media"
@@ -69,8 +70,7 @@ 		panic("failed to decode key database: " + err.Error())
 	}
 
 	r := router.New()
-	r.ServeFiles("/static/{filepath:*}", "./static")
-	r.NotFound = ext.NotFoundHTML
+	r.GET("/static/{filepath:*}", ext.FileServer(img.StaticFS, "static/"))
 
 	authMiddleware := ext.NewAuthMiddleware(hexKey, logger.WithField("context", "auth"))
 	logMiddleware := ext.NewLogMiddleare(logger.WithField("context", "http"))
diff --git a/pkg/ext/fileserver.go b/pkg/ext/fileserver.go
new file mode 100644
index 0000000000000000000000000000000000000000..fdea08eb941ad2e92b15c9646aa4c4455c47d6a7
--- /dev/null
+++ b/pkg/ext/fileserver.go
@@ -0,0 +1,18 @@
+package ext
+
+import (
+	"io/fs"
+
+	"github.com/valyala/fasthttp"
+)
+
+type FileSystem interface {
+	Open(name string) (fs.File, error)
+}
+
+func FileServer(rootFS FileSystem, rootPath string) fasthttp.RequestHandler {
+	return func(r *fasthttp.RequestCtx) {
+		path := r.UserValue("filepath").(string)
+		r.SendFile(rootPath + path)
+	}
+}
diff --git a/pkg/worker/list_processor_test.go b/pkg/worker/list_processor_test.go
index 35672f3b1e9b807fde0698ea752a7763777b7cfa..ce3ff0a3d531d278ac4afa5157d7fd8c3149acf8 100644
--- a/pkg/worker/list_processor_test.go
+++ b/pkg/worker/list_processor_test.go
@@ -9,8 +9,9 @@ 	"math/rand"
 	"sync"
 	"testing"
 
+	"github.com/sirupsen/logrus"
+
 	"git.sr.ht/~gabrielgio/img/pkg/testkit"
-	"github.com/sirupsen/logrus"
 )
 
 type (
diff --git a/tmpl.go b/static.go
rename from tmpl.go
rename to static.go
index b11f9624a8e640836f0cef52fc7fffada179b776..1c6a086b026205651a0d837e10588b65c5df8aaf 100644
--- a/tmpl.go
+++ b/static.go
@@ -7,10 +7,15 @@ 	"html/template"
 	"io"
 )
 
-//go:embed templates/*.html
-var TemplateFS embed.FS
+var (
+	//go:embed templates/*.html
+	TemplateFS embed.FS
+
+	//go:embed static/*
+	StaticFS embed.FS
 
-var Template *template.Template
+	Template *template.Template
+)
 
 type HTMLView[T any] struct {
 	Title    string