From 28c2b2084ed1083b616fab3567f5638a8ea0f81b Mon Sep 17 00:00:00 2001 From: alex Date: Sun, 10 Sep 2023 13:53:57 +0200 Subject: [PATCH] init project --- go.mod | 30 ++++++++ go.sum | 57 ++++++++++++++++ grouptasks.log | 2 + main.go | 34 ++++++++++ modules/config/config.go | 33 +++++++++ modules/loghandler/loghandler.go | 113 +++++++++++++++++++++++++++++++ modules/structs/log.go | 16 +++++ modules/utils/globals.go | 6 ++ modules/utils/utils.go | 51 ++++++++++++++ modules/utils/validator.go | 37 ++++++++++ routers/router/api/v1/log/log.go | 67 ++++++++++++++++++ routers/router/router.go | 15 ++++ 12 files changed, 461 insertions(+) create mode 100644 go.mod create mode 100644 go.sum create mode 100644 grouptasks.log create mode 100644 main.go create mode 100644 modules/config/config.go create mode 100644 modules/loghandler/loghandler.go create mode 100644 modules/structs/log.go create mode 100644 modules/utils/globals.go create mode 100644 modules/utils/utils.go create mode 100644 modules/utils/validator.go create mode 100644 routers/router/api/v1/log/log.go create mode 100644 routers/router/router.go diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..34ee750 --- /dev/null +++ b/go.mod @@ -0,0 +1,30 @@ +module jannex/log-manager + +go 1.21.0 + +require ( + github.com/gofiber/fiber/v2 v2.49.1 + github.com/joho/godotenv v1.5.1 +) + +require ( + github.com/andybalholm/brotli v1.0.5 // indirect + github.com/gabriel-vasile/mimetype v1.4.2 // indirect + github.com/go-playground/locales v0.14.1 // indirect + github.com/go-playground/universal-translator v0.18.1 // indirect + github.com/go-playground/validator/v10 v10.15.3 // indirect + github.com/google/uuid v1.3.1 // indirect + github.com/klauspost/compress v1.16.7 // indirect + github.com/leodido/go-urn v1.2.4 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.19 // indirect + github.com/mattn/go-runewidth v0.0.15 // indirect + github.com/rivo/uniseg v0.2.0 // indirect + github.com/valyala/bytebufferpool v1.0.0 // indirect + github.com/valyala/fasthttp v1.49.0 // indirect + github.com/valyala/tcplisten v1.0.0 // indirect + golang.org/x/crypto v0.7.0 // indirect + golang.org/x/net v0.8.0 // indirect + golang.org/x/sys v0.11.0 // indirect + golang.org/x/text v0.8.0 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..18132e5 --- /dev/null +++ b/go.sum @@ -0,0 +1,57 @@ +github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs= +github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU= +github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA= +github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= +github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= +github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= +github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= +github.com/go-playground/validator/v10 v10.15.3 h1:S+sSpunYjNPDuXkWbK+x+bA7iXiW296KG4dL3X7xUZo= +github.com/go-playground/validator/v10 v10.15.3/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU= +github.com/gofiber/fiber/v2 v2.49.1 h1:0W2DRWevSirc8pJl4o8r8QejDR8TV6ZUCawHxwbIdOk= +github.com/gofiber/fiber/v2 v2.49.1/go.mod h1:nPUeEBUeeYGgwbDm59Gp7vS8MDyScL6ezr/Np9A13WU= +github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4= +github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= +github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= +github.com/klauspost/compress v1.16.7 h1:2mk3MPGNzKyxErAw8YaohYh69+pa4sIQSC0fPGCFR9I= +github.com/klauspost/compress v1.16.7/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= +github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q= +github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= +github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= +github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= +github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= +github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= +github.com/valyala/fasthttp v1.49.0 h1:9FdvCpmxB74LH4dPb7IJ1cOSsluR07XG3I1txXWwJpE= +github.com/valyala/fasthttp v1.49.0/go.mod h1:k2zXd82h/7UZc3VOdJ2WaUqt1uZ/XpXAfE9i+HBC3lA= +github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8= +github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc= +golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A= +golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= +golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ= +golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM= +golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68= +golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/grouptasks.log b/grouptasks.log new file mode 100644 index 0000000..9b61e78 --- /dev/null +++ b/grouptasks.log @@ -0,0 +1,2 @@ +2023/09/10 10:26:56 Hallo +2023/09/10 10:26:56 Ey diff --git a/main.go b/main.go new file mode 100644 index 0000000..fe93bbf --- /dev/null +++ b/main.go @@ -0,0 +1,34 @@ +package main + +import ( + "jannex/log-manager/modules/config" + "jannex/log-manager/modules/utils" + "jannex/log-manager/routers/router" + + "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v2/middleware/cors" + "github.com/gofiber/fiber/v2/middleware/logger" +) + +func init() { + config.LoadConfig() + utils.ValidatorInit() + + utils.CreateDirectoryIfNotExists(config.Cfg.LogFolder) +} + +func main() { + app := fiber.New(fiber.Config{ + BodyLimit: 100 * 1024 * 1024, + }) + + app.Use(cors.New()) + + app.Use(logger.New(logger.Config{ + Format: "${pid} ${locals:requestid} ${status} - ${latency} ${method} ${path}​\n", + })) + + router.SetupRoutes(app) + + app.Listen(config.Cfg.Host + ":" + config.Cfg.Port) +} diff --git a/modules/config/config.go b/modules/config/config.go new file mode 100644 index 0000000..c922ea0 --- /dev/null +++ b/modules/config/config.go @@ -0,0 +1,33 @@ +package config + +import ( + "fmt" + "os" + + "github.com/joho/godotenv" +) + +var Cfg Config + +type Config struct { + Host string + Port string + LogFolder string +} + +func LoadConfig() { + // used to determine server was startet in docker or not + if os.Getenv("DOCKER") == "" { + fmt.Println("Load env from file") + godotenv.Load(".env") + } else { + fmt.Println("Load env from system") + } + + Cfg = Config{ + + Host: os.Getenv("HOST"), + Port: os.Getenv("PORT"), + LogFolder: os.Getenv("LOG_FOLDER"), + } +} diff --git a/modules/loghandler/loghandler.go b/modules/loghandler/loghandler.go new file mode 100644 index 0000000..49c4ff9 --- /dev/null +++ b/modules/loghandler/loghandler.go @@ -0,0 +1,113 @@ +package loghandler + +import ( + "bufio" + "fmt" + "jannex/log-manager/modules/config" + "jannex/log-manager/modules/structs" + "jannex/log-manager/modules/utils" + "os" + "strconv" + "sync" + "time" +) + +var FileMutexMap = make(map[string]*sync.Mutex) +var FileMutexMapLock sync.Mutex + +func getFileMutex(filePath string) *sync.Mutex { + FileMutexMapLock.Lock() + defer FileMutexMapLock.Unlock() + + mutex, ok := FileMutexMap[filePath] + + if !ok { + mutex = &sync.Mutex{} + FileMutexMap[filePath] = mutex + } + + return mutex +} + +func AddLog(body structs.LogBody) { + year, month, day := time.Now().Date() + + logFolder := config.Cfg.LogFolder + + utils.CreateDirectoryIfNotExists(logFolder + body.Type) + + path := logFolder + body.Type + "/" + strconv.Itoa(day) + "-" + strconv.Itoa(int(month)) + "-" + strconv.Itoa(year) + ".log" + + // get the mutex for this file + mutex := getFileMutex(path) + + // lock the mutex to ensure the file is only opened once at a time + mutex.Lock() + defer mutex.Unlock() + + file, err := os.OpenFile(path, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) + + if err != nil { + fmt.Println(err) + return + } + + defer file.Close() + + for _, log := range body.Logs { + if _, err := fmt.Fprintln(file, log); err != nil { + fmt.Println(err) + } + } +} + +func GetAvailableLogFiles(logType string) ([]string, error) { + var availableLogs []string + + path := config.Cfg.LogFolder + logType + + files, err := os.ReadDir(path) + + if err != nil { + fmt.Println(err) + return []string{}, err + } + + for _, file := range files { + availableLogs = append(availableLogs, file.Name()) + } + + return availableLogs, nil +} + +func GetLogByDate(logType string, date string) ([]string, error) { + var logs []string + + path := config.Cfg.LogFolder + logType + "/" + date + ".log" + + // get the mutex for this file + mutex := getFileMutex(path) + + // lock the mutex to ensure the file is only opened once at a time + mutex.Lock() + defer mutex.Unlock() + + file, err := os.Open(path) + if err != nil { + fmt.Println(err) + return []string{}, err + } + defer file.Close() + + scanner := bufio.NewScanner(file) + + for scanner.Scan() { + logs = append(logs, scanner.Text()) + } + + if len(logs) == 0 { + return []string{}, nil + } + + return logs, nil +} diff --git a/modules/structs/log.go b/modules/structs/log.go new file mode 100644 index 0000000..c5d5b75 --- /dev/null +++ b/modules/structs/log.go @@ -0,0 +1,16 @@ +package structs + +type LogBody struct { + Type string + Logs []string +} + +// /log/grouptasks?d=2021-08-01&f=i +type GetLogParams struct { + Type string +} + +type GetLogQuery struct { + D string // date + F string // filter +} diff --git a/modules/utils/globals.go b/modules/utils/globals.go new file mode 100644 index 0000000..bf8f118 --- /dev/null +++ b/modules/utils/globals.go @@ -0,0 +1,6 @@ +package utils + +var logRules = map[string]string{ + "Type": "required", + "Logs": "required", +} diff --git a/modules/utils/utils.go b/modules/utils/utils.go new file mode 100644 index 0000000..b365939 --- /dev/null +++ b/modules/utils/utils.go @@ -0,0 +1,51 @@ +package utils + +import ( + "errors" + "os" + + "github.com/gofiber/fiber/v2" +) + +func ParamsParserHelper(c *fiber.Ctx, params interface{}) error { + if err := c.ParamsParser(params); err != nil { + return errors.New("Failed to parse params") + } + + if errValidation := ValidateStruct(params); errValidation != nil { + return errors.New("Failed to validate params") + } + + return nil +} + +func BodyParserHelper(c *fiber.Ctx, body interface{}) error { + if err := c.BodyParser(body); err != nil { + + return errors.New("Failed to parse body") + } + + if errValidation := ValidateStruct(body); errValidation != nil { + return errors.New("Failed to validate body") + } + + return nil +} + +func QueryParserHelper(c *fiber.Ctx, query interface{}) error { + if err := c.QueryParser(query); err != nil { + return errors.New("Failed to parse query") + } + + if errValidation := ValidateStruct(query); errValidation != nil { + return errors.New("Failed to validate query") + } + + return nil +} + +func CreateDirectoryIfNotExists(path string) { + if _, err := os.Stat(path); os.IsNotExist(err) { + os.Mkdir(path, 0755) + } +} diff --git a/modules/utils/validator.go b/modules/utils/validator.go new file mode 100644 index 0000000..8143378 --- /dev/null +++ b/modules/utils/validator.go @@ -0,0 +1,37 @@ +package utils + +import ( + "jannex/log-manager/modules/structs" + + "github.com/go-playground/validator/v10" +) + +type ErrorResponse struct { + FailedField string + Tag string + Value string +} + +var Validate = validator.New() + +func ValidateStruct(event interface{}) []*ErrorResponse { + var errors []*ErrorResponse + err := Validate.Struct(event) + if err != nil { + for _, err := range err.(validator.ValidationErrors) { + var element ErrorResponse + element.FailedField = err.StructNamespace() + element.Tag = err.Tag() + element.Value = err.Param() + errors = append(errors, &element) + } + } + return errors +} + +func ValidatorInit() { + Validate.RegisterStructValidationMapRules(logRules, + structs.LogBody{}, + structs.GetLogParams{}, + structs.GetLogQuery{}) +} diff --git a/routers/router/api/v1/log/log.go b/routers/router/api/v1/log/log.go new file mode 100644 index 0000000..5f4b725 --- /dev/null +++ b/routers/router/api/v1/log/log.go @@ -0,0 +1,67 @@ +package log + +import ( + "fmt" + "jannex/log-manager/modules/loghandler" + "jannex/log-manager/modules/structs" + "jannex/log-manager/modules/utils" + + "github.com/gofiber/fiber/v2" +) + +func AddLog(c *fiber.Ctx) error { + var body structs.LogBody + + fmt.Println("c", string(c.Body())) + + if err := utils.BodyParserHelper(c, &body); err != nil { + return c.SendStatus(fiber.StatusBadRequest) + } + + fmt.Println("body", body) + + loghandler.AddLog(body) + + return c.SendStatus(fiber.StatusOK) +} + +func GetLog(c *fiber.Ctx) error { + var params structs.GetLogParams + + if err := utils.ParamsParserHelper(c, ¶ms); err != nil { + return c.SendStatus(fiber.StatusBadRequest) + } + + var query structs.GetLogQuery + + if err := utils.QueryParserHelper(c, &query); err != nil { + return c.SendStatus(fiber.StatusBadRequest) + } + + fmt.Println("params", params, "query", query) + + // no type specified + if params.Type == "" { + return c.SendStatus(fiber.StatusBadRequest) + } + + // no date specified -> return all available files + if query.D == "" { + availableLogs, err := loghandler.GetAvailableLogFiles(params.Type) + + if err != nil { + return c.SendStatus(fiber.StatusUnprocessableEntity) + } + + return c.JSON(availableLogs) + } + + logs, err := loghandler.GetLogByDate(params.Type, query.D) + + if err != nil { + return c.SendStatus(fiber.StatusUnprocessableEntity) + } + + return c.JSON(logs) + +} diff --git a/routers/router/router.go b/routers/router/router.go new file mode 100644 index 0000000..784fa48 --- /dev/null +++ b/routers/router/router.go @@ -0,0 +1,15 @@ +package router + +import ( + "jannex/log-manager/routers/router/api/v1/log" + + "github.com/gofiber/fiber/v2" +) + +func SetupRoutes(app *fiber.App) { + v1 := app.Group("/v1") + + l := v1.Group("/log") + l.Post("/", log.AddLog) + l.Get("/:type", log.GetLog) +}