{{$category}}
- {{end}} -{{$category}}
+ {{end}} +{{$service.Name}}
+{{$note.Text}}
{{$note.Name}}
-{{$note.Text}}
+ {{end}}diff --git a/README.md b/README.md index 479ef2a..9924282 100644 --- a/README.md +++ b/README.md @@ -240,19 +240,17 @@ theme: custom_css: "/path/to/custom.css" ``` -
Keep in mind, even if you provide a custom css file, the background and foreground colors will still be applied as long as you provide the correct templating keys in your custom css file like so:
-```css +``` body { background: {{.Background}}; color: {{.Foreground}}; } ``` - ### Groups@@ -370,3 +368,4 @@ A note entry is used to define a simple text note which has a title and a conten - **EASY_GATE_CONFIG_PATH:** Easy Gate configuration file path can be provided by this environment variable. The value will have precedence over the configuration file path provided in the command line. - **EASY_GATE_CONFIG:** Insted of providing a configuration file, it is possible to provide the entire configuration as a JSON or YAML string in this environment variable. The content of this variable will have precedence over the configuration file. +- **EASY_GATE_ROOT_PATH:** This environment variable lets you specify a custom root directory for the Easy Gate application. diff --git a/internal/config/config.go b/internal/config/config.go index 0c36810..277edb7 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -9,12 +9,6 @@ import ( "gopkg.in/yaml.v3" ) -// Easy Gate environment variable names -const ( - cfgPathEnv = "EASY_GATE_CONFIG_PATH" - cfgEnv = "EASY_GATE_CONFIG" -) - // Service - Easy Gate service configuration struct type Service struct { Icon string `json:"icon" yaml:"icon"` diff --git a/internal/config/config_test.go b/internal/config/config_test.go index 9543acf..7615e38 100644 --- a/internal/config/config_test.go +++ b/internal/config/config_test.go @@ -135,6 +135,28 @@ func TestGetPath(t *testing.T) { } } +func TestRootPath(t *testing.T) { + os.Setenv(rootPathEnv, "") + path := GetRootPath() + if path != "/" { + t.Fatal() + } + + os.Setenv(rootPathEnv, "aa") + path = GetRootPath() + if path != "/aa" { + t.Fatal() + } + + os.Setenv(rootPathEnv, "../../../abc") + path = GetRootPath() + if path != "/abc" { + t.Fatal() + } + + os.Unsetenv(rootPathEnv) +} + func TestValidate(t *testing.T) { cfg := Config{} err := validateConfig(&cfg) diff --git a/internal/config/load.go b/internal/config/load.go index dc81d43..cb6673b 100644 --- a/internal/config/load.go +++ b/internal/config/load.go @@ -7,6 +7,8 @@ import ( "os" ) +const cfgEnv = "EASY_GATE_CONFIG" + // Load - Load configuration from environment or file func Load(filePath string) (*Config, string, error) { envCfg := os.Getenv(cfgEnv) diff --git a/internal/config/path.go b/internal/config/path.go index 0873484..1933471 100644 --- a/internal/config/path.go +++ b/internal/config/path.go @@ -2,9 +2,22 @@ package config import ( "fmt" + "net/url" "os" ) +const ( + cfgPathEnv = "EASY_GATE_CONFIG_PATH" + rootPathEnv = "EASY_GATE_ROOT_PATH" +) + +// GetRootPath - Get root directory path from env +// or return the default value "/" +func GetRootPath() string { + rootPath := os.Getenv(rootPathEnv) + return JoinUrlPath("/", rootPath) +} + // GetConfigPath - Get the path to the configuration file func GetConfigPath(args []string) (string, error) { cfgFilePath := os.Getenv(cfgPathEnv) @@ -18,3 +31,12 @@ func GetConfigPath(args []string) (string, error) { return args[1], nil } + +// JoinUrlPath - Wrapper around url.JoinPath +func JoinUrlPath(base string, elem ...string) string { + path, err := url.JoinPath(base, elem...) + if err != nil { + url.JoinPath("/", elem...) + } + return path +} diff --git a/internal/engine/engine.go b/internal/engine/engine.go index e501fe4..a6a858d 100644 --- a/internal/engine/engine.go +++ b/internal/engine/engine.go @@ -8,6 +8,7 @@ import ( "github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2/middleware/logger" "github.com/gofiber/template/html/v2" + "github.com/r7wx/easy-gate/internal/config" "github.com/r7wx/easy-gate/internal/engine/static" "github.com/r7wx/easy-gate/internal/engine/template" "github.com/r7wx/easy-gate/internal/routine" @@ -39,7 +40,9 @@ func (e Engine) Serve() { TimeFormat: "2006/01/02 15:04:05", })) - app.Get("/favicon.ico", func(c *fiber.Ctx) error { + rootPath := config.GetRootPath() + + app.Get(config.JoinUrlPath(rootPath, "favicon.ico"), func(c *fiber.Ctx) error { data, err := static.StaticFS.ReadFile("public/favicon.ico") if err != nil { return c.SendStatus(http.StatusNotFound) @@ -49,7 +52,7 @@ func (e Engine) Serve() { return c.Send(data) }) - app.Get("/roboto-regular.ttf", func(c *fiber.Ctx) error { + app.Get(config.JoinUrlPath(rootPath, "roboto-regular.ttf"), func(c *fiber.Ctx) error { data, err := static.StaticFS.ReadFile("public/font/roboto-regular.ttf") if err != nil { return c.SendStatus(http.StatusNotFound) @@ -59,7 +62,7 @@ func (e Engine) Serve() { return c.Send(data) }) - app.Get("/style.css", func(c *fiber.Ctx) error { + app.Get(config.JoinUrlPath(rootPath, "style.css"), func(c *fiber.Ctx) error { status, err := e.Routine.GetStatus() if err != nil { c.Status(http.StatusInternalServerError) @@ -75,10 +78,11 @@ func (e Engine) Serve() { return tmpl.Execute(c, fiber.Map{ "Background": status.Theme.Background, "Foreground": status.Theme.Foreground, + "FontURL": config.JoinUrlPath(rootPath, "roboto-regular.ttf"), }) }) - app.Get("/", func(c *fiber.Ctx) error { + app.Get(rootPath, func(c *fiber.Ctx) error { status, err := e.Routine.GetStatus() if err != nil { c.Status(http.StatusInternalServerError) @@ -89,13 +93,15 @@ func (e Engine) Serve() { data := getData(status, addr) return c.Render("views/index", fiber.Map{ - "Title": status.Title, - "Data": data, + "Title": status.Title, + "Data": data, + "FaviconPath": config.JoinUrlPath(rootPath, "favicon.ico"), + "StylePath": config.JoinUrlPath(rootPath, "style.css"), }) }) app.Use(func(c *fiber.Ctx) error { - return c.Redirect("/") + return c.Redirect(rootPath) }) if status.UseTLS { diff --git a/internal/engine/static/public/styles/style.css b/internal/engine/static/public/styles/style.css index cbab309..ff3bd8f 100644 --- a/internal/engine/static/public/styles/style.css +++ b/internal/engine/static/public/styles/style.css @@ -1,122 +1,122 @@ @font-face { - font-family: 'Roboto'; - src: url('/roboto-regular.ttf') format('truetype'); - } - - body { - background-color: {{.Background}}; - color: {{.Foreground}}; - font-family: 'Roboto', - sans-serif; - } - - a { - text-decoration: none; - color: inherit; - } - - .main { - padding-top: 1.5rem; - padding-bottom: 1.5rem; - padding-left: 3rem; - padding-right: 3rem; - } - - .category-title { - margin-left: 0.25rem; - } - - .category-block { - display: grid; - grid-template-columns: repeat(1, minmax(0, 1fr)); - margin-top: 0.5rem; - margin-bottom: 0.5rem; - } - - .service-block { - padding: 0.3rem; - border-radius: 0.5rem; - box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.1); - cursor: pointer; - transition: box-shadow 0.2s ease; - margin: 0.25rem; - } - - .service-block:hover { - box-shadow: 0 0.25rem 0.5rem rgba(0, 0, 0, 0.2); - } - - .service-icon { - fill: {{.Foreground}}; - margin: auto; - } - - .service-title { - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - font-weight: 600; - width: 83.33%; - } - - .notes-block { - margin-top: 3rem; - margin-bottom: 0.5rem; - display: grid; - grid-template-columns: repeat(1, minmax(0, 1fr)); - } - - .note-block { - padding: 1rem; - border-radius: 0.25rem; - box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.1); - margin: 0.25rem; - } - - .note-icon { - fill: {{.Foreground}}; - margin-right: 0.5rem; - } - - .note-title { - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - font-weight: 600; - width: 83.33%; - } - - .flex-center { - align-items: center; - display: flex; - } - - @media (min-width: 768px) { - .category-block { - grid-template-columns: repeat(2, minmax(0, 1fr)); - } - - .notes-block { - grid-template-columns: repeat(2, minmax(0, 1fr)); - } - } - - @media (min-width: 1024px) { - .category-block { - grid-template-columns: repeat(4, minmax(0, 1fr)); - } - - .notes-block { - grid-template-columns: repeat(2, minmax(0, 1fr)); - } - } - - @media (min-width: 1280px) { - .category-block { - grid-template-columns: repeat(7, minmax(0, 1fr)); - } - - .notes-block { - grid-template-columns: repeat(4, minmax(0, 1fr)); - } - } \ No newline at end of file + font-family: 'Roboto'; + src: url({{.FontURL}}) format('truetype'); +} + +body { + background-color: {{.Background}}; + color: {{.Foreground}}; + font-family: 'Roboto', + sans-serif; +} + +a { + text-decoration: none; + color: inherit; +} + +.main { + padding-top: 1.5rem; + padding-bottom: 1.5rem; + padding-left: 3rem; + padding-right: 3rem; +} + +.category-title { + margin-left: 0.25rem; +} + +.category-block { + display: grid; + grid-template-columns: repeat(1, minmax(0, 1fr)); + margin-top: 0.5rem; + margin-bottom: 0.5rem; +} + +.service-block { + padding: 0.3rem; + border-radius: 0.5rem; + box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.1); + cursor: pointer; + transition: box-shadow 0.2s ease; + margin: 0.25rem; +} + +.service-block:hover { + box-shadow: 0 0.25rem 0.5rem rgba(0, 0, 0, 0.2); +} + +.service-icon { + fill: {{.Foreground}}; + margin: auto; +} + +.service-title { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + font-weight: 600; + width: 83.33%; +} + +.notes-block { + margin-top: 3rem; + margin-bottom: 0.5rem; + display: grid; + grid-template-columns: repeat(1, minmax(0, 1fr)); +} + +.note-block { + padding: 1rem; + border-radius: 0.25rem; + box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.1); + margin: 0.25rem; +} + +.note-icon { + fill: {{.Foreground}}; + margin-right: 0.5rem; +} + +.note-title { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + font-weight: 600; + width: 83.33%; +} + +.flex-center { + align-items: center; + display: flex; +} + +@media (min-width: 768px) { + .category-block { + grid-template-columns: repeat(2, minmax(0, 1fr)); + } + + .notes-block { + grid-template-columns: repeat(2, minmax(0, 1fr)); + } +} + +@media (min-width: 1024px) { + .category-block { + grid-template-columns: repeat(4, minmax(0, 1fr)); + } + + .notes-block { + grid-template-columns: repeat(2, minmax(0, 1fr)); + } +} + +@media (min-width: 1280px) { + .category-block { + grid-template-columns: repeat(7, minmax(0, 1fr)); + } + + .notes-block { + grid-template-columns: repeat(4, minmax(0, 1fr)); + } +} \ No newline at end of file diff --git a/internal/engine/template/views/index.html b/internal/engine/template/views/index.html index 63dbf6d..b8c5f06 100644 --- a/internal/engine/template/views/index.html +++ b/internal/engine/template/views/index.html @@ -1,62 +1,75 @@ +
+ + +{{$service.Name}}
+{{$note.Text}}
{{$note.Name}}
-{{$note.Text}}
+ {{end}}