diff options
| -rw-r--r-- | .gitignore | 1 | ||||
| -rw-r--r-- | backend/config.go | 3 | ||||
| -rw-r--r-- | backend/router.go | 45 | ||||
| -rw-r--r-- | go.mod | 4 | ||||
| -rw-r--r-- | main.go | 39 |
5 files changed, 89 insertions, 3 deletions
@@ -156,3 +156,4 @@ go.work.sum # env file .idea +public diff --git a/backend/config.go b/backend/config.go new file mode 100644 index 0000000..b7624fd --- /dev/null +++ b/backend/config.go @@ -0,0 +1,3 @@ +package backend + +type Config struct{} diff --git a/backend/router.go b/backend/router.go index 6c7f9f8..4c3b2f8 100644 --- a/backend/router.go +++ b/backend/router.go @@ -1,7 +1,10 @@ package backend import ( + "embed" + "io/fs" "net/http" + "strings" "time" "github.com/go-chi/chi/v5" @@ -22,3 +25,45 @@ func NewRouter() *chi.Mux { }) return r } + +// httpEmbedFS is an implementation of fs.FS, fs.ReadDirFS and fs.ReadFileFS helping to manage embed.FS for http server +type httpEmbedFS struct { + embed.FS + prefix string +} + +func (h *httpEmbedFS) Open(name string) (fs.File, error) { + return h.FS.Open(h.prefix + "/" + name) +} + +func (h *httpEmbedFS) ReadFile(name string) ([]byte, error) { + return h.FS.ReadFile(h.prefix + "/" + name) +} + +func (h *httpEmbedFS) ReadDir(name string) ([]fs.DirEntry, error) { + return h.FS.ReadDir(h.prefix + "/" + name) +} + +// UsableEmbedFS converts embed.FS into usable fs.FS by Golatt +// +// folder may not finish or start with a slash (/) +func UsableEmbedFS(folder string, em embed.FS) fs.FS { + return &httpEmbedFS{ + prefix: folder, + FS: em, + } +} + +func HandleStaticFiles(r *chi.Mux, path string, root fs.FS) { + if path != "/" && path[len(path)-1] != '/' { + r.Get(path, http.RedirectHandler(path+"/", 301).ServeHTTP) + path += "/" + } + path += "*" + + r.Get(path, func(w http.ResponseWriter, r *http.Request) { + ctx := chi.RouteContext(r.Context()) + pathPrefix := strings.TrimSuffix(ctx.RoutePattern(), "/*") + http.StripPrefix(pathPrefix, http.FileServerFS(root)).ServeHTTP(w, r) + }) +} @@ -3,6 +3,6 @@ module git.anhgelus.world/anhgelus/small-world go 1.25.1 require ( - github.com/go-chi/chi/v5 v5.2.3 // indirect - github.com/joho/godotenv v1.5.1 // indirect + github.com/go-chi/chi/v5 v5.2.3 + github.com/joho/godotenv v1.5.1 ) @@ -2,28 +2,65 @@ package main import ( "context" + "embed" "errors" + "flag" + "fmt" "log/slog" "net/http" "os" "os/signal" + "strconv" "syscall" "git.anhgelus.world/anhgelus/small-world/backend" "github.com/joho/godotenv" ) +//go:embed dist +var embeds embed.FS + +var ( + configFile = "config.toml" + port = 8000 + publicDir = "public" +) + func init() { err := godotenv.Load(".env") if err != nil && !errors.Is(err, os.ErrNotExist) { slog.Error("loading .env", "error", err) } + + if v := os.Getenv("CONFIG_FILE"); v != "" { + configFile = v + } + flag.StringVar(&configFile, "config", configFile, "config file") + + if v := os.Getenv("PORT"); v != "" { + port, err = strconv.Atoi(v) + if err != nil { + panic(err) + } + } + flag.IntVar(&port, "port", port, "server port") + + if v := os.Getenv("PUBLIC_DIR"); v != "" { + publicDir = v + } + flag.StringVar(&publicDir, "public", publicDir, "public directory") } + func main() { + flag.Parse() + r := backend.NewRouter() + backend.HandleStaticFiles(r, "/assets", backend.UsableEmbedFS("dist", embeds)) + backend.HandleStaticFiles(r, "/static", os.DirFS(publicDir)) + slog.Info("starting http server") - server := &http.Server{Addr: ":8000", Handler: r} + server := &http.Server{Addr: fmt.Sprintf(":%d", port), Handler: r} errChan := make(chan error) go startServer(server, errChan) |
