commit
eb4f3a4fd0
23
backend.Dockerfile
Normal file
23
backend.Dockerfile
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
FROM golang:1.22-alpine AS builder
|
||||||
|
WORKDIR /build
|
||||||
|
|
||||||
|
COPY go.mod go.sum ./
|
||||||
|
RUN go mod download && go mod verify
|
||||||
|
|
||||||
|
COPY cmd/backend cmd/backend
|
||||||
|
COPY pkg pkg
|
||||||
|
COPY internal internal
|
||||||
|
|
||||||
|
RUN GOEXPERIMENT=boringcrypto go build -ldflags "-s -w" -o ./app ./cmd/backend
|
||||||
|
RUN chmod +x ./app
|
||||||
|
|
||||||
|
FROM alpine:3.21.2 AS production
|
||||||
|
WORKDIR /backend
|
||||||
|
|
||||||
|
COPY --from=builder /build/app .
|
||||||
|
COPY deploy/backend-config.yaml ./config.yaml
|
||||||
|
COPY deploy/backend-jwt-privkey ./privkey
|
||||||
|
|
||||||
|
EXPOSE 8080
|
||||||
|
|
||||||
|
CMD ["./app", "-c", "config.yaml", "-k", "privkey"]
|
||||||
@ -1,7 +1,6 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"backend/cmd/backend/args_parser"
|
|
||||||
"backend/cmd/backend/server"
|
"backend/cmd/backend/server"
|
||||||
"backend/internal/core/models"
|
"backend/internal/core/models"
|
||||||
"backend/internal/core/repos"
|
"backend/internal/core/repos"
|
||||||
@ -55,7 +54,7 @@ func (a *App) Run(p RunParams) {
|
|||||||
|
|
||||||
//-----------------------------------------
|
//-----------------------------------------
|
||||||
|
|
||||||
args, err := args_parser.Parse(osArgs)
|
args, err := CmdArgsParse(osArgs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("failed to parse os args: %v\n", err)
|
log.Fatalf("failed to parse os args: %v\n", err)
|
||||||
}
|
}
|
||||||
@ -90,7 +89,7 @@ func (a *App) Run(p RunParams) {
|
|||||||
|
|
||||||
var key *rsa.PrivateKey
|
var key *rsa.PrivateKey
|
||||||
{
|
{
|
||||||
keyRawBytes, err := os.ReadFile(conf.GetJwtSigningKey())
|
keyRawBytes, err := os.ReadFile(args.GetSigningKeyPath())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Fatal().Err(err).Msg("failed reading signing key file")
|
logger.Fatal().Err(err).Msg("failed reading signing key file")
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,19 +1,21 @@
|
|||||||
package args_parser
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/akamensky/argparse"
|
"github.com/akamensky/argparse"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Args interface {
|
type CmdArgs interface {
|
||||||
GetProfilePath() string
|
GetProfilePath() string
|
||||||
GetConfigPath() string
|
GetConfigPath() string
|
||||||
GetLogPath() string
|
GetLogPath() string
|
||||||
|
GetSigningKeyPath() string
|
||||||
}
|
}
|
||||||
|
|
||||||
func Parse(osArgs []string) (Args, error) {
|
func CmdArgsParse(osArgs []string) (CmdArgs, error) {
|
||||||
parser := argparse.NewParser("backend", "runs backend")
|
parser := argparse.NewParser("backend", "runs backend")
|
||||||
|
|
||||||
s := parser.String("c", "config", &argparse.Options{Required: true, Help: "Path to a config file"})
|
s := parser.String("c", "config", &argparse.Options{Required: true, Help: "Path to a config file"})
|
||||||
|
k := parser.String("k", "key", &argparse.Options{Required: false, Default: "", Help: "Path to a jwt signing key"})
|
||||||
l := parser.String("o", "log", &argparse.Options{Required: false, Default: "", Help: "Path to a log file"})
|
l := parser.String("o", "log", &argparse.Options{Required: false, Default: "", Help: "Path to a log file"})
|
||||||
p := parser.String("p", "profile", &argparse.Options{Required: false, Default: "", Help: "Path to a cpu profile file"})
|
p := parser.String("p", "profile", &argparse.Options{Required: false, Default: "", Help: "Path to a cpu profile file"})
|
||||||
|
|
||||||
@ -23,16 +25,18 @@ func Parse(osArgs []string) (Args, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return &args{
|
return &args{
|
||||||
ConfigPath: *s,
|
ConfigPath: *s,
|
||||||
LogPath: *l,
|
LogPath: *l,
|
||||||
ProfilePath: *p,
|
ProfilePath: *p,
|
||||||
|
SigningKeyPath: *k,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type args struct {
|
type args struct {
|
||||||
ProfilePath string
|
ProfilePath string
|
||||||
ConfigPath string
|
ConfigPath string
|
||||||
LogPath string
|
LogPath string
|
||||||
|
SigningKeyPath string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *args) GetConfigPath() string {
|
func (a *args) GetConfigPath() string {
|
||||||
@ -46,3 +50,7 @@ func (a *args) GetLogPath() string {
|
|||||||
func (a *args) GetProfilePath() string {
|
func (a *args) GetProfilePath() string {
|
||||||
return a.ProfilePath
|
return a.ProfilePath
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a *args) GetSigningKeyPath() string {
|
||||||
|
return a.SigningKeyPath
|
||||||
|
}
|
||||||
@ -5,7 +5,6 @@ import "backend/pkg/config"
|
|||||||
type IConfig interface {
|
type IConfig interface {
|
||||||
GetPort() uint16
|
GetPort() uint16
|
||||||
GetPostgresUrl() string
|
GetPostgresUrl() string
|
||||||
GetJwtSigningKey() string
|
|
||||||
GetKafkaUrl() string
|
GetKafkaUrl() string
|
||||||
GetKafkaTopic() string
|
GetKafkaTopic() string
|
||||||
}
|
}
|
||||||
@ -15,11 +14,10 @@ func LoadConfig(filePath string) (IConfig, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
Port uint16 `yaml:"port"`
|
Port uint16 `yaml:"port"`
|
||||||
PostgresUrl string `yaml:"postgres_url"`
|
PostgresUrl string `yaml:"postgres_url"`
|
||||||
JwtSigningKey string `yaml:"jwt_signing_key" validate:"file"`
|
KafkaUrl string `yaml:"kafka_url"`
|
||||||
KafkaUrl string `yaml:"kafka_url"`
|
KafkaTopic string `yaml:"kafka_topic"`
|
||||||
KafkaTopic string `yaml:"kafka_topic"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Config) GetPort() uint16 {
|
func (c *Config) GetPort() uint16 {
|
||||||
@ -30,10 +28,6 @@ func (c *Config) GetPostgresUrl() string {
|
|||||||
return c.PostgresUrl
|
return c.PostgresUrl
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Config) GetJwtSigningKey() string {
|
|
||||||
return c.JwtSigningKey
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Config) GetKafkaUrl() string {
|
func (c *Config) GetKafkaUrl() string {
|
||||||
return c.KafkaUrl
|
return c.KafkaUrl
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +0,0 @@
|
|||||||
port: 8080
|
|
||||||
postgres_url: "postgres://postgres:postgres@localhost:5432/postgres"
|
|
||||||
jwt_signing_key: "./jwt_signing_key"
|
|
||||||
kafka_url: "localhost:9091"
|
|
||||||
kafka_topic: "events"
|
|
||||||
@ -28,14 +28,14 @@ func NewServer(opts NewServerOpts) *httpserver.Server {
|
|||||||
r := gin.New()
|
r := gin.New()
|
||||||
r.ContextWithFallback = true // Use it to allow getting values from c.Request.Context()
|
r.ContextWithFallback = true // Use it to allow getting values from c.Request.Context()
|
||||||
|
|
||||||
// r.Static("/webapp", "./webapp")
|
metrics := integrations.NewMetrics("backend")
|
||||||
|
serverMetrics := httpserver.NewServerMetrics(metrics)
|
||||||
|
|
||||||
r.GET("/health", handlers.New200OkHandler())
|
r.GET("/health", handlers.New200OkHandler())
|
||||||
|
r.Any("/metrics", gin.WrapH(metrics.HttpHandler()))
|
||||||
|
|
||||||
prometheus := integrations.NewPrometheus()
|
r.Use(httpserver.NewRecoveryMiddleware(opts.Logger, serverMetrics, opts.DebugMode))
|
||||||
r.Any("/metrics", gin.WrapH(prometheus.GetRequestHandler()))
|
r.Use(httpserver.NewRequestLogMiddleware(opts.Logger, opts.Tracer, serverMetrics))
|
||||||
|
|
||||||
r.Use(httpserver.NewRecoveryMiddleware(opts.Logger, prometheus, opts.DebugMode))
|
|
||||||
r.Use(httpserver.NewRequestLogMiddleware(opts.Logger, opts.Tracer, prometheus))
|
|
||||||
r.Use(httpserver.NewTracingMiddleware(opts.Tracer))
|
r.Use(httpserver.NewTracingMiddleware(opts.Tracer))
|
||||||
|
|
||||||
r.GET("/verify-user", handlers.NewUserVerifyEmailHandler(opts.Logger, opts.UserService))
|
r.GET("/verify-user", handlers.NewUserVerifyEmailHandler(opts.Logger, opts.UserService))
|
||||||
|
|||||||
@ -8,6 +8,7 @@ func LoadConfig(filePath string) (Config, error) {
|
|||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
App struct {
|
App struct {
|
||||||
|
Port uint16 `yaml:"port"`
|
||||||
LogFile string `yaml:"logFile"`
|
LogFile string `yaml:"logFile"`
|
||||||
ServiceUrl string `yaml:"serviceUrl"`
|
ServiceUrl string `yaml:"serviceUrl"`
|
||||||
}
|
}
|
||||||
|
|||||||
@ -14,7 +14,7 @@ const MSG_TEXT = `
|
|||||||
<body>
|
<body>
|
||||||
<p>{{.Text}}</p>
|
<p>{{.Text}}</p>
|
||||||
{{if .Link}}
|
{{if .Link}}
|
||||||
<a href="{{.Link}}">Click</a>link</p>
|
<a href="{{.Link}}">link</a>
|
||||||
{{end}}
|
{{end}}
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
@ -34,7 +34,7 @@ func NewEmailer(conf ConfigSMTP) (*Emailer, error) {
|
|||||||
}
|
}
|
||||||
defer closer.Close()
|
defer closer.Close()
|
||||||
|
|
||||||
htmlTemplate, err := template.New("verify-email").Parse(MSG_TEXT)
|
htmlTemplate, err := template.New("email").Parse(MSG_TEXT)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -52,22 +52,23 @@ type Emailer struct {
|
|||||||
dialer *gomail.Dialer
|
dialer *gomail.Dialer
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Emailer) SendRestorePassword(email, token string) error {
|
func (e *Emailer) SendRestorePassword(email, link string) error {
|
||||||
return e.sendEmail("Restore your password", email, MailContent{
|
return e.sendEmail("Restore your password", email, MailContent{
|
||||||
Text: "Token: " + token,
|
Text: "You received this message do request of password change. Use this link to change your password:",
|
||||||
|
Link: link,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Emailer) SendVerifyUser(email, link string) error {
|
func (e *Emailer) SendVerifyUser(email, link string) error {
|
||||||
return e.sendEmail("Verify your email", email, MailContent{
|
return e.sendEmail("Verify your email", email, MailContent{
|
||||||
Text: "You recieved this message due to registration of account. Use this link to verify email:",
|
Text: "You received this message due to registration of account. Use this link to verify email:",
|
||||||
Link: link,
|
Link: link,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Emailer) SendPasswordChanged(email string) error {
|
func (e *Emailer) SendPasswordChanged(email string) error {
|
||||||
return e.sendEmail("Password changed", email, MailContent{
|
return e.sendEmail("Password changed", email, MailContent{
|
||||||
Text: "You recieved this message due to password change",
|
Text: "Your password was successfully changed",
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
91
cmd/notifyer/event_handler.go
Normal file
91
cmd/notifyer/event_handler.go
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"backend/internal/integrations"
|
||||||
|
"backend/pkg/logger"
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
|
||||||
|
"github.com/segmentio/kafka-go"
|
||||||
|
)
|
||||||
|
|
||||||
|
type SendEmailEvent struct {
|
||||||
|
Email string `json:"email"`
|
||||||
|
Token string `json:"token"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewEventHandler(
|
||||||
|
config Config,
|
||||||
|
logger logger.Logger,
|
||||||
|
metrics *integrations.Metrics,
|
||||||
|
emailer *Emailer,
|
||||||
|
) *EventHandler {
|
||||||
|
eventsCounter := metrics.NewCounter("events_counter", "total events handled")
|
||||||
|
return &EventHandler{
|
||||||
|
config: config,
|
||||||
|
logger: logger,
|
||||||
|
emailer: emailer,
|
||||||
|
eventsCounter: eventsCounter,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type EventHandler struct {
|
||||||
|
config Config
|
||||||
|
logger logger.Logger
|
||||||
|
emailer *Emailer
|
||||||
|
eventsCounter integrations.Counter
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *EventHandler) eventLoop(ctx context.Context, kafkaReader *kafka.Reader) {
|
||||||
|
for {
|
||||||
|
msg, err := kafkaReader.FetchMessage(ctx)
|
||||||
|
if err == io.EOF {
|
||||||
|
e.logger.Fatal().Err(err)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
e.logger.Fatal().Err(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
|
e.logger.Log().Msgf("event: %s", msg.Key)
|
||||||
|
e.eventsCounter.Inc()
|
||||||
|
|
||||||
|
if err := kafkaReader.CommitMessages(ctx, msg); err != nil {
|
||||||
|
e.logger.Error().Err(err).Msg("failed to commit offset")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := e.handleEvent(ctx, msg); err != nil {
|
||||||
|
e.logger.Error().Err(err).Msg("failed to handle event")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *EventHandler) handleEvent(ctx context.Context, msg kafka.Message) error {
|
||||||
|
event := SendEmailEvent{}
|
||||||
|
if err := json.Unmarshal(msg.Value, &event); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: add context somehow
|
||||||
|
switch string(msg.Key) {
|
||||||
|
case "email_forgot_password":
|
||||||
|
link := fmt.Sprintf("%s/restore-password?token=%s", e.config.App.ServiceUrl, event.Token)
|
||||||
|
return e.emailer.SendRestorePassword(event.Email, link)
|
||||||
|
case "email_password_changed":
|
||||||
|
return e.emailer.SendPasswordChanged(event.Email)
|
||||||
|
case "email_verify_user":
|
||||||
|
link := fmt.Sprintf("%s/verify-user?token=%s", e.config.App.ServiceUrl, event.Token)
|
||||||
|
return e.emailer.SendVerifyUser(event.Email, link)
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Errorf("unknown event type")
|
||||||
|
}
|
||||||
@ -1,21 +1,16 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
httpserver "backend/internal/http_server"
|
||||||
|
"backend/internal/integrations"
|
||||||
"backend/pkg/logger"
|
"backend/pkg/logger"
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"log"
|
"log"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/segmentio/kafka-go"
|
"github.com/segmentio/kafka-go"
|
||||||
)
|
)
|
||||||
|
|
||||||
type SendEmailEvent struct {
|
|
||||||
Email string `json:"email"`
|
|
||||||
Token string `json:"token"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
|
||||||
@ -24,67 +19,44 @@ func main() {
|
|||||||
log.Fatal(err.Error())
|
log.Fatal(err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
emailer, err := NewEmailer(config.SMTP)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
r := kafka.NewReader(kafka.ReaderConfig{
|
|
||||||
Brokers: config.Kafka.Brokers,
|
|
||||||
Topic: config.Kafka.Topic,
|
|
||||||
GroupID: config.Kafka.ConsumerGroupId,
|
|
||||||
})
|
|
||||||
|
|
||||||
logger, err := logger.New(ctx, logger.NewLoggerOpts{
|
logger, err := logger.New(ctx, logger.NewLoggerOpts{
|
||||||
Debug: true,
|
Debug: true,
|
||||||
OutputFile: config.App.LogFile,
|
OutputFile: config.App.LogFile,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err.Error())
|
logger.Fatal().Err(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.Printf("notifyer service started\n")
|
emailer, err := NewEmailer(config.SMTP)
|
||||||
|
if err != nil {
|
||||||
for {
|
logger.Fatal().Err(err)
|
||||||
msg, err := r.FetchMessage(ctx)
|
|
||||||
if err == io.EOF {
|
|
||||||
log.Fatal("EOF")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Printf("offset: %d, partition: %d, key: %s, value: %s\n", msg.Offset, msg.Partition, string(msg.Key), string(msg.Value))
|
|
||||||
|
|
||||||
if err := r.CommitMessages(ctx, msg); err != nil {
|
|
||||||
log.Fatalf("failed to commit: %s\n", err.Error())
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := handleEvent(config, emailer, msg); err != nil {
|
|
||||||
log.Printf("failed to handle event: %s\n", err.Error())
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
metrics := integrations.NewMetrics("notifyer")
|
||||||
func handleEvent(config Config, emailer *Emailer, msg kafka.Message) error {
|
|
||||||
event := SendEmailEvent{}
|
ginRouter := gin.New()
|
||||||
if err := json.Unmarshal(msg.Value, &event); err != nil {
|
ginRouter.GET("/metrics", gin.WrapH(metrics.HttpHandler()))
|
||||||
return err
|
ginRouter.GET("/health", func(ctx *gin.Context) {
|
||||||
}
|
ctx.Status(200)
|
||||||
|
})
|
||||||
switch string(msg.Key) {
|
|
||||||
case "email_forgot_password":
|
kafkaReader := kafka.NewReader(kafka.ReaderConfig{
|
||||||
return emailer.SendRestorePassword(event.Email, event.Token)
|
Brokers: config.Kafka.Brokers,
|
||||||
case "email_password_changed":
|
Topic: config.Kafka.Topic,
|
||||||
return emailer.SendPasswordChanged(event.Email)
|
GroupID: config.Kafka.ConsumerGroupId,
|
||||||
case "email_verify_user":
|
})
|
||||||
link := fmt.Sprintf("%s/verify-user?token=%s", config.App.ServiceUrl, event.Token)
|
kafkaReader.SetOffset(kafka.LastOffset)
|
||||||
return emailer.SendVerifyUser(event.Email, link)
|
|
||||||
}
|
eventHandler := NewEventHandler(config, logger, metrics, emailer)
|
||||||
|
go eventHandler.eventLoop(ctx, kafkaReader)
|
||||||
return fmt.Errorf("unknown event type")
|
|
||||||
|
logger.Log().Msg("notifyer service started")
|
||||||
|
|
||||||
|
srv := httpserver.New(
|
||||||
|
httpserver.NewServerOpts{
|
||||||
|
Logger: logger,
|
||||||
|
HttpServer: ginRouter,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
srv.Run(ctx, config.App.Port)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,3 +0,0 @@
|
|||||||
http_port: 8081
|
|
||||||
grpc_port: 8082
|
|
||||||
postgres_url: "postgres://postgres:postgres@localhost:5432/postgres"
|
|
||||||
@ -3,6 +3,7 @@ package main
|
|||||||
import "backend/pkg/config"
|
import "backend/pkg/config"
|
||||||
|
|
||||||
type IConfig interface {
|
type IConfig interface {
|
||||||
|
GetServiceUrl() string
|
||||||
GetHttpPort() uint16
|
GetHttpPort() uint16
|
||||||
GetGrpcPort() uint16
|
GetGrpcPort() uint16
|
||||||
GetPostgresUrl() string
|
GetPostgresUrl() string
|
||||||
@ -13,11 +14,16 @@ func LoadConfig(filePath string) (IConfig, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
|
ServiceUrl string `yaml:"service_url" validate:"required"`
|
||||||
HttpPort uint16 `yaml:"http_port" validate:"required"`
|
HttpPort uint16 `yaml:"http_port" validate:"required"`
|
||||||
GrpcPort uint16 `yaml:"grpc_port" validate:"required"`
|
GrpcPort uint16 `yaml:"grpc_port" validate:"required"`
|
||||||
PostgresUrl string `yaml:"postgres_url" validate:"required"`
|
PostgresUrl string `yaml:"postgres_url" validate:"required"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Config) GetServiceUrl() string {
|
||||||
|
return c.ServiceUrl
|
||||||
|
}
|
||||||
|
|
||||||
func (c *Config) GetHttpPort() uint16 {
|
func (c *Config) GetHttpPort() uint16 {
|
||||||
return c.HttpPort
|
return c.HttpPort
|
||||||
}
|
}
|
||||||
|
|||||||
@ -10,7 +10,7 @@ import (
|
|||||||
|
|
||||||
func NewShortlinksGrpc(log logger.Logger, shortlinkService services.ShortlinkService, host string) *ShortlinksGrpc {
|
func NewShortlinksGrpc(log logger.Logger, shortlinkService services.ShortlinkService, host string) *ShortlinksGrpc {
|
||||||
return &ShortlinksGrpc{
|
return &ShortlinksGrpc{
|
||||||
handler: NewCreateHandler(log, shortlinkService, host),
|
handler: NewShortlinkCreateHandler(log, shortlinkService, host),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -19,10 +19,10 @@ type shortlinkCreateOutput struct {
|
|||||||
Link string `json:"link"`
|
Link string `json:"link"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewCreateHandler(
|
func NewShortlinkCreateHandler(
|
||||||
log logger.Logger,
|
log logger.Logger,
|
||||||
shortlinkService services.ShortlinkService,
|
shortlinkService services.ShortlinkService,
|
||||||
host string,
|
serviceUrl string,
|
||||||
) httpserver.Handler[shortlinkCreateInput, shortlinkCreateOutput] {
|
) httpserver.Handler[shortlinkCreateInput, shortlinkCreateOutput] {
|
||||||
return func(ctx context.Context, input shortlinkCreateInput) (shortlinkCreateOutput, error) {
|
return func(ctx context.Context, input shortlinkCreateInput) (shortlinkCreateOutput, error) {
|
||||||
output := shortlinkCreateOutput{}
|
output := shortlinkCreateOutput{}
|
||||||
@ -39,13 +39,17 @@ func NewCreateHandler(
|
|||||||
}
|
}
|
||||||
|
|
||||||
return shortlinkCreateOutput{
|
return shortlinkCreateOutput{
|
||||||
Link: fmt.Sprintf("%s/s/%s", host, linkId),
|
Link: fmt.Sprintf("%s/s/%s", serviceUrl, linkId),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewShortlinkCreateHandler(log logger.Logger, shortlinkService services.ShortlinkService, host string) gin.HandlerFunc {
|
func NewShortlinkCreateGinHandler(
|
||||||
return httpserver.WrapGin(log, NewCreateHandler(log, shortlinkService, host))
|
log logger.Logger,
|
||||||
|
shortlinkService services.ShortlinkService,
|
||||||
|
serviceUrl string,
|
||||||
|
) gin.HandlerFunc {
|
||||||
|
return httpserver.WrapGin(log, NewShortlinkCreateHandler(log, shortlinkService, serviceUrl))
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewShortlinkResolveHandler(logger logger.Logger, shortlinkService services.ShortlinkService) gin.HandlerFunc {
|
func NewShortlinkResolveHandler(logger logger.Logger, shortlinkService services.ShortlinkService) gin.HandlerFunc {
|
||||||
|
|||||||
@ -10,7 +10,6 @@ import (
|
|||||||
"backend/pkg/cache"
|
"backend/pkg/cache"
|
||||||
"backend/pkg/logger"
|
"backend/pkg/logger"
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
@ -37,7 +36,6 @@ func main() {
|
|||||||
rootCmd.MarkPersistentFlagRequired("config")
|
rootCmd.MarkPersistentFlagRequired("config")
|
||||||
|
|
||||||
rootCmd.PersistentFlags().StringVarP(&logPath, "logfile", "l", "", "path to log file")
|
rootCmd.PersistentFlags().StringVarP(&logPath, "logfile", "l", "", "path to log file")
|
||||||
rootCmd.MarkPersistentFlagRequired("logfile")
|
|
||||||
|
|
||||||
if err := rootCmd.Execute(); err != nil {
|
if err := rootCmd.Execute(); err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
@ -79,32 +77,32 @@ func main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func RunServer(ctx context.Context, log logger.Logger, tracer trace.Tracer, conf IConfig, shortlinkService services.ShortlinkService) {
|
func RunServer(ctx context.Context, log logger.Logger, tracer trace.Tracer, conf IConfig, shortlinkService services.ShortlinkService) {
|
||||||
host := fmt.Sprintf("http://localhost:%d", conf.GetHttpPort())
|
|
||||||
debugMode := true
|
debugMode := true
|
||||||
if !debugMode {
|
if !debugMode {
|
||||||
gin.SetMode(gin.ReleaseMode)
|
gin.SetMode(gin.ReleaseMode)
|
||||||
}
|
}
|
||||||
|
|
||||||
prometheus := integrations.NewPrometheus()
|
metrics := integrations.NewMetrics("shortlinks")
|
||||||
|
serverMetrics := httpserver.NewServerMetrics(metrics)
|
||||||
|
|
||||||
r := gin.New()
|
r := gin.New()
|
||||||
r.Any("/metrics", gin.WrapH(prometheus.GetRequestHandler()))
|
r.Any("/metrics", gin.WrapH(metrics.HttpHandler()))
|
||||||
r.GET("/health", func(ctx *gin.Context) {
|
r.GET("/health", func(ctx *gin.Context) {
|
||||||
ctx.Status(200)
|
ctx.Status(200)
|
||||||
})
|
})
|
||||||
|
|
||||||
r.Use(httpserver.NewRecoveryMiddleware(log, prometheus, debugMode))
|
r.Use(httpserver.NewRecoveryMiddleware(log, serverMetrics, debugMode))
|
||||||
r.Use(httpserver.NewRequestLogMiddleware(log, tracer, prometheus))
|
r.Use(httpserver.NewRequestLogMiddleware(log, tracer, serverMetrics))
|
||||||
r.Use(httpserver.NewTracingMiddleware(tracer))
|
r.Use(httpserver.NewTracingMiddleware(tracer))
|
||||||
|
|
||||||
linkGroup := r.Group("/s")
|
linkGroup := r.Group("/s")
|
||||||
linkGroup.POST("/new", NewShortlinkCreateHandler(log, shortlinkService, host))
|
linkGroup.POST("/new", NewShortlinkCreateGinHandler(log, shortlinkService, conf.GetServiceUrl()))
|
||||||
linkGroup.GET("/:linkId", NewShortlinkResolveHandler(log, shortlinkService))
|
linkGroup.GET("/:linkId", NewShortlinkResolveHandler(log, shortlinkService))
|
||||||
|
|
||||||
grpcUnderlying := grpc.NewServer()
|
grpcUnderlying := grpc.NewServer()
|
||||||
shortlinks.RegisterShortlinksServer(
|
shortlinks.RegisterShortlinksServer(
|
||||||
grpcUnderlying,
|
grpcUnderlying,
|
||||||
NewShortlinksGrpc(log, shortlinkService, host),
|
NewShortlinksGrpc(log, shortlinkService, conf.GetServiceUrl()),
|
||||||
)
|
)
|
||||||
|
|
||||||
httpServer := httpserver.New(
|
httpServer := httpserver.New(
|
||||||
|
|||||||
@ -1,2 +0,0 @@
|
|||||||
run:
|
|
||||||
go run . -c ./conf.yml -l ./../../.run/shortlinks.log
|
|
||||||
@ -31,45 +31,22 @@ services:
|
|||||||
- POSTGRES_USER=postgres
|
- POSTGRES_USER=postgres
|
||||||
- POSTGRES_PASSWORD=postgres
|
- POSTGRES_PASSWORD=postgres
|
||||||
|
|
||||||
grafana:
|
# node_exporter:
|
||||||
image: grafana/grafana:11.1.4
|
# image: quay.io/prometheus/node-exporter:latest
|
||||||
shm_size: 256mb
|
# pid: host
|
||||||
ports:
|
# command:
|
||||||
- 3000:3000
|
# - '--path.procfs=/host/proc'
|
||||||
extra_hosts:
|
# - '--path.rootfs=/rootfs'
|
||||||
- "host.docker.internal:host-gateway"
|
# - '--path.sysfs=/host/sys'
|
||||||
volumes:
|
# - '--collector.filesystem.mount-points-exclude=^/(sys|proc|dev|host|etc)($$|/)'
|
||||||
- grafana-volume:/var/lib/grafana
|
# volumes:
|
||||||
- ./deploy/grafana-ds.yaml:/etc/grafana/provisioning/datasources/datasources.yaml
|
# - /proc:/host/proc:ro
|
||||||
|
# - /sys:/host/sys:ro
|
||||||
prometheus:
|
# - /:/rootfs:ro
|
||||||
image: prom/prometheus:v2.54.0
|
# extra_hosts:
|
||||||
shm_size: 256mb
|
# - "host.docker.internal:host-gateway"
|
||||||
user: root
|
# ports:
|
||||||
ports:
|
# - 9100:9100
|
||||||
- 9090:9090
|
|
||||||
extra_hosts:
|
|
||||||
- "host.docker.internal:host-gateway"
|
|
||||||
volumes:
|
|
||||||
- prometheus-volume:/etc/prometheus
|
|
||||||
- ./deploy/prometheus.yml:/etc/prometheus/prometheus.yml
|
|
||||||
|
|
||||||
node_exporter:
|
|
||||||
image: quay.io/prometheus/node-exporter:latest
|
|
||||||
pid: host
|
|
||||||
command:
|
|
||||||
- '--path.procfs=/host/proc'
|
|
||||||
- '--path.rootfs=/rootfs'
|
|
||||||
- '--path.sysfs=/host/sys'
|
|
||||||
- '--collector.filesystem.mount-points-exclude=^/(sys|proc|dev|host|etc)($$|/)'
|
|
||||||
volumes:
|
|
||||||
- /proc:/host/proc:ro
|
|
||||||
- /sys:/host/sys:ro
|
|
||||||
- /:/rootfs:ro
|
|
||||||
extra_hosts:
|
|
||||||
- "host.docker.internal:host-gateway"
|
|
||||||
ports:
|
|
||||||
- 9100:9100
|
|
||||||
|
|
||||||
otel-collector:
|
otel-collector:
|
||||||
image: otel/opentelemetry-collector-contrib:0.108.0
|
image: otel/opentelemetry-collector-contrib:0.108.0
|
||||||
@ -82,25 +59,6 @@ services:
|
|||||||
# - 4317:4317 # OTLP gRPC receiver
|
# - 4317:4317 # OTLP gRPC receiver
|
||||||
- 4318:4318 # OTLP http receiver
|
- 4318:4318 # OTLP http receiver
|
||||||
|
|
||||||
tempo-init:
|
|
||||||
image: &tempoImage grafana/tempo:r177-60780f7
|
|
||||||
user: root
|
|
||||||
entrypoint:
|
|
||||||
- "chown"
|
|
||||||
- "10001:10001"
|
|
||||||
- "/var/tempo"
|
|
||||||
volumes:
|
|
||||||
- tempo-volume:/var/tempo
|
|
||||||
|
|
||||||
tempo:
|
|
||||||
image: *tempoImage
|
|
||||||
command: [ "-config.file=/etc/tempo.yaml" ]
|
|
||||||
volumes:
|
|
||||||
- ./deploy/tempo.yaml:/etc/tempo.yaml
|
|
||||||
- tempo-volume:/var/tempo
|
|
||||||
depends_on:
|
|
||||||
- tempo-init
|
|
||||||
|
|
||||||
kafka:
|
kafka:
|
||||||
image: &kafkaImage apache/kafka:3.8.0
|
image: &kafkaImage apache/kafka:3.8.0
|
||||||
healthcheck:
|
healthcheck:
|
||||||
@ -133,35 +91,34 @@ services:
|
|||||||
entrypoint: >
|
entrypoint: >
|
||||||
/bin/bash -c "/opt/kafka/bin/kafka-topics.sh --bootstrap-server http://kafka:9092 --create --topic events --partitions 6"
|
/bin/bash -c "/opt/kafka/bin/kafka-topics.sh --bootstrap-server http://kafka:9092 --create --topic events --partitions 6"
|
||||||
|
|
||||||
|
# minio:
|
||||||
|
# image: quay.io/minio/minio:latest
|
||||||
|
# command: ["server", "/data", "--console-address", ":9001"]
|
||||||
|
# healthcheck:
|
||||||
|
# test: 'mc ready local'
|
||||||
|
# interval: 1s
|
||||||
|
# environment:
|
||||||
|
# MINIO_ROOT_USER: miniouser
|
||||||
|
# MINIO_ROOT_PASSWORD: miniouser
|
||||||
|
# MINIO_ACCESS_KEY: miniokey
|
||||||
|
# MINIO_SECRET_KEY: miniokey
|
||||||
|
# ports:
|
||||||
|
# - 9000:9000
|
||||||
|
# - 9001:9001
|
||||||
|
# volumes:
|
||||||
|
# - minio-volume:/data
|
||||||
|
|
||||||
minio:
|
# minio-init:
|
||||||
image: quay.io/minio/minio:latest
|
# image: quay.io/minio/mc:latest
|
||||||
command: ["server", "/data", "--console-address", ":9001"]
|
# depends_on:
|
||||||
healthcheck:
|
# - minio
|
||||||
test: 'mc ready local'
|
# entrypoint: >
|
||||||
interval: 1s
|
# /bin/sh -c "
|
||||||
environment:
|
# /usr/bin/mc alias set myminio http://minio:9000 miniouser miniouser;
|
||||||
MINIO_ROOT_USER: miniouser
|
# /usr/bin/mc mb minio/bucket;
|
||||||
MINIO_ROOT_PASSWORD: miniouser
|
# /usr/bin/mc anonymous set public minio/bucket;
|
||||||
MINIO_ACCESS_KEY: miniokey
|
# exit 0;
|
||||||
MINIO_SECRET_KEY: miniokey
|
# "
|
||||||
ports:
|
|
||||||
- 9000:9000
|
|
||||||
- 9001:9001
|
|
||||||
volumes:
|
|
||||||
- minio-volume:/data
|
|
||||||
|
|
||||||
minio-init:
|
|
||||||
image: quay.io/minio/mc:latest
|
|
||||||
depends_on:
|
|
||||||
- minio
|
|
||||||
entrypoint: >
|
|
||||||
/bin/sh -c "
|
|
||||||
/usr/bin/mc alias set myminio http://minio:9000 miniouser miniouser;
|
|
||||||
/usr/bin/mc mb minio/bucket;
|
|
||||||
/usr/bin/mc anonymous set public minio/bucket;
|
|
||||||
exit 0;
|
|
||||||
"
|
|
||||||
|
|
||||||
smtp4dev:
|
smtp4dev:
|
||||||
image: rnwood/smtp4dev:v3
|
image: rnwood/smtp4dev:v3
|
||||||
@ -179,9 +136,35 @@ services:
|
|||||||
- RelayOptions__Login=maillogin
|
- RelayOptions__Login=maillogin
|
||||||
- RelayOptions__Password=mailpass
|
- RelayOptions__Password=mailpass
|
||||||
|
|
||||||
|
backend:
|
||||||
|
build:
|
||||||
|
dockerfile: ./backend.Dockerfile
|
||||||
|
context: .
|
||||||
|
ports:
|
||||||
|
- 8080:8080
|
||||||
|
volumes:
|
||||||
|
- ./deploy/backend-config.yaml:/backend/config.yaml
|
||||||
|
- ./deploy/backend-jwt-privkey:/backend/backend-jwt-privkey
|
||||||
|
|
||||||
|
notifyer:
|
||||||
|
build:
|
||||||
|
dockerfile: ./notifyer.Dockerfile
|
||||||
|
context: .
|
||||||
|
ports:
|
||||||
|
- 8081:8081
|
||||||
|
volumes:
|
||||||
|
- ./deploy/notifyer-config.yaml:/backend/config.yaml
|
||||||
|
|
||||||
|
shortlinks:
|
||||||
|
build:
|
||||||
|
dockerfile: ./shortlinks.Dockerfile
|
||||||
|
context: .
|
||||||
|
ports:
|
||||||
|
- 8082:8082 #http
|
||||||
|
- 8083:8083 #grpc
|
||||||
|
volumes:
|
||||||
|
- ./deploy/notifyer-config.yaml:/backend/config.yaml
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
postgres-volume:
|
postgres-volume:
|
||||||
grafana-volume:
|
|
||||||
tempo-volume:
|
|
||||||
prometheus-volume:
|
|
||||||
minio-volume:
|
minio-volume:
|
||||||
5
deploy/backend-config.yaml
Normal file
5
deploy/backend-config.yaml
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
port: 8080
|
||||||
|
postgres_url: "postgres://postgres:postgres@postgres:5432/postgres"
|
||||||
|
jwt_signing_key: "./backend-jwt-privkey"
|
||||||
|
kafka_url: "kafka:9091"
|
||||||
|
kafka_topic: "events"
|
||||||
@ -1,12 +1,13 @@
|
|||||||
app:
|
app:
|
||||||
serviceUrl: "http://localhost:8080"
|
port: 8081
|
||||||
|
serviceUrl: "http://backend:8080"
|
||||||
kafka:
|
kafka:
|
||||||
brokers:
|
brokers:
|
||||||
- localhost:9091
|
- kafka:9091
|
||||||
topic: events
|
topic: events
|
||||||
consumerGroupId: notifyer-group
|
consumerGroupId: notifyer-group
|
||||||
smtp:
|
smtp:
|
||||||
server: localhost
|
server: smtp4dev
|
||||||
port: 12333
|
port: 12333
|
||||||
login: "maillogin"
|
login: "maillogin"
|
||||||
password: "mailpass"
|
password: "mailpass"
|
||||||
4
deploy/shortlinks-config.yaml
Normal file
4
deploy/shortlinks-config.yaml
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
service_url: http://shortlinks:8082
|
||||||
|
http_port: 8082
|
||||||
|
grpc_port: 8083
|
||||||
|
postgres_url: "postgres://postgres:postgres@postgres:5432/postgres"
|
||||||
80
go.mod
80
go.mod
@ -8,71 +8,47 @@ require (
|
|||||||
github.com/go-playground/validator/v10 v10.22.0
|
github.com/go-playground/validator/v10 v10.22.0
|
||||||
github.com/golang-jwt/jwt/v5 v5.2.1
|
github.com/golang-jwt/jwt/v5 v5.2.1
|
||||||
github.com/google/uuid v1.6.0
|
github.com/google/uuid v1.6.0
|
||||||
|
github.com/jackc/pgx/v5 v5.7.2
|
||||||
github.com/prometheus/client_golang v1.20.2
|
github.com/prometheus/client_golang v1.20.2
|
||||||
github.com/rs/zerolog v1.33.0
|
github.com/rs/zerolog v1.33.0
|
||||||
github.com/segmentio/kafka-go v0.4.47
|
github.com/segmentio/kafka-go v0.4.47
|
||||||
|
github.com/spf13/cobra v1.9.1
|
||||||
github.com/stretchr/testify v1.9.0
|
github.com/stretchr/testify v1.9.0
|
||||||
go.opentelemetry.io/otel v1.29.0
|
go.opentelemetry.io/otel v1.29.0
|
||||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.29.0
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.29.0
|
||||||
go.opentelemetry.io/otel/sdk v1.29.0
|
go.opentelemetry.io/otel/sdk v1.29.0
|
||||||
go.opentelemetry.io/otel/trace v1.29.0
|
go.opentelemetry.io/otel/trace v1.29.0
|
||||||
golang.org/x/crypto v0.26.0
|
golang.org/x/crypto v0.31.0
|
||||||
|
google.golang.org/grpc v1.65.0
|
||||||
|
google.golang.org/protobuf v1.34.2
|
||||||
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df
|
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df
|
||||||
gopkg.in/yaml.v3 v3.0.1
|
gopkg.in/yaml.v3 v3.0.1
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
|
||||||
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
|
|
||||||
github.com/fsnotify/fsnotify v1.7.0 // indirect
|
|
||||||
github.com/go-logr/logr v1.4.2 // indirect
|
|
||||||
github.com/go-logr/stdr v1.2.2 // indirect
|
|
||||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0 // indirect
|
|
||||||
github.com/hashicorp/hcl v1.0.0 // indirect
|
|
||||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
|
||||||
github.com/jackc/pgpassfile v1.0.0 // indirect
|
|
||||||
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect
|
|
||||||
github.com/jackc/puddle/v2 v2.2.1 // indirect
|
|
||||||
github.com/magiconair/properties v1.8.7 // indirect
|
|
||||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
|
||||||
github.com/pierrec/lz4/v4 v4.1.15 // indirect
|
|
||||||
github.com/sagikazarmark/locafero v0.4.0 // indirect
|
|
||||||
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
|
|
||||||
github.com/sourcegraph/conc v0.3.0 // indirect
|
|
||||||
github.com/spf13/afero v1.11.0 // indirect
|
|
||||||
github.com/spf13/cast v1.6.0 // indirect
|
|
||||||
github.com/spf13/pflag v1.0.5 // indirect
|
|
||||||
github.com/spf13/viper v1.19.0 // indirect
|
|
||||||
github.com/subosito/gotenv v1.6.0 // indirect
|
|
||||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.29.0 // indirect
|
|
||||||
go.opentelemetry.io/otel/metric v1.29.0 // indirect
|
|
||||||
go.opentelemetry.io/proto/otlp v1.3.1 // indirect
|
|
||||||
go.uber.org/atomic v1.9.0 // indirect
|
|
||||||
go.uber.org/multierr v1.9.0 // indirect
|
|
||||||
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect
|
|
||||||
golang.org/x/sync v0.8.0 // indirect
|
|
||||||
google.golang.org/genproto/googleapis/api v0.0.0-20240822170219-fc7c04adadcd // indirect
|
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240822170219-fc7c04adadcd // indirect
|
|
||||||
google.golang.org/grpc v1.65.0 // indirect
|
|
||||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
|
||||||
)
|
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/beorn7/perks v1.0.1 // indirect
|
github.com/beorn7/perks v1.0.1 // indirect
|
||||||
github.com/bytedance/sonic v1.12.1 // indirect
|
github.com/bytedance/sonic v1.11.6 // indirect
|
||||||
github.com/bytedance/sonic/loader v0.2.0 // indirect
|
github.com/bytedance/sonic/loader v0.1.1 // indirect
|
||||||
|
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
|
||||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||||
github.com/cloudwego/base64x v0.1.4 // indirect
|
github.com/cloudwego/base64x v0.1.4 // indirect
|
||||||
github.com/cloudwego/iasm v0.2.0 // indirect
|
github.com/cloudwego/iasm v0.2.0 // indirect
|
||||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||||
github.com/gabriel-vasile/mimetype v1.4.5 // indirect
|
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
|
||||||
github.com/gin-contrib/sse v0.1.0 // indirect
|
github.com/gin-contrib/sse v0.1.0 // indirect
|
||||||
|
github.com/go-logr/logr v1.4.2 // indirect
|
||||||
|
github.com/go-logr/stdr v1.2.2 // indirect
|
||||||
github.com/go-playground/locales v0.14.1 // indirect
|
github.com/go-playground/locales v0.14.1 // indirect
|
||||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||||
github.com/goccy/go-json v0.10.3 // indirect
|
github.com/goccy/go-json v0.10.2 // indirect
|
||||||
github.com/jackc/pgx/v5 v5.6.0
|
github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0 // indirect
|
||||||
|
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||||
|
github.com/jackc/pgpassfile v1.0.0 // indirect
|
||||||
|
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
|
||||||
|
github.com/jackc/puddle/v2 v2.2.2 // indirect
|
||||||
github.com/json-iterator/go v1.1.12 // indirect
|
github.com/json-iterator/go v1.1.12 // indirect
|
||||||
github.com/klauspost/compress v1.17.9 // indirect
|
github.com/klauspost/compress v1.17.9 // indirect
|
||||||
github.com/klauspost/cpuid/v2 v2.2.8 // indirect
|
github.com/klauspost/cpuid/v2 v2.2.7 // indirect
|
||||||
github.com/leodido/go-urn v1.4.0 // indirect
|
github.com/leodido/go-urn v1.4.0 // indirect
|
||||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||||
@ -80,17 +56,23 @@ require (
|
|||||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||||
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
|
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
|
||||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
github.com/pierrec/lz4/v4 v4.1.15 // indirect
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
github.com/prometheus/client_model v0.6.1 // indirect
|
github.com/prometheus/client_model v0.6.1 // indirect
|
||||||
github.com/prometheus/common v0.55.0 // indirect
|
github.com/prometheus/common v0.55.0 // indirect
|
||||||
github.com/prometheus/procfs v0.15.1 // indirect
|
github.com/prometheus/procfs v0.15.1 // indirect
|
||||||
github.com/spf13/cobra v1.8.1
|
github.com/spf13/pflag v1.0.6 // indirect
|
||||||
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||||
github.com/ugorji/go/codec v1.2.12 // indirect
|
github.com/ugorji/go/codec v1.2.12 // indirect
|
||||||
golang.org/x/arch v0.9.0 // indirect
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.29.0 // indirect
|
||||||
|
go.opentelemetry.io/otel/metric v1.29.0 // indirect
|
||||||
|
go.opentelemetry.io/proto/otlp v1.3.1 // indirect
|
||||||
|
golang.org/x/arch v0.8.0 // indirect
|
||||||
golang.org/x/net v0.28.0 // indirect
|
golang.org/x/net v0.28.0 // indirect
|
||||||
golang.org/x/sys v0.24.0 // indirect
|
golang.org/x/sync v0.10.0 // indirect
|
||||||
golang.org/x/text v0.17.0 // indirect
|
golang.org/x/sys v0.28.0 // indirect
|
||||||
google.golang.org/protobuf v1.34.2 // indirect
|
golang.org/x/text v0.21.0 // indirect
|
||||||
|
google.golang.org/genproto/googleapis/api v0.0.0-20240822170219-fc7c04adadcd // indirect
|
||||||
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20240822170219-fc7c04adadcd // indirect
|
||||||
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
|
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
|
||||||
)
|
)
|
||||||
|
|||||||
97
go.sum
97
go.sum
@ -2,11 +2,10 @@ github.com/akamensky/argparse v1.4.0 h1:YGzvsTqCvbEZhL8zZu2AiA5nq805NZh75JNj4ajn
|
|||||||
github.com/akamensky/argparse v1.4.0/go.mod h1:S5kwC7IuDcEr5VeXtGPRVZ5o/FdhcMlQz4IZQuw64xA=
|
github.com/akamensky/argparse v1.4.0/go.mod h1:S5kwC7IuDcEr5VeXtGPRVZ5o/FdhcMlQz4IZQuw64xA=
|
||||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||||
github.com/bytedance/sonic v1.12.1 h1:jWl5Qz1fy7X1ioY74WqO0KjAMtAGQs4sYnjiEBiyX24=
|
github.com/bytedance/sonic v1.11.6 h1:oUp34TzMlL+OY1OUWxHqsdkgC/Zfc85zGqw9siXjrc0=
|
||||||
github.com/bytedance/sonic v1.12.1/go.mod h1:B8Gt/XvtZ3Fqj+iSKMypzymZxw/FVwgIGKzMzT9r/rk=
|
github.com/bytedance/sonic v1.11.6/go.mod h1:LysEHSvpvDySVdC2f87zGWf6CIKJcAvqab1ZaiQtds4=
|
||||||
|
github.com/bytedance/sonic/loader v0.1.1 h1:c+e5Pt1k/cy5wMveRDyk2X4B9hF4g7an8N3zCYjJFNM=
|
||||||
github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
|
github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
|
||||||
github.com/bytedance/sonic/loader v0.2.0 h1:zNprn+lsIP06C/IqCHs3gPQIvnvpKbbxyXQP1iU4kWM=
|
|
||||||
github.com/bytedance/sonic/loader v0.2.0/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
|
|
||||||
github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
|
github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
|
||||||
github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
|
github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
|
||||||
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||||
@ -16,16 +15,12 @@ github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJ
|
|||||||
github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg=
|
github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg=
|
||||||
github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY=
|
github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY=
|
||||||
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0=
|
||||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk=
|
||||||
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
|
|
||||||
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
|
|
||||||
github.com/gabriel-vasile/mimetype v1.4.5 h1:J7wGKdGu33ocBOhGy0z653k/lFKLFDPJMG8Gql0kxn4=
|
|
||||||
github.com/gabriel-vasile/mimetype v1.4.5/go.mod h1:ibHel+/kbxn9x2407k1izTA1S81ku1z/DlgOW2QE0M4=
|
|
||||||
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
|
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
|
||||||
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
|
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
|
||||||
github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU=
|
github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU=
|
||||||
@ -43,8 +38,8 @@ github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJn
|
|||||||
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
||||||
github.com/go-playground/validator/v10 v10.22.0 h1:k6HsTZ0sTnROkhS//R0O+55JgM8C4Bx7ia+JlgcnOao=
|
github.com/go-playground/validator/v10 v10.22.0 h1:k6HsTZ0sTnROkhS//R0O+55JgM8C4Bx7ia+JlgcnOao=
|
||||||
github.com/go-playground/validator/v10 v10.22.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
|
github.com/go-playground/validator/v10 v10.22.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
|
||||||
github.com/goccy/go-json v0.10.3 h1:KZ5WoDbxAIgm2HNbYckL0se1fHD6rz5j4ywS6ebzDqA=
|
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
|
||||||
github.com/goccy/go-json v0.10.3/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
|
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
||||||
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||||
github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk=
|
github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk=
|
||||||
github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
|
github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
|
||||||
@ -55,26 +50,24 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
|||||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0 h1:asbCHRVmodnJTuQ3qamDwqVOIjwqUPTYmYuemVOx+Ys=
|
github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0 h1:asbCHRVmodnJTuQ3qamDwqVOIjwqUPTYmYuemVOx+Ys=
|
||||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0/go.mod h1:ggCgvZ2r7uOoQjOyu2Y1NhHmEPPzzuhWgcza5M1Ji1I=
|
github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0/go.mod h1:ggCgvZ2r7uOoQjOyu2Y1NhHmEPPzzuhWgcza5M1Ji1I=
|
||||||
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
|
|
||||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
|
||||||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||||
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||||
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
|
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
|
||||||
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
|
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
|
||||||
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk=
|
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo=
|
||||||
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
|
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
|
||||||
github.com/jackc/pgx/v5 v5.6.0 h1:SWJzexBzPL5jb0GEsrPMLIsi/3jOo7RHlzTjcAeDrPY=
|
github.com/jackc/pgx/v5 v5.7.2 h1:mLoDLV6sonKlvjIEsV56SkWNCnuNv531l94GaIzO+XI=
|
||||||
github.com/jackc/pgx/v5 v5.6.0/go.mod h1:DNZ/vlrUnhWCoFGxHAG8U2ljioxukquj7utPDgtQdTw=
|
github.com/jackc/pgx/v5 v5.7.2/go.mod h1:ncY89UGWxg82EykZUwSpUKEfccBGGYq1xjrOpsbsfGQ=
|
||||||
github.com/jackc/puddle/v2 v2.2.1 h1:RhxXJtFG022u4ibrCSMSiu5aOq1i77R3OHKNJj77OAk=
|
github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo=
|
||||||
github.com/jackc/puddle/v2 v2.2.1/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
|
github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
|
||||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||||
github.com/klauspost/compress v1.15.9/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU=
|
github.com/klauspost/compress v1.15.9/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU=
|
||||||
github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA=
|
github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA=
|
||||||
github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
|
github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
|
||||||
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||||
github.com/klauspost/cpuid/v2 v2.2.8 h1:+StwCXwm9PdpiEkPyzBXIy+M9KUb4ODm0Zarf1kS5BM=
|
github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM=
|
||||||
github.com/klauspost/cpuid/v2 v2.2.8/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
|
github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
|
||||||
github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
|
github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
|
||||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||||
@ -84,16 +77,12 @@ github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0
|
|||||||
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
|
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
|
||||||
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
|
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
|
||||||
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
|
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
|
||||||
github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
|
|
||||||
github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
|
|
||||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
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-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.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||||
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||||
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
|
||||||
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
|
||||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||||
@ -108,8 +97,6 @@ github.com/pierrec/lz4/v4 v4.1.15/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFu
|
|||||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
|
||||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
|
||||||
github.com/prometheus/client_golang v1.20.2 h1:5ctymQzZlyOON1666svgwn3s6IKWgfbjsejTMiXIyjg=
|
github.com/prometheus/client_golang v1.20.2 h1:5ctymQzZlyOON1666svgwn3s6IKWgfbjsejTMiXIyjg=
|
||||||
github.com/prometheus/client_golang v1.20.2/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE=
|
github.com/prometheus/client_golang v1.20.2/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE=
|
||||||
github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
|
github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
|
||||||
@ -124,24 +111,12 @@ github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
|
|||||||
github.com/rs/zerolog v1.33.0 h1:1cU2KZkvPxNyfgEmhHAz/1A9Bz+llsdYzklWFzgp0r8=
|
github.com/rs/zerolog v1.33.0 h1:1cU2KZkvPxNyfgEmhHAz/1A9Bz+llsdYzklWFzgp0r8=
|
||||||
github.com/rs/zerolog v1.33.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss=
|
github.com/rs/zerolog v1.33.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss=
|
||||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||||
github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ=
|
|
||||||
github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4=
|
|
||||||
github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE=
|
|
||||||
github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ=
|
|
||||||
github.com/segmentio/kafka-go v0.4.47 h1:IqziR4pA3vrZq7YdRxaT3w1/5fvIH5qpCwstUanQQB0=
|
github.com/segmentio/kafka-go v0.4.47 h1:IqziR4pA3vrZq7YdRxaT3w1/5fvIH5qpCwstUanQQB0=
|
||||||
github.com/segmentio/kafka-go v0.4.47/go.mod h1:HjF6XbOKh0Pjlkr5GVZxt6CsjjwnmhVOfURM5KMd8qg=
|
github.com/segmentio/kafka-go v0.4.47/go.mod h1:HjF6XbOKh0Pjlkr5GVZxt6CsjjwnmhVOfURM5KMd8qg=
|
||||||
github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo=
|
github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo=
|
||||||
github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0=
|
github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0=
|
||||||
github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8=
|
github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
|
||||||
github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY=
|
github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||||
github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0=
|
|
||||||
github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
|
|
||||||
github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM=
|
|
||||||
github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y=
|
|
||||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
|
||||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
|
||||||
github.com/spf13/viper v1.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI=
|
|
||||||
github.com/spf13/viper v1.19.0/go.mod h1:GQUN9bilAbhU/jgc1bKs99f/suXKeUMct8Adx5+Ntkg=
|
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
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.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||||
@ -154,8 +129,6 @@ github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o
|
|||||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||||
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
|
|
||||||
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
|
|
||||||
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
|
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
|
||||||
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
|
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
|
||||||
github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE=
|
github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE=
|
||||||
@ -181,19 +154,14 @@ go.opentelemetry.io/otel/trace v1.29.0 h1:J/8ZNK4XgR7a21DZUAsbF8pZ5Jcw1VhACmnYt3
|
|||||||
go.opentelemetry.io/otel/trace v1.29.0/go.mod h1:eHl3w0sp3paPkYstJOmAimxhiFXPg+MMTlEh3nsQgWQ=
|
go.opentelemetry.io/otel/trace v1.29.0/go.mod h1:eHl3w0sp3paPkYstJOmAimxhiFXPg+MMTlEh3nsQgWQ=
|
||||||
go.opentelemetry.io/proto/otlp v1.3.1 h1:TrMUixzpM0yuc/znrFTP9MMRh8trP93mkCiDVeXrui0=
|
go.opentelemetry.io/proto/otlp v1.3.1 h1:TrMUixzpM0yuc/znrFTP9MMRh8trP93mkCiDVeXrui0=
|
||||||
go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR0XqQ7niQU+8=
|
go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR0XqQ7niQU+8=
|
||||||
go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE=
|
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
|
||||||
go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
golang.org/x/arch v0.8.0 h1:3wRIsP3pM4yUptoR96otTUOXI367OS0+c9eeRi9doIc=
|
||||||
go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI=
|
golang.org/x/arch v0.8.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
|
||||||
go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ=
|
|
||||||
golang.org/x/arch v0.9.0 h1:ub9TgUInamJ8mrZIGlBG6/4TqWeMszd4N8lNorbrr6k=
|
|
||||||
golang.org/x/arch v0.9.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
|
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
|
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
|
||||||
golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw=
|
golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U=
|
||||||
golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54=
|
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
|
||||||
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g=
|
|
||||||
golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k=
|
|
||||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
@ -207,8 +175,8 @@ golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg=
|
|||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
|
golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
|
||||||
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
@ -220,8 +188,8 @@ golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|||||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg=
|
golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
|
||||||
golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||||
@ -234,8 +202,8 @@ golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
|
|||||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||||
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||||
golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc=
|
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
|
||||||
golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
|
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||||
@ -256,9 +224,8 @@ gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntN
|
|||||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||||
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df h1:n7WqCuqOuCbNr617RXOY0AWRXxgwEyPp2z+p0+hgMuE=
|
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df h1:n7WqCuqOuCbNr617RXOY0AWRXxgwEyPp2z+p0+hgMuE=
|
||||||
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df/go.mod h1:LRQQ+SO6ZHR7tOkpBDuZnXENFzX8qRjMDMyPD6BRkCw=
|
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df/go.mod h1:LRQQ+SO6ZHR7tOkpBDuZnXENFzX8qRjMDMyPD6BRkCw=
|
||||||
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
|
|
||||||
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50=
|
nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50=
|
||||||
|
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
|
||||||
|
|||||||
@ -35,7 +35,7 @@ func (e *EventRepo) sendEmail(ctx context.Context, email, actionToken, eventType
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return e.kafka.SendMessage(ctx, eventType, valueBytes)
|
return e.kafka.PushMessage(ctx, eventType, valueBytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *EventRepo) SendEmailPasswordChanged(ctx context.Context, email string) error {
|
func (e *EventRepo) SendEmailPasswordChanged(ctx context.Context, email string) error {
|
||||||
|
|||||||
@ -35,8 +35,8 @@ func (u *shortlinkRepo) AddShortlink(ctx context.Context, dto ShortlinkDTO) erro
|
|||||||
_, span := u.tracer.Start(ctx, "postgres::AddShortlink")
|
_, span := u.tracer.Start(ctx, "postgres::AddShortlink")
|
||||||
defer span.End()
|
defer span.End()
|
||||||
|
|
||||||
query := `insert into shortlinks (url, expires_at) values ($1, $2);`
|
query := `insert into shortlinks (id, url, expires_at) values ($1, $2, $3);`
|
||||||
_, err := u.db.ExecContext(ctx, query, dto.Url, dto.ExpiresAt)
|
_, err := u.db.ExecContext(ctx, query, dto.Id, dto.Url, dto.ExpiresAt)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -72,7 +72,7 @@ func (u *shortlinkRepo) DeleteExpiredShortlinks(ctx context.Context, limit int)
|
|||||||
where id in (
|
where id in (
|
||||||
select id
|
select id
|
||||||
from shortlinks
|
from shortlinks
|
||||||
where current_date > expiration
|
where current_date > expires_at
|
||||||
limit $1
|
limit $1
|
||||||
)
|
)
|
||||||
returning *
|
returning *
|
||||||
|
|||||||
49
internal/http_server/metrics.go
Normal file
49
internal/http_server/metrics.go
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
package httpserver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"backend/internal/integrations"
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewServerMetrics(p *integrations.Metrics) *ServerMetrics {
|
||||||
|
errors5xxCounter := p.NewCounter("server_responses_5xx", "5xx responses counter")
|
||||||
|
errors4xxCounter := p.NewCounter("server_responses_4xx", "4xx responses count")
|
||||||
|
requestsCounter := p.NewCounter("server_requests_total", "requests counter")
|
||||||
|
avgReqTimeHist := p.NewHistogram("server_requests_time", "requests time histogram")
|
||||||
|
panicsHist := p.NewHistogram("server_panics", "panics histogram metric")
|
||||||
|
|
||||||
|
return &ServerMetrics{
|
||||||
|
rpsCounter: requestsCounter,
|
||||||
|
avgReqTimeHist: avgReqTimeHist,
|
||||||
|
panicsHist: panicsHist,
|
||||||
|
errors4xxCounter: errors4xxCounter,
|
||||||
|
errors5xxCounter: errors5xxCounter,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type ServerMetrics struct {
|
||||||
|
rpsCounter integrations.Counter
|
||||||
|
avgReqTimeHist integrations.Histogram
|
||||||
|
panicsHist integrations.Histogram
|
||||||
|
errors4xxCounter integrations.Counter
|
||||||
|
errors5xxCounter integrations.Counter
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *ServerMetrics) AddRequest() {
|
||||||
|
b.rpsCounter.Inc()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *ServerMetrics) AddRequestTime(reqTime float64) {
|
||||||
|
b.avgReqTimeHist.Observe(reqTime)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *ServerMetrics) AddPanic() {
|
||||||
|
b.panicsHist.Observe(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *ServerMetrics) Add4xxError() {
|
||||||
|
b.errors4xxCounter.Inc()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *ServerMetrics) Add5xxError() {
|
||||||
|
b.errors5xxCounter.Inc()
|
||||||
|
}
|
||||||
@ -3,7 +3,6 @@ package httpserver
|
|||||||
// Modified recovery from gin, use own logger
|
// Modified recovery from gin, use own logger
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"backend/internal/integrations"
|
|
||||||
"backend/pkg/logger"
|
"backend/pkg/logger"
|
||||||
"bytes"
|
"bytes"
|
||||||
"errors"
|
"errors"
|
||||||
@ -30,12 +29,12 @@ var (
|
|||||||
slash = []byte("/")
|
slash = []byte("/")
|
||||||
)
|
)
|
||||||
|
|
||||||
func NewRecoveryMiddleware(logger logger.Logger, prometheus *integrations.Prometheus, debugMode bool) gin.HandlerFunc {
|
func NewRecoveryMiddleware(logger logger.Logger, serverMetrics *ServerMetrics, debugMode bool) gin.HandlerFunc {
|
||||||
handle := defaultHandleRecovery
|
handle := defaultHandleRecovery
|
||||||
return func(c *gin.Context) {
|
return func(c *gin.Context) {
|
||||||
defer func() {
|
defer func() {
|
||||||
if err := recover(); err != nil {
|
if err := recover(); err != nil {
|
||||||
prometheus.AddPanic()
|
serverMetrics.AddPanic()
|
||||||
|
|
||||||
// Check for a broken connection, as it is not really a
|
// Check for a broken connection, as it is not really a
|
||||||
// condition that warrants a panic stack trace.
|
// condition that warrants a panic stack trace.
|
||||||
|
|||||||
@ -1,7 +1,6 @@
|
|||||||
package httpserver
|
package httpserver
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"backend/internal/integrations"
|
|
||||||
log "backend/pkg/logger"
|
log "backend/pkg/logger"
|
||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
@ -11,10 +10,13 @@ import (
|
|||||||
"go.opentelemetry.io/otel/trace"
|
"go.opentelemetry.io/otel/trace"
|
||||||
)
|
)
|
||||||
|
|
||||||
func NewRequestLogMiddleware(logger log.Logger, tracer trace.Tracer, prometheus *integrations.Prometheus) gin.HandlerFunc {
|
func NewRequestLogMiddleware(
|
||||||
|
logger log.Logger,
|
||||||
|
tracer trace.Tracer,
|
||||||
|
serverMetrics *ServerMetrics,
|
||||||
|
) gin.HandlerFunc {
|
||||||
return func(c *gin.Context) {
|
return func(c *gin.Context) {
|
||||||
prometheus.RequestInc()
|
serverMetrics.AddRequest()
|
||||||
defer prometheus.RequestDec()
|
|
||||||
|
|
||||||
requestId := c.GetHeader("X-Request-Id")
|
requestId := c.GetHeader("X-Request-Id")
|
||||||
if requestId == "" {
|
if requestId == "" {
|
||||||
@ -34,7 +36,7 @@ func NewRequestLogMiddleware(logger log.Logger, tracer trace.Tracer, prometheus
|
|||||||
c.Next()
|
c.Next()
|
||||||
latency := time.Since(start)
|
latency := time.Since(start)
|
||||||
|
|
||||||
prometheus.AddRequestTime(float64(latency.Microseconds()))
|
serverMetrics.AddRequestTime(float64(latency.Microseconds()))
|
||||||
|
|
||||||
method := c.Request.Method
|
method := c.Request.Method
|
||||||
statusCode := c.Writer.Status()
|
statusCode := c.Writer.Status()
|
||||||
@ -49,12 +51,12 @@ func NewRequestLogMiddleware(logger log.Logger, tracer trace.Tracer, prometheus
|
|||||||
}
|
}
|
||||||
|
|
||||||
if statusCode >= 400 && statusCode < 500 {
|
if statusCode >= 400 && statusCode < 500 {
|
||||||
prometheus.Add4xxError()
|
serverMetrics.Add4xxError()
|
||||||
ctxLogger.Warning().Msg(msg)
|
ctxLogger.Warning().Msg(msg)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
prometheus.Add5xxError()
|
serverMetrics.Add5xxError()
|
||||||
ctxLogger.Error().Msg(msg)
|
ctxLogger.Error().Msg(msg)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -26,7 +26,7 @@ func NewKafka(addr, topic string) *Kafka {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (k *Kafka) SendMessage(ctx context.Context, key string, value []byte) error {
|
func (k *Kafka) PushMessage(ctx context.Context, key string, value []byte) error {
|
||||||
return k.writer.WriteMessages(
|
return k.writer.WriteMessages(
|
||||||
context.Background(),
|
context.Background(),
|
||||||
kafka.Message{
|
kafka.Message{
|
||||||
|
|||||||
@ -8,90 +8,67 @@ import (
|
|||||||
"github.com/prometheus/client_golang/prometheus/promhttp"
|
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Prometheus struct {
|
type Counter interface {
|
||||||
reg *prometheus.Registry
|
Inc()
|
||||||
rpsCounter prometheus.Counter
|
|
||||||
avgReqTimeHist prometheus.Histogram
|
|
||||||
panicsHist prometheus.Histogram
|
|
||||||
errors4xxCounter prometheus.Counter
|
|
||||||
errors5xxCounter prometheus.Counter
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewPrometheus() *Prometheus {
|
type Gauge interface {
|
||||||
reg := prometheus.NewRegistry()
|
Set(float64)
|
||||||
|
Inc()
|
||||||
|
Dec()
|
||||||
|
}
|
||||||
|
|
||||||
// Add go runtime metrics and process collectors.
|
type Histogram interface {
|
||||||
reg.MustRegister(
|
Observe(float64)
|
||||||
|
}
|
||||||
|
|
||||||
|
type Metrics struct {
|
||||||
|
registry *prometheus.Registry
|
||||||
|
registerer prometheus.Registerer
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewMetrics(prefix string) *Metrics {
|
||||||
|
registry := prometheus.NewRegistry()
|
||||||
|
registerer := prometheus.WrapRegistererWithPrefix(prefix, registry)
|
||||||
|
|
||||||
|
registerer.MustRegister(
|
||||||
collectors.NewGoCollector(),
|
collectors.NewGoCollector(),
|
||||||
collectors.NewProcessCollector(collectors.ProcessCollectorOpts{}),
|
collectors.NewProcessCollector(collectors.ProcessCollectorOpts{}),
|
||||||
)
|
)
|
||||||
|
|
||||||
errors5xxCounter := prometheus.NewCounter(
|
return &Metrics{
|
||||||
prometheus.CounterOpts{
|
registry: registry,
|
||||||
Name: "backend_errors_count_5xx",
|
registerer: registerer,
|
||||||
Help: "5xx errors count",
|
|
||||||
},
|
|
||||||
)
|
|
||||||
errors4xxCounter := prometheus.NewCounter(
|
|
||||||
prometheus.CounterOpts{
|
|
||||||
Name: "backend_errors_count_4xx",
|
|
||||||
Help: "4xx errors count",
|
|
||||||
},
|
|
||||||
)
|
|
||||||
rpsCounter := prometheus.NewCounter(
|
|
||||||
prometheus.CounterOpts{
|
|
||||||
Name: "backend_requests_per_second",
|
|
||||||
Help: "Requests per second metric",
|
|
||||||
},
|
|
||||||
)
|
|
||||||
avgReqTimeHist := prometheus.NewHistogram(
|
|
||||||
prometheus.HistogramOpts{
|
|
||||||
Name: "backend_requests_average_time",
|
|
||||||
Help: "Average time of requests",
|
|
||||||
},
|
|
||||||
)
|
|
||||||
panicsHist := prometheus.NewHistogram(
|
|
||||||
prometheus.HistogramOpts{
|
|
||||||
Name: "backend_panics",
|
|
||||||
Help: "Panics histogram metric",
|
|
||||||
},
|
|
||||||
)
|
|
||||||
reg.MustRegister(rpsCounter, avgReqTimeHist, panicsHist, errors4xxCounter, errors5xxCounter)
|
|
||||||
|
|
||||||
return &Prometheus{
|
|
||||||
panicsHist: panicsHist,
|
|
||||||
avgReqTimeHist: avgReqTimeHist,
|
|
||||||
rpsCounter: rpsCounter,
|
|
||||||
errors4xxCounter: errors4xxCounter,
|
|
||||||
errors5xxCounter: errors5xxCounter,
|
|
||||||
reg: reg,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Prometheus) GetRequestHandler() http.Handler {
|
func (m *Metrics) NewCounter(name, description string) Counter {
|
||||||
return promhttp.HandlerFor(p.reg, promhttp.HandlerOpts{Registry: p.reg})
|
collector := prometheus.NewCounter(prometheus.CounterOpts{
|
||||||
|
Name: name,
|
||||||
|
Help: description,
|
||||||
|
})
|
||||||
|
m.registerer.MustRegister(collector)
|
||||||
|
return collector
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Prometheus) RequestInc() {
|
func (m *Metrics) NewGauge(name, description string) Gauge {
|
||||||
p.rpsCounter.Inc()
|
collector := prometheus.NewGauge(prometheus.GaugeOpts{
|
||||||
|
Name: name,
|
||||||
|
Help: description,
|
||||||
|
})
|
||||||
|
m.registerer.MustRegister(collector)
|
||||||
|
return collector
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Prometheus) RequestDec() {
|
func (m *Metrics) NewHistogram(name, description string) Histogram {
|
||||||
// p.rpsGauge.Dec()
|
collector := prometheus.NewHistogram(prometheus.HistogramOpts{
|
||||||
|
Name: name,
|
||||||
|
Help: description,
|
||||||
|
})
|
||||||
|
m.registerer.MustRegister(collector)
|
||||||
|
return collector
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Prometheus) AddRequestTime(reqTime float64) {
|
func (m *Metrics) HttpHandler() http.Handler {
|
||||||
p.avgReqTimeHist.Observe(reqTime)
|
return promhttp.HandlerFor(m.registry, promhttp.HandlerOpts{Registry: m.registerer})
|
||||||
}
|
|
||||||
|
|
||||||
func (p *Prometheus) AddPanic() {
|
|
||||||
p.panicsHist.Observe(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *Prometheus) Add4xxError() {
|
|
||||||
p.errors4xxCounter.Inc()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *Prometheus) Add5xxError() {
|
|
||||||
p.errors5xxCounter.Inc()
|
|
||||||
}
|
}
|
||||||
|
|||||||
6
makefile
6
makefile
@ -14,6 +14,6 @@ grpc:
|
|||||||
# --go-grpc_out=. --go-grpc_opt=paths=source_relative \
|
# --go-grpc_out=. --go-grpc_opt=paths=source_relative \
|
||||||
# helloworld/helloworld.proto
|
# helloworld/helloworld.proto
|
||||||
|
|
||||||
run: install release
|
# run: install release
|
||||||
mkdir -p ./.run
|
# mkdir -p ./.run
|
||||||
./.build/release/backend -c ./misc/config.yaml -o ./.run/log.txt -p ./.run/cpu.pprof
|
# ./.build/release/backend -c ./misc/config.yaml -o ./.run/log.txt -p ./.run/cpu.pprof
|
||||||
47
monitoring-compose.yml
Normal file
47
monitoring-compose.yml
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
services:
|
||||||
|
grafana:
|
||||||
|
image: grafana/grafana:11.1.4
|
||||||
|
shm_size: 256mb
|
||||||
|
ports:
|
||||||
|
- 3000:3000
|
||||||
|
extra_hosts:
|
||||||
|
- "host.docker.internal:host-gateway"
|
||||||
|
volumes:
|
||||||
|
- grafana-volume:/var/lib/grafana
|
||||||
|
- ./deploy/grafana-ds.yaml:/etc/grafana/provisioning/datasources/datasources.yaml
|
||||||
|
|
||||||
|
prometheus:
|
||||||
|
image: prom/prometheus:v2.54.0
|
||||||
|
shm_size: 256mb
|
||||||
|
user: root
|
||||||
|
ports:
|
||||||
|
- 9090:9090
|
||||||
|
extra_hosts:
|
||||||
|
- "host.docker.internal:host-gateway"
|
||||||
|
volumes:
|
||||||
|
- prometheus-volume:/etc/prometheus
|
||||||
|
- ./deploy/prometheus.yml:/etc/prometheus/prometheus.yml
|
||||||
|
|
||||||
|
tempo-init:
|
||||||
|
image: &tempoImage grafana/tempo:r177-60780f7
|
||||||
|
user: root
|
||||||
|
entrypoint:
|
||||||
|
- "chown"
|
||||||
|
- "10001:10001"
|
||||||
|
- "/var/tempo"
|
||||||
|
volumes:
|
||||||
|
- tempo-volume:/var/tempo
|
||||||
|
|
||||||
|
tempo:
|
||||||
|
image: *tempoImage
|
||||||
|
command: [ "-config.file=/etc/tempo.yaml" ]
|
||||||
|
volumes:
|
||||||
|
- ./deploy/tempo.yaml:/etc/tempo.yaml
|
||||||
|
- tempo-volume:/var/tempo
|
||||||
|
depends_on:
|
||||||
|
- tempo-init
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
grafana-volume:
|
||||||
|
tempo-volume:
|
||||||
|
prometheus-volume:
|
||||||
22
notifyer.Dockerfile
Normal file
22
notifyer.Dockerfile
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
FROM golang:1.22-alpine AS builder
|
||||||
|
WORKDIR /build
|
||||||
|
|
||||||
|
COPY go.mod go.sum ./
|
||||||
|
RUN go mod download && go mod verify
|
||||||
|
|
||||||
|
COPY cmd/notifyer cmd/notifyer
|
||||||
|
COPY pkg pkg
|
||||||
|
COPY internal internal
|
||||||
|
|
||||||
|
RUN go build -ldflags "-s -w" -o ./app ./cmd/notifyer
|
||||||
|
RUN chmod +x ./app
|
||||||
|
|
||||||
|
FROM alpine:3.21.2 AS production
|
||||||
|
WORKDIR /backend
|
||||||
|
|
||||||
|
COPY --from=builder /build/app .
|
||||||
|
COPY deploy/notifyer-config.yaml ./config.yaml
|
||||||
|
|
||||||
|
EXPOSE 8081
|
||||||
|
|
||||||
|
CMD ["./app", "-c", "config.yaml"]
|
||||||
@ -4,19 +4,18 @@ WORKDIR /build
|
|||||||
COPY go.mod go.sum ./
|
COPY go.mod go.sum ./
|
||||||
RUN go mod download && go mod verify
|
RUN go mod download && go mod verify
|
||||||
|
|
||||||
COPY cmd/backend cmd/backend
|
COPY cmd/shortlinks cmd/shortlinks
|
||||||
COPY pkg pkg
|
COPY pkg pkg
|
||||||
COPY internal internal
|
COPY internal internal
|
||||||
|
|
||||||
RUN GOEXPERIMENT=boringcrypto go build -ldflags "-s -w" -o ./app ./cmd/backend
|
RUN GOEXPERIMENT=boringcrypto go build -ldflags "-s -w" -o ./app ./cmd/shortlinks
|
||||||
RUN chmod +x ./app
|
RUN chmod +x ./app
|
||||||
|
|
||||||
FROM alpine:3.21.2 AS production
|
FROM alpine:3.21.2 AS production
|
||||||
WORKDIR /backend
|
WORKDIR /backend
|
||||||
|
|
||||||
COPY --from=builder /build/app .
|
COPY --from=builder /build/app .
|
||||||
COPY cmd/backend/config.yaml .
|
COPY deploy/shortlinks-config.yaml ./config.yaml
|
||||||
COPY cmd/backend/jwt_signing_key .
|
|
||||||
|
|
||||||
EXPOSE 8080
|
EXPOSE 8080
|
||||||
|
|
||||||
@ -9,8 +9,6 @@ create table if not exists users (
|
|||||||
updated_at timestamp
|
updated_at timestamp
|
||||||
);
|
);
|
||||||
|
|
||||||
alter table users alter column active set default true;
|
|
||||||
|
|
||||||
create index if not exists idx_users_email on users(email);
|
create index if not exists idx_users_email on users(email);
|
||||||
|
|
||||||
create or replace trigger trg_user_created
|
create or replace trigger trg_user_created
|
||||||
|
|||||||
@ -1,18 +1,5 @@
|
|||||||
create table if not exists shortlinks (
|
create table if not exists shortlinks (
|
||||||
id int generated always as identity,
|
id text primary key,
|
||||||
url text not null,
|
url text not null,
|
||||||
expires_at timestamp not null,
|
expires_at timestamp not null
|
||||||
created_at timestamp,
|
|
||||||
updated_at timestamp
|
|
||||||
);
|
);
|
||||||
|
|
||||||
create or replace trigger trg_shortlink_created
|
|
||||||
before insert on shortlinks
|
|
||||||
for each row
|
|
||||||
execute function trg_proc_row_created();
|
|
||||||
|
|
||||||
create or replace trigger trg_shortlink_updated
|
|
||||||
before update on shortlinks
|
|
||||||
for each row
|
|
||||||
when (new is distinct from old)
|
|
||||||
execute function trg_proc_row_updated();
|
|
||||||
Loading…
x
Reference in New Issue
Block a user