// Package block provides functionality to load HTML blocks with associated content, JavaScript, and CSS from the filesystem. // API Endpoint: // // /api/block/{blockPath} // // Example: // // /api/block/header would load the block located at {BlockDir}/header/ package block import ( "encoding/json" "log/slog" "net/http" "os" "path/filepath" "git.oblat.lv/alex/triggerssmith/internal/config" "github.com/go-chi/chi/v5" ) type Block struct { Content string `json:"content"` JS string `json:"js"` CSS string `json:"css"` } type blockHandler struct { cfg *config.Config } func Route(config *config.Config) func(chi.Router) { h := &blockHandler{ cfg: config, } return func(r chi.Router) { r.Get("/*", h.handleBlock) } } func (h *blockHandler) handleBlock(w http.ResponseWriter, r *http.Request) { if !h.cfg.Server.BlockConfig.Enabled { http.Error(w, "Block serving is disabled", http.StatusForbidden) return } blockPath := r.URL.Path[len("/api/block/"):] block, err := LoadBlock(blockPath, h.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) }