diff --git a/Dockerfile b/Dockerfile index 6c4034c..164e00f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -4,7 +4,7 @@ ARG app_dir="/home/go/app" # * Building the application -FROM golang:1.23.6-alpine AS build +FROM golang:1.25-alpine3.22 AS build ARG app_dir ARG build_string=pretendo.friends.docker @@ -21,7 +21,7 @@ RUN --mount=type=cache,target=/go/pkg/mod/ \ # * Running the final application -FROM alpine:3.20 AS final +FROM alpine:3.22 AS final ARG app_dir WORKDIR ${app_dir} diff --git a/README.md b/README.md index f9c4718..8a77f5a 100644 --- a/README.md +++ b/README.md @@ -84,15 +84,18 @@ All configuration options are handled via environment variables `.env` files are supported -| Name | Description | Required | -|---------------------------------------------|------------------------------------------------------------------------------------------------------------------------|-------------------------------------| -| `PN_FRIENDS_CONFIG_DATABASE_URI` | Fully qualified URI to your Postgres server (Example `postgres://username:password@localhost/friends?sslmode=disable`) | Yes | -| `PN_FRIENDS_CONFIG_AES_KEY` | AES key used in tokens provided by the account server | Yes | -| `PN_FRIENDS_CONFIG_GRPC_API_KEY` | API key for your GRPC server | No (Assumed to be an open gRPC API) | -| `PN_FRIENDS_GRPC_SERVER_PORT` | Port for the GRPC server | Yes | -| `PN_FRIENDS_AUTHENTICATION_SERVER_PORT` | Port for the authentication server | Yes | -| `PN_FRIENDS_SECURE_SERVER_HOST` | Host name for the secure server (should point to the same address as the authentication server) | Yes | -| `PN_FRIENDS_SECURE_SERVER_PORT` | Port for the secure server | Yes | -| `PN_FRIENDS_ACCOUNT_GRPC_HOST` | Host name for your account server gRPC service | Yes | -| `PN_FRIENDS_ACCOUNT_GRPC_PORT` | Port for your account server gRPC service | Yes | -| `PN_FRIENDS_ACCOUNT_GRPC_API_KEY` | API key for your account server gRPC service | No (Assumed to be an open gRPC API) | +| Name | Description | Required | +| ---------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------- | ----------------------------------- | +| `PN_FRIENDS_CONFIG_POSTGRES_URI` | Fully qualified URI to your Postgres server (Example `postgres://username:password@localhost/friends?sslmode=disable`) | Yes | +| `PN_FRIENDS_CONFIG_POSTGRES_MAX_CONNECTIONS` | Postgres server max connections | Yes | +| `PN_FRIENDS_CONFIG_AES_KEY` | AES key used in tokens provided by the account server | Yes | +| `PN_FRIENDS_CONFIG_GRPC_API_KEY` | API key for your GRPC server | No (Assumed to be an open gRPC API) | +| `PN_FRIENDS_CONFIG_GRPC_SERVER_PORT` | Port for the GRPC server | Yes | +| `PN_FRIENDS_CONFIG_AUTHENTICATION_SERVER_PORT` | Port for the authentication server | Yes | +| `PN_FRIENDS_CONFIG_SECURE_SERVER_HOST` | Host name for the secure server (should point to the same address as the authentication server) | Yes | +| `PN_FRIENDS_CONFIG_SECURE_SERVER_PORT` | Port for the secure server | Yes | +| `PN_FRIENDS_CONFIG_ACCOUNT_GRPC_HOST` | Host name for your account server gRPC service | Yes | +| `PN_FRIENDS_CONFIG_ACCOUNT_GRPC_PORT` | Port for your account server gRPC service | Yes | +| `PN_FRIENDS_CONFIG_ACCOUNT_GRPC_API_KEY` | API key for your account server gRPC service | No (Assumed to be an open gRPC API) | +| `PN_FRIENDS_CONFIG_HEALTH_CHECK_PORT` | Port for the basic UDP health check server | No | +| `PN_FRIENDS_CONFIG_ENABLE_BELLA` | Enables a debug user named "Bella" which is always assigned as your friend | No | No diff --git a/database/connect_postgres.go b/database/connect_postgres.go index 7b9371d..c2e7a38 100644 --- a/database/connect_postgres.go +++ b/database/connect_postgres.go @@ -1,8 +1,6 @@ package database import ( - "os" - _ "github.com/lib/pq" "github.com/PretendoNetwork/friends/globals" @@ -14,7 +12,7 @@ var Manager *sqlmanager.SQLManager func ConnectPostgres() { var err error - Manager, err = sqlmanager.NewSQLManager("postgres", os.Getenv("PN_FRIENDS_CONFIG_DATABASE_URI"), int64(globals.DatabaseMaxConnections)) + Manager, err = sqlmanager.NewSQLManager("postgres", globals.Config.PostgresURI, globals.Config.PostgresMaxConnections) if err != nil { globals.Logger.Critical(err.Error()) } diff --git a/globals/account_details_by_pid.go b/globals/account_details_by_pid.go index 7fe8399..b2048b2 100644 --- a/globals/account_details_by_pid.go +++ b/globals/account_details_by_pid.go @@ -4,7 +4,7 @@ import ( "context" "strconv" - pb "github.com/PretendoNetwork/grpc-go/account" + pb "github.com/PretendoNetwork/grpc/go/account/v2" "github.com/PretendoNetwork/nex-go/v2" "github.com/PretendoNetwork/nex-go/v2/types" "google.golang.org/grpc/metadata" @@ -32,7 +32,7 @@ func AccountDetailsByPID(pid types.PID) (*nex.Account, *nex.Error) { } username := strconv.Itoa(int(pid)) - account := nex.NewAccount(pid, username, response.Password) + account := nex.NewAccount(pid, username, response.Password, false) return account, nil } diff --git a/globals/account_details_by_username.go b/globals/account_details_by_username.go index f8a19dd..2095e3a 100644 --- a/globals/account_details_by_username.go +++ b/globals/account_details_by_username.go @@ -5,7 +5,7 @@ import ( "fmt" "strconv" - pb "github.com/PretendoNetwork/grpc-go/account" + pb "github.com/PretendoNetwork/grpc/go/account/v2" "github.com/PretendoNetwork/nex-go/v2" "github.com/PretendoNetwork/nex-go/v2/types" "google.golang.org/grpc/metadata" @@ -44,7 +44,7 @@ func AccountDetailsByUsername(username string) (*nex.Account, *nex.Error) { return nil, nex.NewError(nex.ResultCodes.RendezVous.InvalidPID, "Invalid PID") } - account := nex.NewAccount(types.NewPID(uint64(pid)), username, response.Password) + account := nex.NewAccount(types.NewPID(uint64(pid)), username, response.Password, false) return account, nil } diff --git a/globals/config.go b/globals/config.go new file mode 100644 index 0000000..8203bc8 --- /dev/null +++ b/globals/config.go @@ -0,0 +1,19 @@ +package globals + +type config struct { + PostgresURI string + PostgresMaxConnections int64 + AESKey string + GRPCAPIKey string `envconf:"optional"` + GRPCServerPort uint16 + AuthenticationServerPort uint16 + SecureServerHost string + SecureServerPort uint16 + AccountGRPCHost string + AccountGRPCPort uint16 + AccountGRPCAPIKey string `envconf:"optional"` + HealthCheckPort uint16 `envconf:"optional"` + EnableBella bool `envconf:"optional"` +} + +var Config *config = &config{} diff --git a/globals/config_parser.go b/globals/config_parser.go new file mode 100644 index 0000000..dd5260a --- /dev/null +++ b/globals/config_parser.go @@ -0,0 +1,308 @@ +package globals + +// * NOTE: THIS IS ALL LIBRARY CODE, INTENDED TO BE REMOVED FROM THIS REPO IN THE FUTURE. +// * THIS IS ONLY HERE FOR NOW SO I CAN PLAY AROUND WITH THE IDEA. + +// * MESSING AROUND WITH THIS BECAUSE I DIDN'T REALLY LIKE THE WAY EXISTING CONFIG +// * PARSERS WORKED, THEY ALL HAD WEIRD QUIRKS LIKE ODD SEMANTICS FOR STRUCT TAGS, +// * COULDN'T HANDLE COMPLEX SLICES AND MAPS CLEANLY, ETC. + +import ( + "encoding/json" + "fmt" + "os" + "reflect" + "strconv" + "strings" + "unicode" + "unicode/utf8" +) + +type fieldTagOptions struct { + optional bool + defaultValue string + hasDefault bool + envNameOverride string +} + +func parseFieldTag(tag string) fieldTagOptions { + options := fieldTagOptions{} + if tag == "" { + return options + } + + if i := strings.Index(tag, "default:"); i != -1 { + options.defaultValue = tag[i+8:] + options.hasDefault = true + tag = strings.TrimSuffix(tag[:i], ",") + } + + for _, part := range strings.Split(tag, ",") { + part = strings.TrimSpace(part) + if part == "" { + continue + } + + if part == "optional" { + options.optional = true + } else if strings.HasPrefix(part, "env:") { + options.envNameOverride = strings.TrimPrefix(part, "env:") + } + } + + return options +} + +type ConfigParser[T any] struct { + config T + prefix string + initialisms map[string]bool + allowedPluralInitialisms map[string]bool +} + +func (cp *ConfigParser[T]) toPascalCase(str string) string { + lowercase := strings.ToLower(str) + words := strings.Split(lowercase, "_") + + var pascalCase strings.Builder + + for _, word := range words { + pascalCase.WriteString(cp.capitalizeWord(word)) + } + + return pascalCase.String() +} + +func (cp *ConfigParser[T]) capitalizeWord(word string) string { + if word == "" { + return word + } + + upper := strings.ToUpper(word) + + if cp.initialisms[upper] { + return upper + } + + endsWithS := strings.HasSuffix(upper, "S") + withoutS := strings.TrimSuffix(upper, "S") + + if cp.initialisms[withoutS] && endsWithS && cp.allowedPluralInitialisms[withoutS] { + return withoutS + "s" + } else if cp.initialisms[withoutS] { + return upper + } + + r, n := utf8.DecodeRuneInString(word) + if r == utf8.RuneError && n == 0 { + return word + } + + return string(unicode.ToUpper(r)) + word[n:] +} + +func (cp *ConfigParser[T]) toEnvVarName(fieldName string) string { + var result strings.Builder + if cp.prefix != "" { + result.WriteString(cp.prefix) + } + + runes := []rune(fieldName) + for i := 0; i < len(runes); i++ { + r := runes[i] + + if r == 's' && i > 0 && unicode.IsUpper(runes[i-1]) { + result.WriteRune('S') + continue + } + + if i > 0 && unicode.IsUpper(r) { + prevIsLower := unicode.IsLower(runes[i-1]) + nextIsLower := i+1 < len(runes) && unicode.IsLower(runes[i+1]) + nextIsPluralS := i+1 < len(runes) && runes[i+1] == 's' && (i+2 >= len(runes) || unicode.IsUpper(runes[i+2])) + + if prevIsLower || (nextIsLower && !nextIsPluralS) { + result.WriteRune('_') + } + } + + result.WriteRune(unicode.ToUpper(r)) + } + + return result.String() +} + +func (cp *ConfigParser[T]) SetPrefix(prefix string) *ConfigParser[T] { + cp.prefix = prefix + "_" + + return cp +} + +func (cp *ConfigParser[T]) AddInitialisms(initialisms map[string]bool) *ConfigParser[T] { + for key, value := range initialisms { + cp.initialisms[key] = value + } + + return cp +} + +func (cp *ConfigParser[T]) ParseFromEnv() T { + envAsPascal := make(map[string]string) + + for _, env := range os.Environ() { + pair := strings.SplitN(env, "=", 2) + key := pair[0] + value := strings.TrimSpace(pair[1]) + + if !strings.HasPrefix(key, cp.prefix) { + continue + } + + fieldName := cp.toPascalCase(strings.TrimPrefix(key, cp.prefix)) + + envAsPascal[fieldName] = value + } + + v := reflect.ValueOf(cp.config).Elem() + t := v.Type() + + for i := 0; i < v.NumField(); i++ { + field := t.Field(i) + fieldName := field.Name + fieldValue := v.Field(i) + fieldOptions := parseFieldTag(field.Tag.Get("envconf")) + errors := make([]string, 0) + warnings := make([]string, 0) + + envVarName := cp.toEnvVarName(fieldName) + envValue, exists := envAsPascal[fieldName] + + if fieldOptions.envNameOverride != "" { + envVarName = fieldOptions.envNameOverride + envValue, exists = os.LookupEnv(envVarName) + } + + if !exists && fieldOptions.hasDefault { + envValue = fieldOptions.defaultValue + exists = true + + warnings = append(warnings, fmt.Sprintf("Optional field %s does not have a corresponding %s environment variable. Using default value \"%s\"", fieldName, envVarName, envValue)) + } + + if exists && fieldValue.CanSet() { + fieldType := fieldValue.Type() + switch fieldValue.Kind() { + case reflect.String: + fieldValue.SetString(envValue) + case reflect.Bool: + if boolVal, err := strconv.ParseBool(envValue); err == nil { + fieldValue.SetBool(boolVal) + } + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + if intVal, err := strconv.ParseInt(envValue, 10, fieldValue.Type().Bits()); err == nil { + fieldValue.SetInt(intVal) + } else { + errors = append(errors, fmt.Sprintf("Error parsing %s: %v", envVarName, err)) + } + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + if uintVal, err := strconv.ParseUint(envValue, 10, fieldValue.Type().Bits()); err == nil { + fieldValue.SetUint(uintVal) + } else { + errors = append(errors, fmt.Sprintf("Error parsing %s: %v", envVarName, err)) + } + case reflect.Float32, reflect.Float64: + if floatVal, err := strconv.ParseFloat(envValue, fieldValue.Type().Bits()); err == nil { + fieldValue.SetFloat(floatVal) + } else { + errors = append(errors, fmt.Sprintf("Error parsing %s: %v", envVarName, err)) + } + case reflect.Slice, reflect.Map: + sliceOrMap := reflect.New(fieldType) + if err := json.Unmarshal([]byte(envValue), sliceOrMap.Interface()); err != nil { + errors = append(errors, fmt.Sprintf("Error parsing %s: %v", envVarName, err)) + } + fieldValue.Set(sliceOrMap.Elem()) + } + } else if !exists { + if !fieldOptions.optional { + errors = append(errors, fmt.Sprintf("Required field %s does not have a corresponding %s environment variable", fieldName, envVarName)) + } else if !fieldOptions.hasDefault { + warnings = append(warnings, fmt.Sprintf("Optional field %s does not have a corresponding %s environment variable and no default value. Skipping", fieldName, envVarName)) + } + } + + if len(warnings) != 0 { + for _, warning := range warnings { + fmt.Println(warning) + } + } + + if len(errors) != 0 { + for _, err := range errors { + fmt.Println(err) + } + + os.Exit(0) + } + } + + return cp.config +} + +func NewConfigParser[T any](config T) *ConfigParser[T] { + return &ConfigParser[T]{ + config: config, + initialisms: map[string]bool{ // * https://go.googlesource.com/lint/+/818c5a804067/lint.go#767 + "ACL": true, + "API": true, + "ASCII": true, + "CPU": true, + "CSS": true, + "DNS": true, + "EOF": true, + "GUID": true, + "HTML": true, + "HTTP": true, + "HTTPS": true, + "ID": true, + "IP": true, + "JSON": true, + "LHS": true, + "QPS": true, + "RAM": true, + "RHS": true, + "RPC": true, + "SLA": true, + "SMTP": true, + "SQL": true, + "SSH": true, + "TCP": true, + "TLS": true, + "TTL": true, + "UDP": true, + "UI": true, + "UID": true, + "UUID": true, + "URI": true, + "URL": true, + "UTF8": true, + "VM": true, + "XML": true, + "XMPP": true, + "XSRF": true, + "XSS": true, + "NEX": true, // * Start of our custom ones + "GRPC": true, + "AES": true, + }, + allowedPluralInitialisms: map[string]bool{ + "API": true, + "GUID": true, + "ID": true, + "IP": true, + "UUID": true, + "URI": true, + "URL": true, + }, + } +} diff --git a/globals/get_user_data.go b/globals/get_user_data.go index 0f2ce27..3f5160c 100644 --- a/globals/get_user_data.go +++ b/globals/get_user_data.go @@ -3,7 +3,7 @@ package globals import ( "context" - pb "github.com/PretendoNetwork/grpc-go/account" + pb "github.com/PretendoNetwork/grpc/go/account/v2" "google.golang.org/grpc/metadata" ) diff --git a/globals/globals.go b/globals/globals.go index e1c168d..c32b000 100644 --- a/globals/globals.go +++ b/globals/globals.go @@ -2,7 +2,7 @@ package globals import ( "github.com/PretendoNetwork/friends/types" - pb "github.com/PretendoNetwork/grpc-go/account" + pb "github.com/PretendoNetwork/grpc/go/account/v2" "github.com/PretendoNetwork/nex-go/v2" "github.com/PretendoNetwork/plogger-go" "google.golang.org/grpc" @@ -21,6 +21,5 @@ var SecureEndpoint *nex.PRUDPEndPoint var ConnectedUsers *nex.MutexMap[uint32, *types.ConnectedUser] var AESKey []byte var GRPCAccountClientConnection *grpc.ClientConn -var GRPCAccountClient pb.AccountClient +var GRPCAccountClient pb.AccountServiceClient var GRPCAccountCommonMetadata metadata.MD -var DatabaseMaxConnections int diff --git a/go.mod b/go.mod index d3b761c..7c3d759 100644 --- a/go.mod +++ b/go.mod @@ -1,39 +1,47 @@ module github.com/PretendoNetwork/friends -go 1.23.0 +go 1.24.0 -toolchain go1.23.6 +toolchain go1.24.3 require ( - github.com/PretendoNetwork/grpc-go v1.0.2 - github.com/PretendoNetwork/nex-go/v2 v2.1.3 + github.com/PretendoNetwork/grpc/go v0.0.0-20260114221322-0631a1e0c840 + github.com/PretendoNetwork/nex-go/v2 v2.3.0 github.com/PretendoNetwork/nex-protocols-common-go/v2 v2.4.0 - github.com/PretendoNetwork/nex-protocols-go/v2 v2.2.1 + github.com/PretendoNetwork/nex-protocols-go/v2 v2.2.2 github.com/PretendoNetwork/plogger-go v1.1.0 github.com/PretendoNetwork/sql-manager v1.0.0 github.com/golang/protobuf v1.5.4 github.com/joho/godotenv v1.5.1 github.com/lib/pq v1.10.9 - google.golang.org/grpc v1.71.1 + google.golang.org/grpc v1.78.0 ) require ( + github.com/beorn7/perks v1.0.1 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/dolthub/maphash v0.1.0 // indirect github.com/fatih/color v1.18.0 // indirect github.com/jwalton/go-supportscolor v1.2.0 // indirect - github.com/klauspost/compress v1.18.0 // indirect - github.com/lxzan/gws v1.8.8 // indirect + github.com/klauspost/compress v1.18.3 // indirect + github.com/lxzan/gws v1.8.9 // indirect github.com/mattn/go-colorable v0.1.14 // indirect github.com/mattn/go-isatty v0.0.20 // indirect + github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect + github.com/prometheus/client_golang v1.23.2 // indirect + github.com/prometheus/client_model v0.6.2 // indirect + github.com/prometheus/common v0.67.5 // indirect + github.com/prometheus/procfs v0.19.2 // indirect github.com/rasky/go-lzo v0.0.0-20200203143853-96a758eda86e // indirect github.com/superwhiskers/crunch/v3 v3.5.7 // indirect - golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0 // indirect - golang.org/x/mod v0.24.0 // indirect - golang.org/x/net v0.39.0 // indirect - golang.org/x/sync v0.13.0 // indirect - golang.org/x/sys v0.32.0 // indirect - golang.org/x/term v0.31.0 // indirect - golang.org/x/text v0.24.0 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20250414145226-207652e42e2e // indirect - google.golang.org/protobuf v1.36.6 // indirect + go.yaml.in/yaml/v2 v2.4.3 // indirect + golang.org/x/exp v0.0.0-20260112195511-716be5621a96 // indirect + golang.org/x/mod v0.32.0 // indirect + golang.org/x/net v0.49.0 // indirect + golang.org/x/sync v0.19.0 // indirect + golang.org/x/sys v0.40.0 // indirect + golang.org/x/term v0.39.0 // indirect + golang.org/x/text v0.33.0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20260114163908-3f89685c29c3 // indirect + google.golang.org/protobuf v1.36.11 // indirect ) diff --git a/go.sum b/go.sum index bfc8b28..7c791f0 100644 --- a/go.sum +++ b/go.sum @@ -1,88 +1,119 @@ -github.com/PretendoNetwork/grpc-go v1.0.2 h1:9TvKmX7dCOANyoHEra1MMYqS1N/RGav66TRG4SHInvo= -github.com/PretendoNetwork/grpc-go v1.0.2/go.mod h1:XZjEsij9lL7HJBNkH6JPbBIkUSq/1rjflvjGdv+DAj0= -github.com/PretendoNetwork/nex-go/v2 v2.1.3 h1:hdi8PbJIWbpr3WOc1fGJ5ssF76kTxFJ3Wnz46WJYvVs= -github.com/PretendoNetwork/nex-go/v2 v2.1.3/go.mod h1:3LyJzsv3AataJW8D0binp15Q8ZH22MWTYly1VNtXi64= +github.com/PretendoNetwork/grpc/go v0.0.0-20260114221322-0631a1e0c840 h1:IkflRrU2XT/8voysYxZTxcQYPyMBCt7yBHWy8U6Q/tU= +github.com/PretendoNetwork/grpc/go v0.0.0-20260114221322-0631a1e0c840/go.mod h1:L6We4KkcQeiQVPrF7iu8Zax0B1Bm0v4nssR1JOAiRFQ= +github.com/PretendoNetwork/nex-go/v2 v2.3.0 h1:CQNm/DzYhXvyzD/5l+Dxfp0/AbObuCfyfhLAeY6BejI= +github.com/PretendoNetwork/nex-go/v2 v2.3.0/go.mod h1:2xKxiTtNxGliQ80xeicc6w3D53hmunOndoB3XJxUn/8= github.com/PretendoNetwork/nex-protocols-common-go/v2 v2.4.0 h1:EhXj1EDbNgdg40BPx/7n1HHsAy/DayGIWthu81UNyvI= github.com/PretendoNetwork/nex-protocols-common-go/v2 v2.4.0/go.mod h1:tNtZly5sL3wfy4LVgybS2efm00L/wNgyvcrBV59S/YM= -github.com/PretendoNetwork/nex-protocols-go/v2 v2.2.1 h1:/dsuP0W7bZNvrXoXH0ZRdxpxonfbWmmson51WCQdpEQ= -github.com/PretendoNetwork/nex-protocols-go/v2 v2.2.1/go.mod h1:+soBHmwX6ixGxj6cphLuCvfJqxcZPuowc/5e7Qi9Bz0= +github.com/PretendoNetwork/nex-protocols-go/v2 v2.2.2 h1:RNXr0jTZZYZOZTch1e18cbx6BFfIT2IoFR9bnLd/6Ig= +github.com/PretendoNetwork/nex-protocols-go/v2 v2.2.2/go.mod h1:+soBHmwX6ixGxj6cphLuCvfJqxcZPuowc/5e7Qi9Bz0= github.com/PretendoNetwork/plogger-go v1.1.0 h1:x2XgyeeM8zDFGy+NcIZd3SYC2fNrVWpBBbkqTejOfiM= github.com/PretendoNetwork/plogger-go v1.1.0/go.mod h1:wpltahp91IXr9nOvWgwep8zGtUKDeCVwm+/Wa484lQ4= github.com/PretendoNetwork/sql-manager v1.0.0 h1:g0SYpQgi6Kk4ptufrLTSmDxvqaYioTcfXaDH+uXC+a0= github.com/PretendoNetwork/sql-manager v1.0.0/go.mod h1:NaEdDC0S/9J8eoxCDvuHB8fofv0svh44lWvgCdtuMq0= +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/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= 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/dolthub/maphash v0.1.0 h1:bsQ7JsF4FkkWyrP3oCnFJgrCUAFbFf3kOl4L/QxPDyQ= github.com/dolthub/maphash v0.1.0/go.mod h1:gkg4Ch4CdCDu5h6PMriVLawB7koZ+5ijb9puGMV50a4= github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= -github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= -github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= +github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= -github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= -github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= 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/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= github.com/jwalton/go-supportscolor v1.2.0 h1:g6Ha4u7Vm3LIsQ5wmeBpS4gazu0UP1DRDE8y6bre4H8= github.com/jwalton/go-supportscolor v1.2.0/go.mod h1:hFVUAZV2cWg+WFFC4v8pT2X/S2qUUBYMioBD9AINXGs= -github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= -github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= +github.com/klauspost/compress v1.18.3 h1:9PJRvfbmTabkOX8moIpXPbMMbYN60bWImDDU7L+/6zw= +github.com/klauspost/compress v1.18.3/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= +github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= -github.com/lxzan/gws v1.8.8 h1:st193ZG8qN8sSw8/g/UituFhs7etmKzS7jUqhijg5wM= -github.com/lxzan/gws v1.8.8/go.mod h1:FcGeRMB7HwGuTvMLR24ku0Zx0p6RXqeKASeMc4VYgi4= +github.com/lxzan/gws v1.8.9 h1:VU3SGUeWlQrEwfUSfokcZep8mdg/BrUF+y73YYshdBM= +github.com/lxzan/gws v1.8.9/go.mod h1:d9yHaR1eDTBHagQC6KY7ycUOaz5KWeqQtP3xu7aMK8Y= github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= 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/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= 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/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o= +github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg= +github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk= +github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE= +github.com/prometheus/common v0.67.5 h1:pIgK94WWlQt1WLwAC5j2ynLaBRDiinoAb86HZHTUGI4= +github.com/prometheus/common v0.67.5/go.mod h1:SjE/0MzDEEAyrdr5Gqc6G+sXI67maCxzaT3A2+HqjUw= +github.com/prometheus/procfs v0.19.2 h1:zUMhqEW66Ex7OXIiDkll3tl9a1ZdilUOd/F6ZXw4Vws= +github.com/prometheus/procfs v0.19.2/go.mod h1:M0aotyiemPhBCM0z5w87kL22CxfcH05ZpYlu+b4J7mw= github.com/rasky/go-lzo v0.0.0-20200203143853-96a758eda86e h1:dCWirM5F3wMY+cmRda/B1BiPsFtmzXqV9b0hLWtVBMs= github.com/rasky/go-lzo v0.0.0-20200203143853-96a758eda86e/go.mod h1:9leZcVcItj6m9/CfHY5Em/iBrCz7js8LcRQGTKEEv2M= -github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= -github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= +github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= github.com/superwhiskers/crunch/v3 v3.5.7 h1:N9RLxaR65C36i26BUIpzPXGy2f6pQ7wisu2bawbKNqg= github.com/superwhiskers/crunch/v3 v3.5.7/go.mod h1:4ub2EKgF1MAhTjoOCTU4b9uLMsAweHEa89aRrfAypXA= -go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= -go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= -go.opentelemetry.io/otel v1.34.0 h1:zRLXxLCgL1WyKsPVrgbSdMN4c0FMkDAskSTQP+0hdUY= -go.opentelemetry.io/otel v1.34.0/go.mod h1:OWFPOQ+h4G8xpyjgqo4SxJYdDQ/qmRH+wivy7zzx9oI= -go.opentelemetry.io/otel/metric v1.34.0 h1:+eTR3U0MyfWjRDhmFMxe2SsW64QrZ84AOhvqS7Y+PoQ= -go.opentelemetry.io/otel/metric v1.34.0/go.mod h1:CEDrp0fy2D0MvkXE+dPV7cMi8tWZwX3dmaIhwPOaqHE= -go.opentelemetry.io/otel/sdk v1.34.0 h1:95zS4k/2GOy069d321O8jWgYsW3MzVV+KuSPKp7Wr1A= -go.opentelemetry.io/otel/sdk v1.34.0/go.mod h1:0e/pNiaMAqaykJGKbi+tSjWfNNHMTxoC9qANsCzbyxU= -go.opentelemetry.io/otel/sdk/metric v1.34.0 h1:5CeK9ujjbFVL5c1PhLuStg1wxA7vQv7ce1EK0Gyvahk= -go.opentelemetry.io/otel/sdk/metric v1.34.0/go.mod h1:jQ/r8Ze28zRKoNRdkjCZxfs6YvBTG1+YIqyFVFYec5w= -go.opentelemetry.io/otel/trace v1.34.0 h1:+ouXS2V8Rd4hp4580a8q23bg0azF2nI8cqLYnC8mh/k= -go.opentelemetry.io/otel/trace v1.34.0/go.mod h1:Svm7lSjQD7kG7KJ/MUHPVXSDGz2OX4h0M2jHBhmSfRE= -golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0 h1:R84qjqJb5nVJMxqWYb3np9L5ZsaDtB+a39EqjV0JSUM= -golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0/go.mod h1:S9Xr4PYopiDyqSyp5NjCrhFrqg6A5zA2E/iPHPhqnS8= -golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU= -golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww= -golang.org/x/net v0.39.0 h1:ZCu7HMWDxpXpaiKdhzIfaltL9Lp31x/3fCP11bc6/fY= -golang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E= -golang.org/x/sync v0.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610= -golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= +go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= +go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8= +go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM= +go.opentelemetry.io/otel/metric v1.38.0 h1:Kl6lzIYGAh5M159u9NgiRkmoMKjvbsKtYRwgfrA6WpA= +go.opentelemetry.io/otel/metric v1.38.0/go.mod h1:kB5n/QoRM8YwmUahxvI3bO34eVtQf2i4utNVLr9gEmI= +go.opentelemetry.io/otel/sdk v1.38.0 h1:l48sr5YbNf2hpCUj/FoGhW9yDkl+Ma+LrVl8qaM5b+E= +go.opentelemetry.io/otel/sdk v1.38.0/go.mod h1:ghmNdGlVemJI3+ZB5iDEuk4bWA3GkTpW+DOoZMYBVVg= +go.opentelemetry.io/otel/sdk/metric v1.38.0 h1:aSH66iL0aZqo//xXzQLYozmWrXxyFkBJ6qT5wthqPoM= +go.opentelemetry.io/otel/sdk/metric v1.38.0/go.mod h1:dg9PBnW9XdQ1Hd6ZnRz689CbtrUp0wMMs9iPcgT9EZA= +go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE= +go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= +go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0= +go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8= +golang.org/x/exp v0.0.0-20260112195511-716be5621a96 h1:Z/6YuSHTLOHfNFdb8zVZomZr7cqNgTJvA8+Qz75D8gU= +golang.org/x/exp v0.0.0-20260112195511-716be5621a96/go.mod h1:nzimsREAkjBCIEFtHiYkrJyT+2uy9YZJB7H1k68CXZU= +golang.org/x/mod v0.32.0 h1:9F4d3PHLljb6x//jOyokMv3eX+YDeepZSEo3mFJy93c= +golang.org/x/mod v0.32.0/go.mod h1:SgipZ/3h2Ci89DlEtEXWUk/HteuRin+HHhN+WbNhguU= +golang.org/x/net v0.49.0 h1:eeHFmOGUTtaaPSGNmjBKpbng9MulQsJURQUAfUwY++o= +golang.org/x/net v0.49.0/go.mod h1:/ysNB2EvaqvesRkuLAyjI1ycPZlQHM3q01F02UY/MV8= +golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4= +golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20= -golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ= +golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.31.0 h1:erwDkOK1Msy6offm1mOgvspSkslFnIGsFnxOKoufg3o= -golang.org/x/term v0.31.0/go.mod h1:R4BeIy7D95HzImkxGkTW1UQTtP54tio2RyHz7PwK0aw= -golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0= -golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250414145226-207652e42e2e h1:ztQaXfzEXTmCBvbtWYRhJxW+0iJcz2qXfd38/e9l7bA= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250414145226-207652e42e2e/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A= -google.golang.org/grpc v1.71.1 h1:ffsFWr7ygTUscGPI0KKK6TLrGz0476KUvvsbqWK0rPI= -google.golang.org/grpc v1.71.1/go.mod h1:H0GRtasmQOh9LkFoCPDu3ZrwUtD1YGE+b2vYBYd/8Ec= -google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY= -google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= +golang.org/x/term v0.39.0 h1:RclSuaJf32jOqZz74CkPA9qFuVTX7vhLlpfj/IGWlqY= +golang.org/x/term v0.39.0/go.mod h1:yxzUCTP/U+FzoxfdKmLaA0RV1WgE0VY7hXBwKtY/4ww= +golang.org/x/text v0.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE= +golang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8= +gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= +gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260114163908-3f89685c29c3 h1:C4WAdL+FbjnGlpp2S+HMVhBeCq2Lcib4xZqfPNF6OoQ= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260114163908-3f89685c29c3/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ= +google.golang.org/grpc v1.78.0 h1:K1XZG/yGDJnzMdd/uZHAkVqJE+xIDOcmdSFZkBUicNc= +google.golang.org/grpc v1.78.0/go.mod h1:I47qjTo4OKbMkjA/aOOwxDIiPSBofUtQUI5EfpWvW7U= +google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= +google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/grpc/accept_friend_request.go b/grpc/accept_friend_request.go index e6db7b4..2536a65 100644 --- a/grpc/accept_friend_request.go +++ b/grpc/accept_friend_request.go @@ -9,7 +9,7 @@ import ( "github.com/PretendoNetwork/friends/database" database_wiiu "github.com/PretendoNetwork/friends/database/wiiu" "github.com/PretendoNetwork/friends/globals" - pb "github.com/PretendoNetwork/grpc-go/friends" + pb "github.com/PretendoNetwork/grpc/go/friends" ) func (s *gRPCFriendsServer) AcceptFriendRequest(ctx context.Context, in *pb.AcceptFriendRequestRequest) (*pb.AcceptFriendRequestResponse, error) { diff --git a/grpc/api_key_interceptor.go b/grpc/api_key_interceptor.go index 51e5886..d6608e4 100644 --- a/grpc/api_key_interceptor.go +++ b/grpc/api_key_interceptor.go @@ -3,8 +3,8 @@ package grpc import ( "context" "errors" - "os" + "github.com/PretendoNetwork/friends/globals" "google.golang.org/grpc" "google.golang.org/grpc/metadata" ) @@ -15,7 +15,7 @@ func apiKeyInterceptor(ctx context.Context, req interface{}, info *grpc.UnarySer if ok { apiKeyHeader := md.Get("X-API-Key") - if len(apiKeyHeader) == 0 || apiKeyHeader[0] != os.Getenv("PN_FRIENDS_CONFIG_GRPC_API_KEY") { + if len(apiKeyHeader) == 0 || apiKeyHeader[0] != globals.Config.GRPCAPIKey { return nil, errors.New("Missing or invalid API key") } } diff --git a/grpc/deny_friend_request.go b/grpc/deny_friend_request.go index 9879c1e..6bfc702 100644 --- a/grpc/deny_friend_request.go +++ b/grpc/deny_friend_request.go @@ -9,7 +9,7 @@ import ( "github.com/PretendoNetwork/friends/database" database_wiiu "github.com/PretendoNetwork/friends/database/wiiu" "github.com/PretendoNetwork/friends/globals" - pb "github.com/PretendoNetwork/grpc-go/friends" + pb "github.com/PretendoNetwork/grpc/go/friends" ) func (s *gRPCFriendsServer) DenyFriendRequest(ctx context.Context, in *pb.DenyFriendRequestRequest) (*pb.DenyFriendRequestResponse, error) { diff --git a/grpc/get_user_friend_pids.go b/grpc/get_user_friend_pids.go index 555a0aa..b6e44a9 100644 --- a/grpc/get_user_friend_pids.go +++ b/grpc/get_user_friend_pids.go @@ -10,7 +10,7 @@ import ( database_3ds "github.com/PretendoNetwork/friends/database/3ds" database_wiiu "github.com/PretendoNetwork/friends/database/wiiu" "github.com/PretendoNetwork/friends/globals" - pb "github.com/PretendoNetwork/grpc-go/friends" + pb "github.com/PretendoNetwork/grpc/go/friends" ) func (s *gRPCFriendsServer) GetUserFriendPIDs(ctx context.Context, in *pb.GetUserFriendPIDsRequest) (*pb.GetUserFriendPIDsResponse, error) { diff --git a/grpc/get_user_friend_requests_incoming.go b/grpc/get_user_friend_requests_incoming.go index fb6fab8..4501dd5 100644 --- a/grpc/get_user_friend_requests_incoming.go +++ b/grpc/get_user_friend_requests_incoming.go @@ -9,7 +9,7 @@ import ( "github.com/PretendoNetwork/friends/database" database_wiiu "github.com/PretendoNetwork/friends/database/wiiu" "github.com/PretendoNetwork/friends/globals" - pb "github.com/PretendoNetwork/grpc-go/friends" + pb "github.com/PretendoNetwork/grpc/go/friends" ) func (s *gRPCFriendsServer) GetUserFriendRequestsIncoming(ctx context.Context, in *pb.GetUserFriendRequestsIncomingRequest) (*pb.GetUserFriendRequestsIncomingResponse, error) { diff --git a/grpc/grpc_server.go b/grpc/grpc_server.go index 63e9c20..6a97e77 100644 --- a/grpc/grpc_server.go +++ b/grpc/grpc_server.go @@ -4,9 +4,9 @@ import ( "fmt" "log" "net" - "os" - pb "github.com/PretendoNetwork/grpc-go/friends" + "github.com/PretendoNetwork/friends/globals" + pb "github.com/PretendoNetwork/grpc/go/friends" "google.golang.org/grpc" ) @@ -15,7 +15,7 @@ type gRPCFriendsServer struct { } func StartGRPCServer() { - listener, err := net.Listen("tcp", fmt.Sprintf(":%s", os.Getenv("PN_FRIENDS_GRPC_SERVER_PORT"))) + listener, err := net.Listen("tcp", fmt.Sprintf(":%d", globals.Config.GRPCServerPort)) if err != nil { log.Fatalf("failed to listen: %v", err) } diff --git a/grpc/send_user_friend_request.go b/grpc/send_user_friend_request.go index 0f3fc72..d87fe09 100644 --- a/grpc/send_user_friend_request.go +++ b/grpc/send_user_friend_request.go @@ -9,7 +9,7 @@ import ( database_wiiu "github.com/PretendoNetwork/friends/database/wiiu" "github.com/PretendoNetwork/friends/globals" - pb "github.com/PretendoNetwork/grpc-go/friends" + pb "github.com/PretendoNetwork/grpc/go/friends" "github.com/PretendoNetwork/nex-go/v2/types" ) diff --git a/grpc/send_user_notification_wiiu.go b/grpc/send_user_notification_wiiu.go index 2656bb5..9aca868 100644 --- a/grpc/send_user_notification_wiiu.go +++ b/grpc/send_user_notification_wiiu.go @@ -4,7 +4,7 @@ import ( "context" "github.com/PretendoNetwork/friends/globals" - pb "github.com/PretendoNetwork/grpc-go/friends" + pb "github.com/PretendoNetwork/grpc/go/friends" nex "github.com/PretendoNetwork/nex-go/v2" "github.com/PretendoNetwork/nex-go/v2/constants" nintendo_notifications "github.com/PretendoNetwork/nex-protocols-go/v2/nintendo-notifications" diff --git a/init.go b/init.go index 674f4d6..d8b7497 100644 --- a/init.go +++ b/init.go @@ -1,18 +1,16 @@ package main import ( - "cmp" "crypto/rand" "encoding/hex" "fmt" "os" - "strconv" "strings" "github.com/PretendoNetwork/friends/database" "github.com/PretendoNetwork/friends/globals" "github.com/PretendoNetwork/friends/types" - pb "github.com/PretendoNetwork/grpc-go/account" + pb "github.com/PretendoNetwork/grpc/go/account/v2" "github.com/PretendoNetwork/nex-go/v2" nex_types "github.com/PretendoNetwork/nex-go/v2/types" "github.com/PretendoNetwork/plogger-go" @@ -27,7 +25,6 @@ func init() { globals.Logger = plogger.NewLogger() globals.ConnectedUsers = nex.NewMutexMap[uint32, *types.ConnectedUser]() - // * Setup RSA private key for token parsing var err error err = godotenv.Load() @@ -35,31 +32,7 @@ func init() { globals.Logger.Warningf("Error loading .env file: %s", err.Error()) } - postgresURI := os.Getenv("PN_FRIENDS_CONFIG_DATABASE_URI") - databaseMaxConnectionsStr := cmp.Or(os.Getenv("PN_FRIENDS_CONFIG_DATABASE_MAX_CONNECTIONS"), "100") - aesKey := os.Getenv("PN_FRIENDS_CONFIG_AES_KEY") - grpcAPIKey := os.Getenv("PN_FRIENDS_CONFIG_GRPC_API_KEY") - grpcServerPort := os.Getenv("PN_FRIENDS_GRPC_SERVER_PORT") - authenticationServerPort := os.Getenv("PN_FRIENDS_AUTHENTICATION_SERVER_PORT") - secureServerHost := os.Getenv("PN_FRIENDS_SECURE_SERVER_HOST") - secureServerPort := os.Getenv("PN_FRIENDS_SECURE_SERVER_PORT") - accountGRPCHost := os.Getenv("PN_FRIENDS_ACCOUNT_GRPC_HOST") - accountGRPCPort := os.Getenv("PN_FRIENDS_ACCOUNT_GRPC_PORT") - accountGRPCAPIKey := os.Getenv("PN_FRIENDS_ACCOUNT_GRPC_API_KEY") - - if strings.TrimSpace(postgresURI) == "" { - globals.Logger.Error("PN_FRIENDS_CONFIG_DATABASE_URI environment variable not set") - os.Exit(0) - } - - databaseMaxConnections, err := strconv.Atoi(databaseMaxConnectionsStr) - - if err != nil { - globals.Logger.Errorf("PN_FRIENDS_CONFIG_DATABASE_MAX_CONNECTIONS is not a valid number. Got %s", databaseMaxConnectionsStr) - os.Exit(0) - } else { - globals.DatabaseMaxConnections = databaseMaxConnections - } + globals.Config = globals.NewConfigParser(globals.Config).SetPrefix("PN_FRIENDS_CONFIG").ParseFromEnv() kerberosPassword := make([]byte, 0x10) _, err = rand.Read(kerberosPassword) @@ -70,100 +43,32 @@ func init() { globals.KerberosPassword = string(kerberosPassword) - globals.AuthenticationServerAccount = nex.NewAccount(nex_types.NewPID(1), "Quazal Authentication", globals.KerberosPassword) - globals.SecureServerAccount = nex.NewAccount(nex_types.NewPID(2), "Quazal Rendez-Vous", globals.KerberosPassword) - globals.GuestAccount = nex.NewAccount(nex_types.NewPID(100), "guest", "MMQea3n!fsik") // * Guest account password is always the same, known to all consoles. Only allow on the friends server - - if strings.TrimSpace(aesKey) == "" { - globals.Logger.Error("PN_FRIENDS_CONFIG_AES_KEY environment variable not set") + globals.AuthenticationServerAccount = nex.NewAccount(nex_types.NewPID(1), "Quazal Authentication", globals.KerberosPassword, false) + globals.SecureServerAccount = nex.NewAccount(nex_types.NewPID(2), "Quazal Rendez-Vous", globals.KerberosPassword, false) + globals.GuestAccount = nex.NewAccount(nex_types.NewPID(100), "guest", "MMQea3n!fsik", false) + globals.AESKey, err = hex.DecodeString(globals.Config.AESKey) + if err != nil { + globals.Logger.Criticalf("Failed to decode AES key: %v", err) os.Exit(0) - } else { - globals.AESKey, err = hex.DecodeString(os.Getenv("PN_FRIENDS_CONFIG_AES_KEY")) - if err != nil { - globals.Logger.Criticalf("Failed to decode AES key: %v", err) - os.Exit(0) - } } - if strings.TrimSpace(grpcAPIKey) == "" { + if strings.TrimSpace(globals.Config.GRPCAPIKey) == "" { globals.Logger.Warning("Insecure gRPC server detected. PN_FRIENDS_CONFIG_GRPC_API_KEY environment variable not set") } - if strings.TrimSpace(grpcServerPort) == "" { - globals.Logger.Error("PN_FRIENDS_GRPC_SERVER_PORT environment variable not set") - os.Exit(0) + if strings.TrimSpace(globals.Config.AccountGRPCAPIKey) == "" { + globals.Logger.Warning("Insecure gRPC server detected. PN_FRIENDS_CONFIG_ACCOUNT_GRPC_API_KEY environment variable not set") } - if port, err := strconv.Atoi(grpcServerPort); err != nil { - globals.Logger.Errorf("PN_FRIENDS_GRPC_SERVER_PORT is not a valid port. Expected 0-65535, got %s", grpcServerPort) - os.Exit(0) - } else if port < 0 || port > 65535 { - globals.Logger.Errorf("PN_FRIENDS_GRPC_SERVER_PORT is not a valid port. Expected 0-65535, got %s", grpcServerPort) - os.Exit(0) - } - - if strings.TrimSpace(authenticationServerPort) == "" { - globals.Logger.Error("PN_FRIENDS_AUTHENTICATION_SERVER_PORT environment variable not set") - os.Exit(0) - } - - if port, err := strconv.Atoi(authenticationServerPort); err != nil { - globals.Logger.Errorf("PN_FRIENDS_AUTHENTICATION_SERVER_PORT is not a valid port. Expected 0-65535, got %s", authenticationServerPort) - os.Exit(0) - } else if port < 0 || port > 65535 { - globals.Logger.Errorf("PN_FRIENDS_AUTHENTICATION_SERVER_PORT is not a valid port. Expected 0-65535, got %s", authenticationServerPort) - os.Exit(0) - } - - if strings.TrimSpace(secureServerHost) == "" { - globals.Logger.Error("PN_FRIENDS_SECURE_SERVER_HOST environment variable not set") - os.Exit(0) - } - - if strings.TrimSpace(secureServerPort) == "" { - globals.Logger.Error("PN_FRIENDS_SECURE_SERVER_PORT environment variable not set") - os.Exit(0) - } - - if port, err := strconv.Atoi(secureServerPort); err != nil { - globals.Logger.Errorf("PN_FRIENDS_SECURE_SERVER_PORT is not a valid port. Expected 0-65535, got %s", secureServerPort) - os.Exit(0) - } else if port < 0 || port > 65535 { - globals.Logger.Errorf("PN_FRIENDS_SECURE_SERVER_PORT is not a valid port. Expected 0-65535, got %s", secureServerPort) - os.Exit(0) - } - - if strings.TrimSpace(accountGRPCHost) == "" { - globals.Logger.Error("PN_FRIENDS_ACCOUNT_GRPC_HOST environment variable not set") - os.Exit(0) - } - - if strings.TrimSpace(accountGRPCPort) == "" { - globals.Logger.Error("PN_FRIENDS_ACCOUNT_GRPC_PORT environment variable not set") - os.Exit(0) - } - - if port, err := strconv.Atoi(accountGRPCPort); err != nil { - globals.Logger.Errorf("PN_FRIENDS_ACCOUNT_GRPC_PORT is not a valid port. Expected 0-65535, got %s", accountGRPCPort) - os.Exit(0) - } else if port < 0 || port > 65535 { - globals.Logger.Errorf("PN_FRIENDS_ACCOUNT_GRPC_PORT is not a valid port. Expected 0-65535, got %s", accountGRPCPort) - os.Exit(0) - } - - if strings.TrimSpace(accountGRPCAPIKey) == "" { - globals.Logger.Warning("Insecure gRPC server detected. PN_FRIENDS_ACCOUNT_GRPC_API_KEY environment variable not set") - } - - globals.GRPCAccountClientConnection, err = grpc.Dial(fmt.Sprintf("%s:%s", accountGRPCHost, accountGRPCPort), grpc.WithTransportCredentials(insecure.NewCredentials())) + globals.GRPCAccountClientConnection, err = grpc.NewClient(fmt.Sprintf("%s:%d", globals.Config.AccountGRPCHost, globals.Config.AccountGRPCPort), grpc.WithTransportCredentials(insecure.NewCredentials())) if err != nil { globals.Logger.Criticalf("Failed to connect to account gRPC server: %v", err) os.Exit(0) } - globals.GRPCAccountClient = pb.NewAccountClient(globals.GRPCAccountClientConnection) + globals.GRPCAccountClient = pb.NewAccountServiceClient(globals.GRPCAccountClientConnection) globals.GRPCAccountCommonMetadata = metadata.Pairs( - "X-API-Key", accountGRPCAPIKey, + "X-API-Key", globals.Config.AccountGRPCAPIKey, ) database.ConnectPostgres() diff --git a/nex/authentication.go b/nex/authentication.go index 0430b86..3876317 100644 --- a/nex/authentication.go +++ b/nex/authentication.go @@ -1,9 +1,6 @@ package nex import ( - "os" - "strconv" - "github.com/PretendoNetwork/friends/globals" "github.com/PretendoNetwork/nex-go/v2" ) @@ -11,7 +8,10 @@ import ( var serverBuildString string func StartAuthenticationServer() { - port, _ := strconv.Atoi(os.Getenv("PN_FRIENDS_AUTHENTICATION_SERVER_PORT")) + + if globals.Config.HealthCheckPort != 0 { + go nex.EnableBasicUDPHealthCheck(int(globals.Config.HealthCheckPort)) + } globals.AuthenticationServer = nex.NewPRUDPServer() globals.AuthenticationEndpoint = nex.NewPRUDPEndPoint(1) @@ -27,5 +27,5 @@ func StartAuthenticationServer() { globals.AuthenticationServer.SessionKeyLength = 16 globals.AuthenticationServer.AccessKey = "ridfebb9" globals.AuthenticationServer.BindPRUDPEndPoint(globals.AuthenticationEndpoint) - globals.AuthenticationServer.Listen(port) + globals.AuthenticationServer.Listen(int(globals.Config.AuthenticationServerPort)) } diff --git a/nex/friends-wiiu/update_and_get_all_information.go b/nex/friends-wiiu/update_and_get_all_information.go index d23c6bb..de1e2f7 100644 --- a/nex/friends-wiiu/update_and_get_all_information.go +++ b/nex/friends-wiiu/update_and_get_all_information.go @@ -1,8 +1,6 @@ package nex_friends_wiiu import ( - "os" - "github.com/PretendoNetwork/friends/database" database_wiiu "github.com/PretendoNetwork/friends/database/wiiu" "github.com/PretendoNetwork/friends/globals" @@ -93,7 +91,7 @@ func UpdateAndGetAllInformation(err error, packet nex.PacketInterface, callID ui notifications_wiiu.SendPresenceUpdate(presence) - if os.Getenv("PN_FRIENDS_CONFIG_ENABLE_BELLA") == "true" { + if globals.Config.EnableBella { bella := friends_wiiu_types.NewFriendInfo() bella.NNAInfo = friends_wiiu_types.NewNNAInfo() diff --git a/nex/register_common_authentication_server_protocols.go b/nex/register_common_authentication_server_protocols.go index 4df0551..f9148e6 100644 --- a/nex/register_common_authentication_server_protocols.go +++ b/nex/register_common_authentication_server_protocols.go @@ -1,9 +1,6 @@ package nex import ( - "os" - "strconv" - "github.com/PretendoNetwork/friends/globals" "github.com/PretendoNetwork/nex-go/v2/constants" "github.com/PretendoNetwork/nex-go/v2/types" @@ -15,12 +12,10 @@ func registerCommonAuthenticationServerProtocols() { ticketGrantingProtocol := ticket_granting.NewProtocol() commonTicketGrantingProtocol := common_ticket_granting.NewCommonProtocol(ticketGrantingProtocol) - port, _ := strconv.Atoi(os.Getenv("PN_FRIENDS_SECURE_SERVER_PORT")) - secureStationURL := types.NewStationURL("") secureStationURL.SetURLType(constants.StationURLPRUDPS) - secureStationURL.SetAddress(os.Getenv("PN_FRIENDS_SECURE_SERVER_HOST")) - secureStationURL.SetPortNumber(uint16(port)) + secureStationURL.SetAddress(globals.Config.SecureServerHost) + secureStationURL.SetPortNumber(globals.Config.SecureServerPort) secureStationURL.SetConnectionID(1) secureStationURL.SetPrincipalID(types.NewPID(2)) secureStationURL.SetStreamID(1) diff --git a/nex/secure.go b/nex/secure.go index 3560347..a21715b 100644 --- a/nex/secure.go +++ b/nex/secure.go @@ -1,8 +1,6 @@ package nex import ( - "os" - "strconv" "time" database_3ds "github.com/PretendoNetwork/friends/database/3ds" @@ -17,8 +15,6 @@ import ( ) func StartSecureServer() { - port, _ := strconv.Atoi(os.Getenv("PN_FRIENDS_SECURE_SERVER_PORT")) - globals.SecureServer = nex.NewPRUDPServer() globals.SecureEndpoint = nex.NewPRUDPEndPoint(1) @@ -66,5 +62,5 @@ func StartSecureServer() { globals.SecureServer.SessionKeyLength = 16 globals.SecureServer.AccessKey = "ridfebb9" globals.SecureServer.BindPRUDPEndPoint(globals.SecureEndpoint) - globals.SecureServer.Listen(port) + globals.SecureServer.Listen(int(globals.Config.SecureServerPort)) }