From 66f3d124128d7ae0d32ae6fd4a1d126732c13953 Mon Sep 17 00:00:00 2001 From: alex Date: Sat, 5 Jul 2025 22:13:16 +0300 Subject: [PATCH] Implement automatic update functionality and improve server initialization; add NODE_PATH to Makefile, enhance logging, and update README --- Makefile | 6 ++ README.md | 2 +- cmd/node/node.go | 44 ++++++++++--- config.yaml | 9 +-- core/config/config.go | 12 ++-- core/config/consts.go | 14 ++++- core/init/init.go | 45 ++++++++++++++ core/sv1/handle_com.go | 34 +++++----- core/sv1/handle_list.go | 16 ++--- core/sv1/server.go | 4 -- core/sv1/utils.go | 19 +++--- core/update/update.go | 135 ++++++++++++++++++++++++++++++++++++++-- go.mod | 1 + go.sum | 2 + 14 files changed, 271 insertions(+), 72 deletions(-) create mode 100644 core/init/init.go diff --git a/Makefile b/Makefile index bc35297..82ebbb6 100644 --- a/Makefile +++ b/Makefile @@ -2,6 +2,8 @@ APP_NAME := node BIN_DIR := bin GOPATH := $(shell go env GOPATH) export CONFIG_PATH := ./config.yaml +export NODE_PATH := $(shell pwd) + LDFLAGS := -X 'github.com/akyaiy/GoSally-mvp/core/config.NodeVersion=v0.0.1-dev' CGO_CFLAGS := -I/usr/local/include CGO_LDFLAGS := -L/usr/local/lib -llua5.1 -lm -ldl @@ -38,6 +40,10 @@ runq: build @echo "Running!" ./$(BIN_DIR)/$(APP_NAME) | jq +pure-run: + @echo "Running!" + ./$(BIN_DIR)/$(APP_NAME) | jq + test: @go test ./... | grep -v '^?' || true diff --git a/README.md b/README.md index 55a78ff..468f2cd 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ System that allows you to build your own infrastructure based on identical nodes and various scripts written using built-in Lua 5.1, shebang scripts (scripts that start with the `#!` symbols), compiled binaries. ### Features -Go Sally is not viable at the moment, but it already has the ability to run embedded scripts, log slog events to stdout, and handle RPC like requests. +Go Sally is not viable at the moment, but it already has the ability to run embedded scripts, log slog events to stdout, handle RPC like requests, and independent automatic update from the repository (my pride, to be honest). ### Example of use The basic directory tree looks something like this diff --git a/cmd/node/node.go b/cmd/node/node.go index 5cbd8ba..cc99c0e 100644 --- a/cmd/node/node.go +++ b/cmd/node/node.go @@ -1,19 +1,21 @@ package main import ( - "fmt" "log/slog" "net" "net/http" "regexp" + "time" "golang.org/x/net/netutil" "github.com/akyaiy/GoSally-mvp/core/config" gs "github.com/akyaiy/GoSally-mvp/core/general_server" + _ "github.com/akyaiy/GoSally-mvp/core/init" "github.com/akyaiy/GoSally-mvp/core/logs" "github.com/akyaiy/GoSally-mvp/core/sv1" "github.com/akyaiy/GoSally-mvp/core/update" + "github.com/go-chi/cors" "github.com/go-chi/chi/v5" ) @@ -27,20 +29,37 @@ func init() { log = logs.SetupLogger(cfg.Mode) log = log.With("mode", cfg.Mode) - log.Info("Initializing server", slog.String("address", cfg.HTTPServer.Address)) + currentV, currentB, _ := update.NewUpdater(*log, cfg).GetCurrentVersion() + + log.Info("Initializing server", slog.String("address", cfg.HTTPServer.Address), slog.String("version", string(currentV)+"-"+string(currentB))) log.Debug("Server running in debug mode") } +func UpdateDaemon(u *update.Updater, cfg config.ConfigConf) { + for { + isNewUpdate, err := u.CkeckUpdates() + if err != nil { + log.Error("Failed to check for updates", slog.String("error", err.Error())) + } + if isNewUpdate { + log.Info("New update available, starting update process...") + err = u.Update() + if err != nil { + log.Error("Failed to update", slog.String("error", err.Error())) + } else { + log.Info("Update completed successfully") + } + } else { + log.Info("No new updates available") + } + time.Sleep(cfg.CheckInterval) + } +} + func main() { updater := update.NewUpdater(*log, cfg) - versuion, versionType, _ := updater.GetCurrentVersion() - fmt.Printf("Current version: %s (%s)\n", versuion, versionType) - ver, vert, _ := updater.GetLatestVersion(versionType) - fmt.Printf("Latest version: %s (%s)\n", ver, vert) + go UpdateDaemon(updater, *cfg) - fmt.Println("Checking for updates...") - isNewUpdate, _ := updater.CkeckUpdates() - fmt.Println("Update check result:", isNewUpdate) serverv1 := sv1.InitV1Server(&sv1.HandlerV1InitStruct{ Log: *log, Config: cfg, @@ -54,6 +73,13 @@ func main() { }, serverv1) r := chi.NewRouter() + r.Use(cors.Handler(cors.Options{ + AllowedOrigins: []string{"*"}, + AllowedMethods: []string{"GET", "POST", "OPTIONS"}, + AllowedHeaders: []string{"Accept", "Authorization", "Content-Type", "X-CSRF-Token"}, + AllowCredentials: true, + MaxAge: 300, + })) r.Route(config.GetServerConsts().GetApiRoute()+config.GetServerConsts().GetComDirRoute(), func(r chi.Router) { r.Get("/", s.HandleList) r.Get("/{cmd}", s.Handle) diff --git a/config.yaml b/config.yaml index f97aa1a..ca23c71 100644 --- a/config.yaml +++ b/config.yaml @@ -1,7 +1,7 @@ mode: "dev" http_server: - address: "0.0.0.0:8080" + address: "192.168.1.176:8080" timeout: 3s idle_timeout: 30s api: @@ -23,10 +23,5 @@ com_dir: "com/" updates: enabled: true - allow-auto-updates: true - allow-updates: true - allow-downgrades: false - check-interval: 1h - repository_url: "https://repo.serve.lv/raw/go-sally" - wanted-version: "latest-stable" \ No newline at end of file + repository_url: "https://repo.serve.lv/raw/go-sally" \ No newline at end of file diff --git a/core/config/config.go b/core/config/config.go index 0acf0c3..a4d7594 100644 --- a/core/config/config.go +++ b/core/config/config.go @@ -43,18 +43,16 @@ type Internal struct { } type Updates struct { - UpdatesEnabled bool `yaml:"enabled" env-default:"false"` - AllowAutoUpdates bool `yaml:"allow_auto_updates" env-default:"false"` - AllowUpdates bool `yaml:"allow_updates" env-default:"false"` - AllowDowngrades bool `yaml:"allow_downgrades" env-default:"false"` - CheckInterval time.Duration `yaml:"check_interval" env-default:"2h"` - RepositoryURL string `yaml:"repository_url" env-default:""` - WantedVersion string `yaml:"wanted_version" env-default:"latest-stable"` + UpdatesEnabled bool `yaml:"enabled" env-default:"false"` + CheckInterval time.Duration `yaml:"check_interval" env-default:"2h"` + RepositoryURL string `yaml:"repository_url" env-default:""` + WantedVersion string `yaml:"wanted_version" env-default:"latest-stable"` } // ConfigEnv structure for environment variables type ConfigEnv struct { ConfigPath string `env:"CONFIG_PATH" env-default:"./cfg/config.yaml"` + NodePath string `env:"NODE_PATH" env-default:"./"` } // MustLoadConfig loads the configuration from the specified path and environment variables. diff --git a/core/config/consts.go b/core/config/consts.go index 8685719..4d0a67e 100644 --- a/core/config/consts.go +++ b/core/config/consts.go @@ -1,7 +1,9 @@ package config +import "os" + // UUIDLength is uuids length for sessions. By default it is 16 bytes. -var UUIDLength byte = 4 +var UUIDLength byte = 16 // ApiRoute setting for go-chi for main route for api requests var ApiRoute string = "/api/{ver}" @@ -17,6 +19,12 @@ var NodeVersion string // In the repository, the file specified in the variable contains the current information about updates var ActualFileName string = "actual.txt" +// UpdateArchiveName is the name of the archive that will be used for updates. +var UpdateArchiveName string = "gosally-node" + +// UpdateInstallPath is the path where the update will be installed. +var UpdateDownloadPath string = os.TempDir() + type _internalConsts struct{} type _serverConsts struct{} type _updateConsts struct{} @@ -28,7 +36,9 @@ func (_ _updateConsts) GetNodeVersion() string { } return NodeVersion } -func (_ _updateConsts) GetActualFileName() string { return ActualFileName } +func (_ _updateConsts) GetActualFileName() string { return ActualFileName } +func (_ _updateConsts) GetUpdateArchiveName() string { return UpdateArchiveName } +func (_ _updateConsts) GetUpdateDownloadPath() string { return UpdateDownloadPath } func GetInternalConsts() _internalConsts { return _internalConsts{} } func (_ _internalConsts) GetUUIDLength() byte { return UUIDLength } diff --git a/core/init/init.go b/core/init/init.go new file mode 100644 index 0000000..20ebf79 --- /dev/null +++ b/core/init/init.go @@ -0,0 +1,45 @@ +package init + +import ( + "io" + "log" + "os" + "path/filepath" + "strings" + "syscall" +) + +func init() { + if strings.HasPrefix(os.Args[0], "/tmp") { + return + } + runPath, err := os.MkdirTemp("", "*-gs-runtime") + log.SetOutput(os.Stderr) + input, err := os.Open(os.Args[0]) + if err != nil { + log.Fatalf("Failed to init node: %s", err) + } + + runBinaryPath := filepath.Join(runPath, "node") + output, err := os.Create(runBinaryPath) + if err != nil { + log.Fatalf("Failed to init node: %s", err) + } + + if _, err := io.Copy(output, input); err != nil { + log.Fatalf("Failed to init node: %s", err) + } + + // Делаем исполняемым (на всякий случай) + if err := os.Chmod(runBinaryPath, 0755); err != nil { + log.Fatalf("Failed to init node: %s", err) + } + + input.Close() + output.Close() + runArgs := os.Args + runArgs[0] = runBinaryPath + if err := syscall.Exec(runBinaryPath, runArgs, append(os.Environ(), "GS_RUNTIME_PATH=" + runPath)); err != nil { + log.Fatalf("Failed to init node: %s", err) + } +} diff --git a/core/sv1/handle_com.go b/core/sv1/handle_com.go index 4eb68ae..ef664bc 100644 --- a/core/sv1/handle_com.go +++ b/core/sv1/handle_com.go @@ -13,46 +13,46 @@ import ( ) // HandlerV1 is the main handler for version 1 of the API. -// The function processes the HTTP request and runs Lua scripts, +// The function processes the HTTP request and runs Lua scripts, // preparing the environment and subsequently transmitting the execution result func (h *HandlerV1) Handle(w http.ResponseWriter, r *http.Request) { uuid16, err := utils.NewUUID() if err != nil { h.log.Error("Failed to generate UUID", slog.String("error", err.Error())) - utils.WriteJSONError(h.w, http.StatusInternalServerError, "failed to generate UUID: "+err.Error()) + utils.WriteJSONError(w, http.StatusInternalServerError, "failed to generate UUID: "+err.Error()) return } log := h.log.With( slog.Group("request", slog.String("version", h.GetVersion()), - slog.String("url", h.r.URL.String()), - slog.String("method", h.r.Method), + slog.String("url", r.URL.String()), + slog.String("method", r.Method), ), slog.Group("connection", slog.String("connection-uuid", uuid16), - slog.String("remote", h.r.RemoteAddr), + slog.String("remote", r.RemoteAddr), ), ) log.Info("Received request") - cmd := chi.URLParam(h.r, "cmd") + cmd := chi.URLParam(r, "cmd") if !h.allowedCmd.MatchString(string([]rune(cmd)[0])) || !h.listAllowedCmd.MatchString(cmd) { log.Error("HTTP request error", slog.String("error", "invalid command"), slog.String("cmd", cmd), slog.Int("status", http.StatusBadRequest)) - utils.WriteJSONError(h.w, http.StatusBadRequest, "invalid command") + utils.WriteJSONError(w, http.StatusBadRequest, "invalid command") return } - scriptPath := h.comMatch(chi.URLParam(h.r, "ver"), cmd) + scriptPath := h.comMatch(chi.URLParam(r, "ver"), cmd) if scriptPath == "" { log.Error("HTTP request error", slog.String("error", "command not found"), slog.String("cmd", cmd), slog.Int("status", http.StatusNotFound)) - utils.WriteJSONError(h.w, http.StatusNotFound, "command not found") + utils.WriteJSONError(w, http.StatusNotFound, "command not found") return } @@ -62,7 +62,7 @@ func (h *HandlerV1) Handle(w http.ResponseWriter, r *http.Request) { slog.String("error", "command not found"), slog.String("cmd", cmd), slog.Int("status", http.StatusNotFound)) - utils.WriteJSONError(h.w, http.StatusNotFound, "command not found") + utils.WriteJSONError(w, http.StatusNotFound, "command not found") return } @@ -70,7 +70,7 @@ func (h *HandlerV1) Handle(w http.ResponseWriter, r *http.Request) { defer L.Close() paramsTable := L.NewTable() - qt := h.r.URL.Query() + qt := r.URL.Query() for k, v := range qt { if len(v) > 0 { L.SetField(paramsTable, k, lua.LString(v[0])) @@ -91,7 +91,7 @@ func (h *HandlerV1) Handle(w http.ResponseWriter, r *http.Request) { if err := L.DoFile(prepareLuaEnv); err != nil { log.Error("Failed to prepare lua environment", slog.String("error", err.Error())) - utils.WriteJSONError(h.w, http.StatusInternalServerError, "lua error: "+err.Error()) + utils.WriteJSONError(w, http.StatusInternalServerError, "lua error: "+err.Error()) return } } else { @@ -101,7 +101,7 @@ func (h *HandlerV1) Handle(w http.ResponseWriter, r *http.Request) { if err := L.DoFile(scriptPath); err != nil { log.Error("Failed to execute lua script", slog.String("error", err.Error())) - utils.WriteJSONError(h.w, http.StatusInternalServerError, "lua error: "+err.Error()) + utils.WriteJSONError(w, http.StatusInternalServerError, "lua error: "+err.Error()) return } @@ -109,7 +109,7 @@ func (h *HandlerV1) Handle(w http.ResponseWriter, r *http.Request) { tbl, ok := lv.(*lua.LTable) if !ok { log.Error("Lua global 'Out' is not a table") - utils.WriteJSONError(h.w, http.StatusInternalServerError, "'Out' is not a table") + utils.WriteJSONError(w, http.StatusInternalServerError, "'Out' is not a table") return } @@ -117,7 +117,7 @@ func (h *HandlerV1) Handle(w http.ResponseWriter, r *http.Request) { resultTbl, ok := resultVal.(*lua.LTable) if !ok { log.Error("Lua global 'Result' is not a table") - utils.WriteJSONError(h.w, http.StatusInternalServerError, "'Result' is not a table") + utils.WriteJSONError(w, http.StatusInternalServerError, "'Result' is not a table") return } @@ -126,8 +126,8 @@ func (h *HandlerV1) Handle(w http.ResponseWriter, r *http.Request) { out[key.String()] = utils.ConvertLuaTypesToGolang(value) }) - h.w.Header().Set("Content-Type", "application/json") - if err := json.NewEncoder(h.w).Encode(out); err != nil { + w.Header().Set("Content-Type", "application/json") + if err := json.NewEncoder(w).Encode(out); err != nil { log.Error("Failed to encode JSON response", slog.String("error", err.Error())) } diff --git a/core/sv1/handle_list.go b/core/sv1/handle_list.go index 355e684..be73cca 100644 --- a/core/sv1/handle_list.go +++ b/core/sv1/handle_list.go @@ -18,18 +18,18 @@ func (h *HandlerV1) HandleList(w http.ResponseWriter, r *http.Request) { if err != nil { h.log.Error("Failed to generate UUID", slog.String("error", err.Error())) - utils.WriteJSONError(h.w, http.StatusInternalServerError, "failed to generate UUID: "+err.Error()) + utils.WriteJSONError(w, http.StatusInternalServerError, "failed to generate UUID: "+err.Error()) return } log := h.log.With( slog.Group("request", slog.String("version", h.GetVersion()), - slog.String("url", h.r.URL.String()), - slog.String("method", h.r.Method), + slog.String("url", r.URL.String()), + slog.String("method", r.Method), ), slog.Group("connection", slog.String("connection-uuid", uuid16), - slog.String("remote", h.r.RemoteAddr), + slog.String("remote", r.RemoteAddr), ), ) log.Info("Received request") @@ -46,11 +46,11 @@ func (h *HandlerV1) HandleList(w http.ResponseWriter, r *http.Request) { if files, err = os.ReadDir(h.cfg.ComDir); err != nil { log.Error("Failed to read commands directory", slog.String("error", err.Error())) - utils.WriteJSONError(h.w, http.StatusInternalServerError, "failed to read commands directory: "+err.Error()) + utils.WriteJSONError(w, http.StatusInternalServerError, "failed to read commands directory: "+err.Error()) return } - apiVer := chi.URLParam(h.r, "ver") + apiVer := chi.URLParam(r, "ver") // Сначала ищем версионные for _, file := range files { @@ -110,8 +110,8 @@ func (h *HandlerV1) HandleList(w http.ResponseWriter, r *http.Request) { log.Info("Session completed") - h.w.Header().Set("Content-Type", "application/json") - if err := json.NewEncoder(h.w).Encode(commands); err != nil { + w.Header().Set("Content-Type", "application/json") + if err := json.NewEncoder(w).Encode(commands); err != nil { h.log.Error("Failed to write JSON error response", slog.String("error", err.Error())) } diff --git a/core/sv1/server.go b/core/sv1/server.go index 2e7e1fb..92c543d 100644 --- a/core/sv1/server.go +++ b/core/sv1/server.go @@ -4,7 +4,6 @@ package sv1 import ( "log/slog" - "net/http" "regexp" "github.com/akyaiy/GoSally-mvp/core/config" @@ -21,9 +20,6 @@ type HandlerV1InitStruct struct { // HandlerV1 implements the ServerV1UtilsContract and serves as the main handler for API requests. type HandlerV1 struct { - w http.ResponseWriter - r *http.Request - log slog.Logger cfg *config.ConfigConf diff --git a/core/sv1/utils.go b/core/sv1/utils.go index ff37a2b..877f7e6 100644 --- a/core/sv1/utils.go +++ b/core/sv1/utils.go @@ -2,21 +2,18 @@ package sv1 import ( "log/slog" - "net/http" "os" "regexp" - - "github.com/akyaiy/GoSally-mvp/core/utils" ) -func (h *HandlerV1) errNotFound(w http.ResponseWriter, r *http.Request) { - utils.WriteJSONError(h.w, http.StatusBadRequest, "invalid request") - h.log.Error("HTTP request error", - slog.String("remote", h.r.RemoteAddr), - slog.String("method", h.r.Method), - slog.String("url", h.r.URL.String()), - slog.Int("status", http.StatusBadRequest)) -} +// func (h *HandlerV1) errNotFound(w http.ResponseWriter, r *http.Request) { +// utils.WriteJSONError(h.w, http.StatusBadRequest, "invalid request") +// h.log.Error("HTTP request error", +// slog.String("remote", h.r.RemoteAddr), +// slog.String("method", h.r.Method), +// slog.String("url", h.r.URL.String()), +// slog.Int("status", http.StatusBadRequest)) +// } func (h *HandlerV1) extractDescriptionStatic(path string) (string, error) { data, err := os.ReadFile(path) diff --git a/core/update/update.go b/core/update/update.go index bc792cb..902f7a9 100644 --- a/core/update/update.go +++ b/core/update/update.go @@ -1,10 +1,15 @@ package update import ( + "archive/tar" + "compress/gzip" "errors" "io" "log/slog" "net/http" + "os" + "os/exec" + "path/filepath" "strconv" "strings" @@ -187,15 +192,133 @@ func (u *Updater) CkeckUpdates() (IsNewUpdate, error) { if currentVersion == latestVersion && currentBranch == latestBranch { return false, nil } - u.Log.Info("New update available", - slog.String("current_version", string(currentVersion)), - slog.String("current_branch", string(currentBranch)), - slog.String("latest_version", string(latestVersion)), - slog.String("latest_branch", string(latestBranch)), - ) return true, nil } +func (u *Updater) Update() error { + if !(u.Config.UpdatesEnabled) { + return errors.New("updates are disabled in config, skipping update") + } + downloadPath, err := os.MkdirTemp("", "*-gs-up") + if err != nil { + return errors.New("failed to create temp dir " + err.Error()) + } + //defer os.RemoveAll(downloadPath) + _, currentBranch, err := u.GetCurrentVersion() + if err != nil { + return errors.New("failed to get current version: " + err.Error()) + } + latestVersion, latestBranch, err := u.GetLatestVersion(currentBranch) + if err != nil { + return errors.New("failed to get latest version: " + err.Error()) + } + updateArchiveName := config.GetUpdateConsts().GetUpdateArchiveName() + ".v" + string(latestVersion) + "-" + string(latestBranch) + updateDest := u.Config.Updates.RepositoryURL + "/" + updateArchiveName + ".tar.gz" + resp, err := http.Get(updateDest) + if err != nil { + return errors.New("failed to fetch latest version archive: " + err.Error()) + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusOK { + body, _ := io.ReadAll(resp.Body) + return errors.New("failed to fetch latest version archive: status " + resp.Status + ", body: " + string(body)) + } + gzReader, err := gzip.NewReader(resp.Body) + if err != nil { + return errors.New("failed to create gzip reader: " + err.Error()) + } + defer gzReader.Close() + tarReader := tar.NewReader(gzReader) + for { + header, err := tarReader.Next() + if err == io.EOF { + break // archive is fully read + } + if err != nil { + return errors.New("failed to read tar header: " + err.Error()) + } + + targetPath := filepath.Join(downloadPath, header.Name) + + switch header.Typeflag { + case tar.TypeDir: + // Создаём директорию + if err := os.MkdirAll(targetPath, os.FileMode(header.Mode)); err != nil { + return errors.New("failed to create directory: " + err.Error()) + } + case tar.TypeReg: + // Создаём директорию, если её ещё нет + if err := os.MkdirAll(filepath.Dir(targetPath), 0755); err != nil { + return errors.New("failed to create directory for file: " + err.Error()) + } + // Создаём файл + outFile, err := os.Create(targetPath) + if err != nil { + return errors.New("failed to create file: " + err.Error()) + } + // Копируем содержимое + if _, err := io.Copy(outFile, tarReader); err != nil { + outFile.Close() + return errors.New("failed to copy file content: " + err.Error()) + } + outFile.Close() + default: + return errors.New("unsupported tar entry type: " + string(header.Typeflag)) + } + } + return u.InstallAndRestart(filepath.Join(downloadPath, updateArchiveName, "node")) +} + +func (u *Updater) InstallAndRestart(newBinaryPath string) error { + nodePath := os.Getenv("NODE_PATH") + if nodePath == "" { + return errors.New("NODE_PATH environment variable is not set") + } + installDir := filepath.Join(nodePath, "bin") + targetPath := filepath.Join(installDir, "node") + + // Копируем новый бинарник + input, err := os.Open(newBinaryPath) + if err != nil { + return err + } + + output, err := os.Create(targetPath) + if err != nil { + return err + } + + + if _, err := io.Copy(output, input); err != nil { + return err + } + + if err := os.Chmod(targetPath, 0755); err != nil { + return errors.New("failed to chmod file: " + err.Error()) + } + + input.Close() + output.Close() + // Запускаем новый процесс + u.Log.Info("Launching new version...", slog.String("path", targetPath)) + cmd := exec.Command(targetPath, os.Args[1:]...) + cmd.Env = os.Environ() + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + + err = nil + if err = cmd.Start(); err != nil { + return err + } + // if err := syscall.Exec(targetPath, os.Args, os.Environ()); err != nil { + // u.Log.Error("Failed to run new version automatickly", slog.String("err", err.Error())) + // return err + // } + u.Log.Info("Shutting down") + os.Exit(0) + return errors.New("failed to shutdown the process") +} + // func (u *Updater) Update() error { // if !(u.Config.UpdatesEnabled && u.Config.Updates.AllowUpdates && u.Config.Updates.AllowDowngrades) { // u.Log.Info("Updates are disabled in config, skipping update") diff --git a/go.mod b/go.mod index 8141e05..25f0842 100644 --- a/go.mod +++ b/go.mod @@ -11,6 +11,7 @@ require ( require ( github.com/BurntSushi/toml v1.5.0 // indirect + github.com/go-chi/cors v1.2.2 github.com/joho/godotenv v1.5.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect olympos.io/encoding/edn v0.0.0-20201019073823-d3554ca0b0a3 // indirect diff --git a/go.sum b/go.sum index 1e409b1..a72ba4a 100644 --- a/go.sum +++ b/go.sum @@ -3,6 +3,8 @@ github.com/BurntSushi/toml v1.5.0 h1:W5quZX/G/csjUnuI8SUYlsHs9M38FC7znL0lIO+DvMg github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= github.com/go-chi/chi/v5 v5.2.2 h1:CMwsvRVTbXVytCk1Wd72Zy1LAsAh9GxMmSNWLHCG618= github.com/go-chi/chi/v5 v5.2.2/go.mod h1:L2yAIGWB3H+phAw1NxKwWM+7eUH/lU8pOMm5hHcoops= +github.com/go-chi/cors v1.2.2 h1:Jmey33TE+b+rB7fT8MUy1u0I4L+NARQlK6LhzKPSyQE= +github.com/go-chi/cors v1.2.2/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vzc58= github.com/ilyakaznacheev/cleanenv v1.5.0 h1:0VNZXggJE2OYdXE87bfSSwGxeiGt9moSR2lOrsHHvr4= github.com/ilyakaznacheev/cleanenv v1.5.0/go.mod h1:a5aDzaJrLCQZsazHol1w8InnDcOX0OColm64SlIi6gk= github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=