logger mv; get recipe http handler

This commit is contained in:
yash 2024-01-23 13:22:16 +03:00
parent a28beb0e9c
commit 55c7c2bff6
8 changed files with 230 additions and 9 deletions

View File

@ -5,6 +5,7 @@ import (
"log/slog"
"os"
"recipes/internal/config"
"recipes/internal/lib/logger/sl"
"recipes/internal/media_storage/minio"
"recipes/internal/parser"
"recipes/internal/storage/postgresql"
@ -32,7 +33,7 @@ func main() {
cfg.Postgresql.DBName,
)
if err != nil {
log.Error("failed to init storage", "err", err)
log.Error("failed to init storage", sl.Err(err))
os.Exit(1)
}
// init media storage
@ -43,13 +44,13 @@ func main() {
cfg.Minio.Password,
)
if err != nil {
log.Error("failed to init media storage", "err", err)
log.Error("failed to init media storage", sl.Err(err))
os.Exit(1)
}
// run parser
_, err = parser.SavePage(log, 1, mstorage, storage, storage)
if err != nil {
log.Error("Parse failed", "err", err)
log.Error("Parse failed", sl.Err(err))
os.Exit(1)
}
log.Info("parsing was completed successfully")

View File

@ -4,7 +4,9 @@ import (
"fmt"
"log/slog"
"os"
"os/signal"
"recipes/internal/config"
"syscall"
prettyLogger "github.com/charmbracelet/log"
)
@ -15,6 +17,13 @@ const (
envProd = "prod"
)
//TODO
// cache
// http server
// app
// graceful sd
// tests
func main() {
// load config
cfg := config.MustLoad()
@ -23,15 +32,20 @@ func main() {
log.Info("starting application", slog.String("env", cfg.Env))
log.Debug("debug messages are enabled")
log.Debug("Application config", slog.Any("config", fmt.Sprintf("%+v", *cfg)))
// init storage
// init cache
// init media storage
// init app
// init app (storage, cache, media storage)
// graceful shutdown
stop := make(chan os.Signal, 1)
signal.Notify(stop, syscall.SIGTERM, syscall.SIGINT)
<-stop
log.Info("stopping application...")
// application.GRPCSrv.Stop()
log.Info("application stopped")
}
func setupLogger(env string) *slog.Logger {

8
go.mod
View File

@ -14,6 +14,7 @@ require (
require (
github.com/BurntSushi/toml v1.2.1 // indirect
github.com/PuerkitoBio/goquery v1.8.1 // indirect
github.com/ajg/form v1.5.1 // indirect
github.com/andybalholm/cascadia v1.3.1 // indirect
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
github.com/cenkalti/backoff/v4 v4.1.2 // indirect
@ -21,8 +22,14 @@ require (
github.com/charmbracelet/lipgloss v0.9.1 // indirect
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/gabriel-vasile/mimetype v1.4.2 // indirect
github.com/go-chi/chi/v5 v5.0.11 // indirect
github.com/go-chi/render v1.0.3 // indirect
github.com/go-ini/ini v1.67.0 // indirect
github.com/go-logfmt/logfmt v0.6.0 // 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.17.0 // indirect
github.com/google/uuid v1.5.0 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-multierror v1.1.1 // indirect
@ -34,6 +41,7 @@ require (
github.com/klauspost/compress v1.17.4 // indirect
github.com/klauspost/cpuid/v2 v2.2.6 // indirect
github.com/kr/text v0.2.0 // indirect
github.com/leodido/go-urn v1.2.4 // indirect
github.com/lib/pq v1.10.9 // indirect
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
github.com/mattn/go-isatty v0.0.18 // indirect

21
go.sum
View File

@ -6,6 +6,8 @@ github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migc
github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM=
github.com/PuerkitoBio/goquery v1.8.1 h1:uQxhNlArOIdbrH1tr0UXwdVFgDcZDrZVdcpygAcwmWM=
github.com/PuerkitoBio/goquery v1.8.1/go.mod h1:Q8ICL1kNUJ2sXGoAhPGUdYDJvgQgHzJsnnd3H7Ho5jQ=
github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU=
github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=
github.com/andybalholm/cascadia v1.3.1 h1:nhxRkql1kdYCc8Snf7D5/D3spOX+dBgjA6u8x004T2c=
github.com/andybalholm/cascadia v1.3.1/go.mod h1:R4bJ1UQfqADjvDa4P6HZHLh/3OxWWEqc0Sk8XGwHqvA=
github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
@ -40,10 +42,22 @@ github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4
github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
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-chi/chi/v5 v5.0.11 h1:BnpYbFZ3T3S1WMpD79r7R5ThWX40TaFB7L31Y8xqSwA=
github.com/go-chi/chi/v5 v5.0.11/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
github.com/go-chi/render v1.0.3 h1:AsXqd2a1/INaIfUSKq3G5uA8weYx20FOsM7uSoCyyt4=
github.com/go-chi/render v1.0.3/go.mod h1:/gr3hVkmYR0YlEy3LxCuVRFzEu9Ruok+gFqbIofjao0=
github.com/go-ini/ini v1.67.0 h1:z6ZrTEZqSWOTyH2FlglNbNgARyHG8oLW9gMELqKr06A=
github.com/go-ini/ini v1.67.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
github.com/go-logfmt/logfmt v0.6.0 h1:wGYYu3uicYdqXVgoYbvnkrPVXkuLM1p1ifugDMEdRi4=
github.com/go-logfmt/logfmt v0.6.0/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
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.17.0 h1:SmVVlfAOtlZncTxRuinDPomC2DkXJ4E5T9gDA0AIH74=
github.com/go-playground/validator/v10 v10.17.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang-migrate/migrate/v4 v4.17.0 h1:rd40H3QXU0AA4IoLllFcEAEo9dYKRHYND2gB4p7xcaU=
@ -80,6 +94,8 @@ github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
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/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
@ -135,8 +151,13 @@ github.com/s32x/httpclient v0.0.0-20220217184346-6df4d4d51c14/go.mod h1:FqlhGa3u
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
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.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
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/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=

View File

@ -0,0 +1,76 @@
package recipe
import (
"context"
"errors"
"log/slog"
"net/http"
"recipes/internal/domain/models"
resp "recipes/internal/lib/api/response"
"recipes/internal/lib/logger/sl"
"recipes/internal/storage"
"github.com/go-chi/chi/v5/middleware"
"github.com/go-chi/render"
"github.com/go-playground/validator/v10"
)
type Request struct {
RecipeId uint `json:"recipe_id" validate:"required"`
}
type Response struct {
resp.Response
Recipe models.Recipe `json:"recipe"`
}
type RecipeProvider interface {
GetRecipe(ctx context.Context, r_id uint) (models.Recipe, error)
}
func New(log *slog.Logger, recipeProvider RecipeProvider) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
const op = "http-server.handlers.recipe.New"
log = log.With(
slog.String("op", op),
slog.String("request_id", middleware.GetReqID(r.Context())),
)
var req Request
// decode request
err := render.DecodeJSON(r.Body, &req)
if err != nil {
log.Error("failed to decode request body", sl.Err(err))
render.JSON(w, r, resp.Error("failed to decode request"))
return
}
log.Debug("request body decoded", slog.Any("request", req))
// validate request
if err := validator.New().Struct(req); err != nil {
validateErr := err.(validator.ValidationErrors)
log.Error("invalid request", sl.Err(err))
render.JSON(w, r, resp.ValidationError(validateErr))
return
}
// get from storage
recipe, err := recipeProvider.GetRecipe(r.Context(), req.RecipeId)
if err != nil {
log.Error("failed to get recipe from storage", sl.Err(err))
if errors.Is(err, storage.ErrRecipeNotFound) {
render.JSON(w, r, resp.Error("recipe not found"))
return
}
render.JSON(w, r, resp.Error("failed to get recipe"))
return
}
// render response
var resp Response = Response{
Response: resp.OK(),
Recipe: recipe,
}
log.Debug("response", slog.Any("resp", resp))
render.JSON(w, r, resp)
}
}

View File

@ -0,0 +1,42 @@
package middleware
import (
"log/slog"
"net/http"
"time"
"github.com/go-chi/chi/v5/middleware"
)
func New(log *slog.Logger) func(next http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
log = log.With(
slog.String("component", "midddleware/logger"),
)
log.Info("logger middleware enabled")
fn := func(w http.ResponseWriter, r *http.Request) {
entry := log.With(
slog.String("method", r.Method),
slog.String("path", r.URL.Path),
slog.String("remote_addr", r.RemoteAddr),
slog.String("user_agent", r.UserAgent()),
slog.String("request_id", middleware.GetReqID(r.Context())),
)
ww := middleware.NewWrapResponseWriter(w, r.ProtoMajor)
t1 := time.Now()
defer func() {
entry.Info("request completed",
slog.Int("status", ww.Status()),
slog.Int("bytes", ww.BytesWritten()),
slog.String("duration", time.Since(t1).String()),
)
}()
next.ServeHTTP(ww, r)
}
return http.HandlerFunc(fn)
}
}

View File

@ -0,0 +1,49 @@
package response
import (
"fmt"
"strings"
"github.com/go-playground/validator/v10"
)
type Response struct {
Status string `json:"status"`
Error string `json:"error,omitempty"`
}
const (
StatusOK = "OK"
StatusError = "Error"
)
func OK() Response {
return Response{
Status: StatusOK,
}
}
func Error(msg string) Response {
return Response{
Status: StatusError,
Error: msg,
}
}
func ValidationError(errs validator.ValidationErrors) Response {
var errMsgs []string
for _, err := range errs {
switch err.ActualTag() {
case "required":
errMsgs = append(errMsgs, fmt.Sprintf("field %s is a required field", err.Field()))
default:
errMsgs = append(errMsgs, fmt.Sprintf("field %s is not valid", err.Field()))
}
}
return Response{
Status: StatusError,
Error: strings.Join(errMsgs, ", "),
}
}

View File

@ -0,0 +1,10 @@
package sl
import "log/slog"
func Err(err error) slog.Attr {
return slog.Attr{
Key: "error",
Value: slog.StringValue(err.Error()),
}
}