summaryrefslogtreecommitdiff
path: root/internal
diff options
context:
space:
mode:
authormihna123 <mihailonvojinovic@gmail.com>2026-09-04 01:13:17 +0200
committermihna123 <mihailonvojinovic@gmail.com>2026-09-04 01:13:17 +0200
commit5907450d569c997809674a2abeec97ddf0afe4aa (patch)
tree7bd5af677b14433b5c18879e08bb672c87e259e1 /internal
parenta57f4fcbcd7857286cff854f6d62285df7d098aa (diff)
downloadopenstore-5907450d569c997809674a2abeec97ddf0afe4aa.tar.gz
openstore-5907450d569c997809674a2abeec97ddf0afe4aa.zip
config: add config package and wire it into server and handlers
Replace the hardcoded server.Config with an ini-backed config package that reads /etc/openstore/openstore.conf then the user's config file, so user settings override system ones and both override the built-in defaults. Missing files are ignored rather than fatal. Move route registration into a handlers package that owns the templates and builds a Page view model, which removes the server -> handlers import cycle along with the setter-based context injection.
Diffstat (limited to 'internal')
-rw-r--r--internal/config/config.go73
-rw-r--r--internal/server/handlers/handlers.go25
-rw-r--r--internal/server/handlers/pages.go24
-rw-r--r--internal/server/server.go36
-rw-r--r--internal/server/server_test.go22
-rw-r--r--internal/web/templates/index.html6
6 files changed, 155 insertions, 31 deletions
diff --git a/internal/config/config.go b/internal/config/config.go
new file mode 100644
index 0000000..2be9dd6
--- /dev/null
+++ b/internal/config/config.go
@@ -0,0 +1,73 @@
+package config
+
+import (
+ "fmt"
+ "net/url"
+ "os"
+ "path/filepath"
+
+ "gopkg.in/ini.v1"
+)
+
+type Serve struct {
+ Addr string `ini:"addr"`
+}
+
+type Instance struct {
+ Name string `ini:"name"`
+ ContactEmail string `ini:"contact-email"`
+ BaseURL string `ini:"base-url"`
+}
+
+type Config struct {
+ Serve Serve `ini:"serve"`
+ Instance Instance `ini:"instance"`
+}
+
+var systemConfigPath = "/etc/openstore/openstore.conf"
+
+func DefaultPath() (string, error) {
+ dir, err := os.UserConfigDir()
+ if err != nil {
+ return "", err
+ }
+
+ return filepath.Join(dir, "openstore", "openstore.conf"), nil
+}
+
+func Load(filename string) (*Config, error) {
+ cfg, err := ini.LoadSources(ini.LoadOptions{
+ Loose: true,
+ KeyValueDelimiters: "=",
+ }, systemConfigPath, filename)
+
+ if err != nil {
+ return nil, err
+ }
+
+ config := &Config{
+ Serve: Serve{
+ Addr: ":3000",
+ },
+ Instance: Instance{
+ Name: "Openstore",
+ ContactEmail: "jane@doe.com",
+ BaseURL: "https://www.example.com",
+ },
+ }
+
+ err = cfg.MapTo(config)
+ if err != nil {
+ return nil, err
+ }
+
+ u, err := url.Parse(config.Instance.BaseURL)
+ if err != nil || u.Host == "" || u.Scheme == "" {
+ return nil, fmt.Errorf(
+ "config: base-url must be an absolute url, got %s",
+ config.Instance.BaseURL,
+ )
+ }
+
+ return config, nil
+}
diff --git a/internal/server/handlers/handlers.go b/internal/server/handlers/handlers.go
new file mode 100644
index 0000000..958ca30
--- /dev/null
+++ b/internal/server/handlers/handlers.go
@@ -0,0 +1,25 @@
+package handlers
+
+import (
+ "html/template"
+ "net/http"
+
+ "codeberg.org/mihna123/openstore/internal/config"
+ "codeberg.org/mihna123/openstore/internal/web"
+)
+
+type Handler struct {
+ tmpl *template.Template
+ cfg *config.Config
+}
+
+func Routes(conf *config.Config) *http.ServeMux {
+ handler := Handler{
+ tmpl: template.Must(template.ParseFS(web.Files, "templates/*.html")),
+ cfg: conf,
+ }
+ mux := http.NewServeMux()
+ mux.HandleFunc("GET /{$}", handler.home)
+
+ return mux
+}
diff --git a/internal/server/handlers/pages.go b/internal/server/handlers/pages.go
new file mode 100644
index 0000000..8c5bfb5
--- /dev/null
+++ b/internal/server/handlers/pages.go
@@ -0,0 +1,24 @@
+package handlers
+
+import (
+ "log/slog"
+ "net/http"
+
+ "codeberg.org/mihna123/openstore/internal/config"
+)
+
+type Page struct {
+ Lang string
+ Instance config.Instance
+ Data any
+}
+
+func (h *Handler) home(w http.ResponseWriter, r *http.Request) {
+ page := Page{Lang: "en", Instance: h.cfg.Instance}
+ err := h.tmpl.ExecuteTemplate(w, "index.html", page)
+
+ if err != nil {
+ slog.Error("render template", "err", err.Error())
+ return
+ }
+}
diff --git a/internal/server/server.go b/internal/server/server.go
index a444958..a2f2d45 100644
--- a/internal/server/server.go
+++ b/internal/server/server.go
@@ -5,55 +5,39 @@ import (
"net"
"time"
- "html/template"
"net/http"
- "codeberg.org/mihna123/openstore/internal/web"
+ "codeberg.org/mihna123/openstore/internal/config"
+ "codeberg.org/mihna123/openstore/internal/server/handlers"
)
-type Config struct {
- Name string
-}
-
type Server struct {
- config *Config
- tmpl *template.Template
+ config *config.Config
mux *http.ServeMux
}
-func (s *Server) routes() {
- s.mux.HandleFunc("GET /{$}", func(w http.ResponseWriter, r *http.Request) {
- err := s.tmpl.ExecuteTemplate(w, "index.html", s.config)
-
- if err != nil {
- slog.Error("render template", "err", err.Error())
- return
- }
- })
-}
-
-func (s *Server) Serve(addr string) error {
+func (s *Server) Serve() error {
httpSrv := &http.Server{
Handler: logRequests(s.mux),
ReadHeaderTimeout: 5 * time.Second,
WriteTimeout: 10 * time.Second,
}
- ln, err := net.Listen("tcp", addr)
+ ln, err := net.Listen("tcp", s.config.Serve.Addr)
if err != nil {
return err
}
- slog.Info("listening", "name", s.config.Name, "addr", addr)
+ slog.Info("listening", "name", s.config.Instance.Name, "addr", s.config.Serve.Addr)
return httpSrv.Serve(ln)
}
-func New(c *Config) *Server {
+func New(c *config.Config) *Server {
+ mux := handlers.Routes(c)
s := &Server{
config: c,
- mux: http.NewServeMux(),
- tmpl: template.Must(template.ParseFS(web.Files, "templates/*.html")),
+ mux: mux,
}
- s.routes()
+
return s
}
diff --git a/internal/server/server_test.go b/internal/server/server_test.go
index 7e6a748..aea434f 100644
--- a/internal/server/server_test.go
+++ b/internal/server/server_test.go
@@ -7,6 +7,8 @@ import (
"os"
"strings"
"testing"
+
+ "codeberg.org/mihna123/openstore/internal/config"
)
func TestMain(m *testing.M) {
@@ -14,9 +16,23 @@ func TestMain(m *testing.M) {
os.Exit(m.Run())
}
+func newTestConfig(t *testing.T) *config.Config {
+ t.Helper()
+ return &config.Config{
+ Serve: config.Serve{
+ Addr: ":3000",
+ },
+ Instance: config.Instance{
+ Name: "TestStore",
+ ContactEmail: "test@email.com",
+ BaseURL: "https://example.com",
+ },
+ }
+}
+
func newTestServer(t *testing.T) *Server {
t.Helper()
- return New(&Config{Name: "test"})
+ return New(newTestConfig(t))
}
func TestRoutes(t *testing.T) {
@@ -47,7 +63,9 @@ func TestRoutes(t *testing.T) {
}
func TestIndexRendersTemplate(t *testing.T) {
- s := New(&Config{Name: "testname123"})
+ cfg := newTestConfig(t)
+ cfg.Instance.Name = "testname123"
+ s := New(cfg)
rec := httptest.NewRecorder()
s.mux.ServeHTTP(rec, httptest.NewRequest(http.MethodGet, "/", nil))
diff --git a/internal/web/templates/index.html b/internal/web/templates/index.html
index 54630e7..16ca1fe 100644
--- a/internal/web/templates/index.html
+++ b/internal/web/templates/index.html
@@ -3,11 +3,11 @@
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
- <title>{{ .Name }}</title>
- <link href="css/style.css" rel="stylesheet" />
+ <title>{{ .Instance.Name }}</title>
+ <link href="/css/style.css" rel="stylesheet" />
</head>
<body>
- <h1>Welcome to {{ .Name }}!</h1>
+ <h1>Welcome to {{ .Instance.Name }}!</h1>
<p>Work in progress...</p>
</body>
</html>