Organize backend into product executable and shared Go modules

This commit is contained in:
Stephan D committed 2026-09-12 14:14:08 +02:00
1 parent 981e84a4b4
commit f0720ec66b
382 files changed
+1944 -6989

No files matched your search

+10
View File
@@ -0,0 +1,10 @@
package mlogger
import (
lt "git.sendico.io/tech/sxchnge/shared/logging"
li "git.sendico.io/tech/sxchnge/shared/logging/internal"
)
func NewLogger(debug bool) lt.Logger {
return li.NewLoggerImp(debug)
}
+23
View File
@@ -0,0 +1,23 @@
package mzap
import (
"git.sendico.io/tech/sxchnge/shared/logging/mask"
"go.mongodb.org/mongo-driver/v2/bson"
"go.uber.org/zap"
)
func AccRef(accountRef bson.ObjectID) zap.Field {
return ObjRef("account_ref", accountRef)
}
func Email(email string) zap.Field {
return MaskEmail("email", email)
}
func MaskEmail(field, email string) zap.Field {
return zap.String(field, mask.Email(email))
}
func MaskEmails(field string, emails []string) zap.Field {
return zap.Strings(field, mask.Emails(emails))
}
+14
View File
@@ -0,0 +1,14 @@
package mzap
import (
"reflect"
"go.uber.org/zap"
)
func ErrorType(err error) zap.Field {
if err == nil {
return zap.String("error_type", "")
}
return zap.String("error_type", reflect.TypeOf(err).String())
}
+15
View File
@@ -0,0 +1,15 @@
package mzap
import (
"git.sendico.io/tech/sxchnge/shared/database/record"
"go.mongodb.org/mongo-driver/v2/bson"
"go.uber.org/zap"
)
func ObjRef(name string, objRef bson.ObjectID) zap.Field {
return zap.String(name, objRef.Hex())
}
func StorableRef(obj storable.Storable) zap.Field {
return ObjRef(obj.Collection()+"_ref", *obj.GetID())
}
@@ -0,0 +1,23 @@
package mlogger
import (
"git.sendico.io/tech/sxchnge/shared/logging"
"github.com/mattn/go-colorable"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
)
func NewLoggerImp(debug bool) mlogger.Logger {
if debug {
dc := zap.NewDevelopmentEncoderConfig()
dc.EncodeLevel = zapcore.CapitalColorLevelEncoder
logger := zap.New(zapcore.NewCore(
zapcore.NewConsoleEncoder(dc),
zapcore.AddSync(colorable.NewColorableStdout()),
zapcore.DebugLevel,
))
return logger
}
return zap.Must(zap.NewProduction())
}
+119
View File
@@ -0,0 +1,119 @@
package mlogger
import (
"fmt"
"strings"
"go.uber.org/zap"
)
type Logger = *zap.Logger
// Named appends logger name components while avoiding adjacent or already-present
// name stutter such as "notification.notification" or "vault.vault.kv".
func Named(logger Logger, names ...any) Logger {
if logger == nil {
logger = zap.NewNop()
}
current := splitName(logger.Name())
for _, name := range names {
if name == nil {
continue
}
parts := splitName(fmt.Sprint(name))
if len(parts) == 0 {
continue
}
if containsParts(current, parts) {
continue
}
parts = trimOverlappingPrefix(current, parts)
parts = trimStutteringPrefix(current, parts)
if len(parts) == 0 {
continue
}
child := strings.Join(parts, ".")
logger = logger.Named(child)
current = append(current, parts...)
}
return logger
}
func splitName(name string) []string {
raw := strings.Split(name, ".")
parts := make([]string, 0, len(raw))
for _, part := range raw {
part = strings.TrimSpace(part)
if part == "" {
continue
}
parts = append(parts, part)
}
return parts
}
func containsParts(parent, child []string) bool {
if len(parent) == 0 || len(child) == 0 || len(child) > len(parent) {
return false
}
for i := 0; i <= len(parent)-len(child); i++ {
matches := true
for j := range child {
if parent[i+j] != child[j] {
matches = false
break
}
}
if matches {
return true
}
}
return false
}
func trimOverlappingPrefix(parent, child []string) []string {
max := len(parent)
if len(child) < max {
max = len(child)
}
for n := max; n > 0; n-- {
matches := true
for i := 0; i < n; i++ {
if parent[len(parent)-n+i] != child[i] {
matches = false
break
}
}
if matches {
return child[n:]
}
}
return child
}
func trimStutteringPrefix(parent, child []string) []string {
if len(parent) == 0 || len(child) == 0 {
return child
}
last := parent[len(parent)-1]
first := child[0]
for _, separator := range []string{"_", "-"} {
prefix := last + separator
if !strings.HasPrefix(first, prefix) {
continue
}
trimmed := strings.TrimSpace(strings.TrimPrefix(first, prefix))
if trimmed == "" {
return child[1:]
}
out := append([]string{trimmed}, child[1:]...)
return out
}
return child
}
+71
View File
@@ -0,0 +1,71 @@
package mlogger
import (
"testing"
"github.com/stretchr/testify/require"
"go.uber.org/zap"
"go.uber.org/zap/zaptest/observer"
)
func TestNamedAvoidsLoggerNameStutter(t *testing.T) {
t.Parallel()
tests := []struct {
name string
base string
add []any
want string
}{
{
name: "skips duplicate service name",
base: "notification",
add: []any{"notification"},
want: "notification",
},
{
name: "overlaps dotted component",
base: "vault",
add: []any{"vault.kv"},
want: "vault.kv",
},
{
name: "drops child prefix stutter",
base: "server.client",
add: []any{"client_v2"},
want: "server.client.v2",
},
{
name: "keeps already present module name",
base: "payments.quotation.server",
add: []any{"payments.quotation"},
want: "payments.quotation.server",
},
{
name: "extends partial overlap",
base: "payments",
add: []any{"payments.quotation"},
want: "payments.quotation",
},
{
name: "keeps unrelated names",
base: "gateway.server",
add: []any{"chain", "storage.mongo"},
want: "gateway.server.chain.storage.mongo",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
observed, logs := observer.New(zap.InfoLevel)
logger := Named(zap.New(observed).Named(tt.base), tt.add...)
logger.Info("test")
entries := logs.All()
require.Len(t, entries, 1)
require.Equal(t, tt.want, entries[0].LoggerName)
})
}
}
+29
View File
@@ -0,0 +1,29 @@
package mask
import "strings"
func Email(email string) string {
parts := strings.Split(email, "@")
if len(parts) != 2 {
return email
}
local := parts[0]
if len(local) > 2 {
local = local[:1] + "***" + local[len(local)-1:]
} else {
local = local[:1] + "***"
}
return local + "@" + parts[1]
}
func Emails(emails []string) []string {
if emails == nil {
return nil
}
masked := make([]string, len(emails))
for i, email := range emails {
masked[i] = Email(email)
}
return masked
}
+21
View File
@@ -0,0 +1,21 @@
package mask
import (
"reflect"
"testing"
)
func TestEmailsMasksEachEmail(t *testing.T) {
got := Emails([]string{"alice@example.com", "bo@example.org", "invalid"})
want := []string{"a***e@example.com", "b***@example.org", "invalid"}
if !reflect.DeepEqual(got, want) {
t.Fatalf("Emails() = %#v, want %#v", got, want)
}
}
func TestEmailsPreservesNil(t *testing.T) {
if got := Emails(nil); got != nil {
t.Fatalf("Emails(nil) = %#v, want nil", got)
}
}