diff --git a/cmd/fileserver/main.go b/cmd/fileserver/main.go index def7ee4..185592d 100644 --- a/cmd/fileserver/main.go +++ b/cmd/fileserver/main.go @@ -1,6 +1,7 @@ package main import ( + "flag" "fmt" "log" @@ -9,7 +10,19 @@ import ( ) func main() { - cfg, err := config.LoadConfig("config.yaml") + var cfgFlag = flag.String("c", "config.yaml", "Configuration file to load") + var genFlag = flag.String("g", "", "Generate an example configuration to the given file") + flag.Parse() + + if *genFlag != "" { + if err := config.WriteExampleConfig(*genFlag); err != nil { + log.Fatalf("Failed to create example config: %v", err) + } + log.Printf("Generated example configuration at: %s\n", *genFlag) + return + } + + cfg, err := config.LoadConfig(*cfgFlag) if err != nil { log.Fatalf("Failed to load config: %v", err) } diff --git a/embed.go b/embed.go index b7a8e7f..1e16e82 100644 --- a/embed.go +++ b/embed.go @@ -4,3 +4,6 @@ import "embed" //go:embed templates/* var TemplateFS embed.FS + +//go:embed static/* +var StaticFS embed.FS diff --git a/internal/config/config.go b/internal/config/config.go index 4d0de72..29f5925 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -6,12 +6,34 @@ import ( "gopkg.in/yaml.v3" ) -type Config struct { - ServeDirectory string `yaml:"serveDirectory"` - Port int `int:"port"` +var defaultCfg = Config{ + ServerName: "My Fileserver", + Port: 8080, + Directories: []Directory{ + { + Id: "example", + DisplayName: "Example Directory", + Path: "/path/to/directory", + }, + }, } -// LoadConfig loads a Config from the given path. +// Directory represents a configuration for a directory to be served. +type Directory struct { + Id string `yaml:"id"` + DisplayName string `yaml:"display_name"` + Description string `yaml:"description"` + Path string `yaml:"path"` +} + +// Config represents a configuration for the fileserver. +type Config struct { + ServerName string `yaml:"server_name"` + Port int `yaml:"port"` + Directories []Directory `yaml:"directories"` +} + +// LoadConfig loads configuration from a YAML file. func LoadConfig(path string) (*Config, error) { data, err := os.ReadFile(path) if err != nil { @@ -25,3 +47,13 @@ func LoadConfig(path string) (*Config, error) { return &cfg, nil } + +// WriteExampleConfig writes an example configuration to a YAML file. +func WriteExampleConfig(filepath string) error { + data, err := yaml.Marshal(&defaultCfg) + if err != nil { + return err + } + + return os.WriteFile(filepath, data, 0644) +} diff --git a/internal/server/controllers/directories.go b/internal/server/controllers/directories.go new file mode 100644 index 0000000..deb97f7 --- /dev/null +++ b/internal/server/controllers/directories.go @@ -0,0 +1,16 @@ +package controllers + +import ( + "git.zervo.org/zervo/fileserver/internal/config" + "git.zervo.org/zervo/fileserver/internal/server/views" + "github.com/kataras/iris/v12" +) + +// DirectoriesRoute registers all routes under /directories +func DirectoriesRoute(app *iris.Application, cfg *config.Config) { + party := app.Party("/directories") + + party.Get("/", func(ctx iris.Context) { + views.DirectoriesView(ctx, cfg) + }) +} diff --git a/internal/server/server.go b/internal/server/server.go index 0489f38..e642c7b 100644 --- a/internal/server/server.go +++ b/internal/server/server.go @@ -1,6 +1,7 @@ package server import ( + "errors" "fmt" "io/fs" "net/http" @@ -9,6 +10,7 @@ import ( "git.zervo.org/zervo/fileserver" "git.zervo.org/zervo/fileserver/internal/config" + "git.zervo.org/zervo/fileserver/internal/server/controllers" "github.com/kataras/iris/v12" "github.com/kataras/iris/v12/view" ) @@ -17,16 +19,23 @@ import ( func Start(cfg *config.Config) error { app := iris.New() - subFs, err := fs.Sub(fileserver.TemplateFS, "templates") + templates, err := fs.Sub(fileserver.TemplateFS, "templates") if err != nil { - return fmt.Errorf("Failed to sub fs") + return errors.New("fs.Sub operation on TemplateFS") } - tmpl := view.HTML(subFs, ".html") - app.RegisterView(tmpl) + templater := view.HTML(templates, ".html").Layout("layouts/main.html") + app.RegisterView(templater) - app.Get("/", func(ctx iris.Context) { - files, err := os.ReadDir(cfg.ServeDirectory) + // Register core routes + app.Get("/", index) + app.Get("/favicon.ico", favicon) + + // Register controller routes + controllers.DirectoriesRoute(app, cfg) + + app.Get("/aaa", func(ctx iris.Context) { + files, err := os.ReadDir("test") if err != nil { ctx.StatusCode(http.StatusInternalServerError) ctx.WriteString("Could not read directory") @@ -48,9 +57,25 @@ func Start(cfg *config.Config) error { app.Get("/download/{filename:string}", func(ctx iris.Context) { filename := ctx.Params().Get("filename") - filePath := filepath.Join(cfg.ServeDirectory, filename) + filePath := filepath.Join("test", filename) ctx.SendFile(filePath, filename) }) return app.Listen(":" + fmt.Sprint(cfg.Port)) } + +// index redirects request from '/' to '/directories'. +func index(ctx iris.Context) { + ctx.Redirect("/directories") +} + +// favicon returns favicon data upon request. +func favicon(ctx iris.Context) { + data, err := fileserver.StaticFS.ReadFile("static/favicon.ico") + if err != nil { + ctx.StatusCode(http.StatusNotFound) + return + } + ctx.ContentType("image/x-icon") + ctx.Write(data) +} diff --git a/internal/server/views/directories.go b/internal/server/views/directories.go new file mode 100644 index 0000000..8b3a989 --- /dev/null +++ b/internal/server/views/directories.go @@ -0,0 +1,29 @@ +package views + +import ( + "fmt" + + "git.zervo.org/zervo/fileserver/internal/config" + "git.zervo.org/zervo/fileserver/internal/util/data" + "github.com/kataras/iris/v12" +) + +type directorypage struct { + Directories []config.Directory +} + +// DirectoriesView renders the view for '/directories'. +func DirectoriesView(ctx iris.Context, cfg *config.Config) { + ctx.CompressWriter(true) + ctx.ViewData("", data.LayoutData{ + ServerName: cfg.ServerName, + ServerVersion: "0.0.1", + Page: directorypage{ + Directories: cfg.Directories, + }, + }) + if err := ctx.View("directories.html"); err != nil { + errorView(ctx, fmt.Errorf("Failed to render DirectoriesView: %v", err)) + return + } +} diff --git a/internal/server/views/error.go b/internal/server/views/error.go new file mode 100644 index 0000000..f8a6cbd --- /dev/null +++ b/internal/server/views/error.go @@ -0,0 +1,8 @@ +package views + +import "github.com/kataras/iris/v12" + +// errorView renders a view for unexpected errors. +func errorView(ctx iris.Context, err error) { + ctx.HTML(`

Unexpected Error


%s

`, err.Error()) +} diff --git a/internal/util/data/layout.go b/internal/util/data/layout.go new file mode 100644 index 0000000..faf0349 --- /dev/null +++ b/internal/util/data/layout.go @@ -0,0 +1,13 @@ +package data + +// LayoutData holds templating data for a layout. +type LayoutData struct { + // ServerName is the custom name of the server instance. + ServerName string + + // ServerVersion is a string representing the server version. + ServerVersion string + + // Page contains page/view-specific data. + Page any +} diff --git a/static/favicon.ico b/static/favicon.ico new file mode 100644 index 0000000..eae56c0 Binary files /dev/null and b/static/favicon.ico differ diff --git a/templates/directories.html b/templates/directories.html new file mode 100644 index 0000000..0b50f1d --- /dev/null +++ b/templates/directories.html @@ -0,0 +1,23 @@ + + + +

Welcome To

+ {{ render "partials/badge.html" . }} +

Select Directory

+
+ {{range .Page.Directories}} +
+
+
{{.DisplayName}}
+

{{.Description}}

+ Open +
+
+ {{else}} +
+ No directories found.
If you are the administrator of this server,
you can add directories in the server configuration. +
+ {{end}} +
+ + \ No newline at end of file diff --git a/templates/layouts/main.html b/templates/layouts/main.html new file mode 100644 index 0000000..c0b7fcf --- /dev/null +++ b/templates/layouts/main.html @@ -0,0 +1,27 @@ + + + + + + + + {{.ServerName}} + + + + +
+
+ {{ yield . }} +
+
+ + {{ render "partials/footer.html" . }} + + + + \ No newline at end of file diff --git a/templates/index.html b/templates/list.html similarity index 70% rename from templates/index.html rename to templates/list.html index f692b5e..84482e3 100644 --- a/templates/index.html +++ b/templates/list.html @@ -2,7 +2,6 @@ Go FileServer -

File Server

diff --git a/templates/partials/badge.html b/templates/partials/badge.html new file mode 100644 index 0000000..a472c44 --- /dev/null +++ b/templates/partials/badge.html @@ -0,0 +1,3 @@ +
+

{{if .ServerName}}{{.ServerName}}{{else}}My Fileserver{{end}}

+
\ No newline at end of file diff --git a/templates/partials/footer.html b/templates/partials/footer.html new file mode 100644 index 0000000..41dba5d --- /dev/null +++ b/templates/partials/footer.html @@ -0,0 +1,23 @@ + \ No newline at end of file