init: first steps

This commit is contained in:
2025-07-20 23:50:47 +03:00
commit 5f26ad6941
25 changed files with 1134 additions and 0 deletions

118
pkg/http/errors.go Normal file
View File

@@ -0,0 +1,118 @@
package http
import (
"encoding/json"
"net/http"
"github.com/go-chi/chi/v5/middleware"
)
type ResponseErr struct {
Message string `json:"message"`
Trace string `json:"trace"`
Details map[string]any `json:"details"`
}
type ResponseErrReadyToSend interface {
Send(http.ResponseWriter) error
}
type ResponseErrBuilder interface {
WithDetails(map[string]any) ResponseErrBuilder
WithMessage(string) ResponseErrBuilder
WithStatusCode(int) ResponseErrBuilder
WithTrace(string) ResponseErrBuilder
Ready() ResponseErrReadyToSend
Send(http.ResponseWriter, *http.Request) error
}
type responseErrBuilder struct {
details map[string]any
message string
status int
trace string
ready *ResponseErr
}
type readyResponseErr struct {
builder responseErrBuilder
}
func (r readyResponseErr) Send(response http.ResponseWriter) error {
format, err := json.Marshal(r.builder.ready)
if err != nil {
return err
}
if r.builder.status == 0 {
r.builder.status = http.StatusInternalServerError
}
response.WriteHeader(r.builder.status)
_, err = response.Write(format)
return err
}
func (r responseErrBuilder) WithTrace(s string) ResponseErrBuilder {
r.trace = s
return r
}
func (r responseErrBuilder) WithStatusCode(i int) ResponseErrBuilder {
r.status = i
return r
}
func NewResponseErrBuilder() ResponseErrBuilder {
return &responseErrBuilder{}
}
func (r responseErrBuilder) WithDetails(m map[string]any) ResponseErrBuilder {
r.details = m
return r
}
func (r responseErrBuilder) WithMessage(s string) ResponseErrBuilder {
r.message = s
return r
}
func (r responseErrBuilder) Ready() ResponseErrReadyToSend {
r.ready = &ResponseErr{
Message: r.message,
Trace: r.trace,
Details: r.details,
}
return readyResponseErr{
builder: r,
}
}
func (r responseErrBuilder) Send(response http.ResponseWriter, request *http.Request) error {
if r.ready == nil {
r.ready = &ResponseErr{
Message: r.message,
Trace: middleware.GetReqID(request.Context()),
Details: r.details,
}
}
format, err := json.Marshal(r.ready)
if err != nil {
return err
}
if r.status == 0 {
r.status = http.StatusInternalServerError
}
response.WriteHeader(r.status)
_, err = response.Write(format)
return err
}

46
pkg/http/extract.go Normal file
View File

@@ -0,0 +1,46 @@
package http
import (
"net/http"
"strconv"
)
func ExtractLimitOffset(r *http.Request) (int, int, ResponseErrReadyToSend) {
limit, offset := r.URL.Query().Get("limit"), r.URL.Query().Get("offset")
if limit == "" {
limit = "20"
}
if offset == "" {
offset = "0"
}
limitInt, err := strconv.Atoi(limit)
if err != nil {
writeErr := NewResponseErrBuilder().
WithMessage("Incorrect limit parameter").
WithDetails(map[string]interface{}{
"err": err.Error(),
}).
WithStatusCode(http.StatusBadRequest).
Ready()
return 0, 0, writeErr
}
offsetInt, err := strconv.Atoi(offset)
if err != nil {
writeErr := NewResponseErrBuilder().
WithMessage("Incorrect offset parameter").
WithDetails(map[string]interface{}{
"err": err.Error(),
}).
WithStatusCode(http.StatusBadRequest).
Ready()
return 0, 0, writeErr
}
return limitInt, offsetInt, nil
}

19
pkg/http/response.go Normal file
View File

@@ -0,0 +1,19 @@
package http
import (
"encoding/json"
"net/http"
)
func JSON(w http.ResponseWriter, i any, httpCode int) error {
response, err := json.Marshal(i)
if err != nil {
return err
}
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(httpCode)
_, err = w.Write(response)
return err
}