From cda0edfde2d0df595db9dd3f31beb977a031def8 Mon Sep 17 00:00:00 2001 From: Alexey Date: Wed, 17 Dec 2025 11:09:54 +0200 Subject: [PATCH] block logic --- api/block/handle.go | 77 +++++++++++++++++++++++++++++++++++++++ api/router.go | 33 ++++++++++++++--- cmd/serve.go | 4 +- internal/config/config.go | 34 +++++++++++++---- 4 files changed, 133 insertions(+), 15 deletions(-) create mode 100644 api/block/handle.go diff --git a/api/block/handle.go b/api/block/handle.go new file mode 100644 index 0000000..332d01d --- /dev/null +++ b/api/block/handle.go @@ -0,0 +1,77 @@ +package block + +import ( + "encoding/json" + "log/slog" + "net/http" + "os" + "path/filepath" + + "git.oblat.lv/alex/triggerssmith/internal/config" +) + +type Block struct { + Content string `json:"content"` + JS string `json:"js"` + CSS string `json:"css"` +} + +func LoadHtmlBlock(cfg *config.Config) func(w http.ResponseWriter, r *http.Request) { + return func(w http.ResponseWriter, r *http.Request) { + if !cfg.Server.BlockConfig.Enabled { + http.Error(w, "Block serving is disabled", http.StatusNotImplemented) + return + } + + blockPath := r.URL.Path[len("/api/block/"):] + block, err := LoadBlock(blockPath, cfg) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + w.Write([]byte(block.ToJSON())) + } +} + +// LoadBlock loads a block from the filesystem given its path and configuration. +// It reads the content, JavaScript, and CSS files associated with the block. +// If err is not nil, it indicates a failure in reading the block files. +func LoadBlock(path string, cfg *config.Config) (*Block, error) { + slog.Debug("loading block", slog.String("path", path)) + path = filepath.Join(cfg.Server.BlockConfig.BlockDir, path) + var block Block + var err error + contentPath := filepath.Join(path, "content.md") + jsPath := filepath.Join(path, "script.js") + cssPath := filepath.Join(path, "style.css") + if b, err := os.ReadFile(contentPath); err == nil { + block.Content = string(b) + } else { + slog.Warn("failed to read block content", slog.String("path", contentPath), slog.String("err", err.Error())) + } + + if b, err := os.ReadFile(jsPath); err == nil { + block.JS = string(b) + } else { + slog.Warn("failed to read block JS", slog.String("path", contentPath), slog.String("err", err.Error())) + } + + if b, err := os.ReadFile(cssPath); err == nil { + block.CSS = string(b) + } else { + slog.Warn("failed to read block CSS", slog.String("path", contentPath), slog.String("err", err.Error())) + } + + return &block, err +} + +func (b *Block) ToJSON() string { + jsonData, err := json.Marshal(b) + if err != nil { + slog.Error("failed to marshal block to JSON", slog.String("err", err.Error())) + return "{}" + } + return string(jsonData) +} diff --git a/api/router.go b/api/router.go index 6acc98a..e1d4473 100644 --- a/api/router.go +++ b/api/router.go @@ -2,14 +2,16 @@ package api import ( "encoding/json" + "log/slog" "net/http" "path/filepath" "time" - "git.oblat.lv/alex/triggerssmith/api/invoke" + "git.oblat.lv/alex/triggerssmith/api/block" "git.oblat.lv/alex/triggerssmith/internal/config" "git.oblat.lv/alex/triggerssmith/internal/vars" "github.com/go-chi/chi/v5" + "github.com/go-chi/chi/v5/middleware" ) type Router struct { @@ -26,12 +28,31 @@ func NewRouter(cfg *config.Config) *Router { } } +// RouteHandler sets up the routes and middleware for the router. +// TODO: implement hot reload for static files enabled/disabled func (r *Router) RouteHandler() chi.Router { - r.r.Get("/", func(w http.ResponseWriter, req *http.Request) { - http.ServeFile(w, req, filepath.Join(r.cfg.Server.StaticFilesPath, "index.html")) + r.r.Use(middleware.Logger) + r.r.Use(middleware.Recoverer) + r.r.Use(middleware.Timeout(r.cfg.Server.TimeoutSeconds)) + + if r.cfg.Server.StaticConfig.Enabled { + slog.Debug("Static file serving is enabled", + slog.String("dir", r.cfg.Server.StaticConfig.Dir), + slog.String("index_file", r.cfg.Server.StaticConfig.IndexFile), + ) + r.r.Get("/", func(w http.ResponseWriter, req *http.Request) { + http.ServeFile(w, req, filepath.Join(r.cfg.Server.StaticConfig.Dir, r.cfg.Server.StaticConfig.IndexFile)) + }) + fs := http.FileServer(http.Dir(r.cfg.Server.StaticConfig.Dir)) + r.r.Handle("/static/*", http.StripPrefix("/static/", fs)) + } else { + slog.Info("Static file serving is disabled") + } + + r.r.Route("/api", func(api chi.Router) { + api.Get("/block/*", block.LoadHtmlBlock(r.cfg)) }) - fs := http.FileServer(http.Dir("static")) - r.r.Handle("/static/*", http.StripPrefix("/static/", fs)) + r.r.Get("/health", func(w http.ResponseWriter, r *http.Request) { b, _ := json.Marshal(struct { Status string `json:"status"` @@ -42,6 +63,6 @@ func (r *Router) RouteHandler() chi.Router { }) w.Write([]byte(b)) }) - r.r.Handle("/invoke/function/{function_id}/{function_version}", invoke.InvokeHandler(r.cfg)) + //r.r.Handle("/invoke/function/{function_id}/{function_version}", invoke.InvokeHandler(r.cfg)) return r.r } diff --git a/cmd/serve.go b/cmd/serve.go index cbef70c..2932017 100644 --- a/cmd/serve.go +++ b/cmd/serve.go @@ -18,8 +18,8 @@ import ( ) var optsServeCmd = struct { - ConfigPath *string - Debug *bool + ConfigPath *string + Debug *bool HideGreetings *bool }{} diff --git a/internal/config/config.go b/internal/config/config.go index e77a0bd..738a3d8 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -2,15 +2,29 @@ package config import ( "sync/atomic" + "time" "github.com/akyaiy/GSfass/core/config" ) +type StaticConfig struct { + Enabled bool `mapstructure:"enabled"` + Dir string `mapstructure:"static_dir"` + IndexFile string `mapstructure:"index_file"` +} + +type BlockConfig struct { + Enabled bool `mapstructure:"enabled"` + BlockDir string `mapstructure:"block_dir"` +} + type ServerConfig struct { - Port int `mapstructure:"port"` - Addr string `mapstructure:"address"` - StaticFilesPath string `mapstructure:"static_dir"` - LogPath string `mapstructure:"log_path"` + StaticConfig StaticConfig `mapstructure:"static"` + BlockConfig BlockConfig `mapstructure:"block"` + Port int `mapstructure:"port"` + Addr string `mapstructure:"address"` + LogPath string `mapstructure:"log_path"` + TimeoutSeconds time.Duration `mapstructure:"timeout_seconds"` } type FuncConfig struct { @@ -24,9 +38,15 @@ type Config struct { var configPath atomic.Value // string var defaults = map[string]any{ - "server.port": 8080, - "server.address": "127.0.0.0", - "server.static_dir": "./static", + "server.port": 8080, + "server.address": "127.0.0.0", + "server.timeout_seconds": 5, + "server.log_path": "./logs/server.log", + "server.static.enabled": true, + "server.static.static_dir": "./static", + "server.static.index_file": "index.html", + "server.block.enabled": true, + "server.block.block_dir": "./blocks", "functions.func_dir": "./functions", }